2929 RiskLevel .CRITICAL : 4 ,
3030}
3131
32- _SECRET_RE = re .compile (
33- r"(?i)(api[_-]?key|access[_-]?token|password|passwd|private[_-]?key|secret)"
34- )
32+ _SECRET_RE = re .compile (r"(?i)(api[_-]?key|access[_-]?token|password|passwd|private[_-]?key|secret)" )
3533_URL_RE = re .compile (r"https?://[^\s'\"\\)]+" , re .IGNORECASE )
3634
3735
@@ -100,7 +98,7 @@ def _add(
10098 text : str ,
10199 ) -> None :
102100 evidence = evidence [:160 ]
103- line = text [: text .find (evidence )].count ("\n " ) + 1 if evidence in text else None
101+ line = text [:text .find (evidence )].count ("\n " ) + 1 if evidence in text else None
104102 findings .append (
105103 SafetyFinding (
106104 risk_type = risk_type ,
@@ -109,29 +107,25 @@ def _add(
109107 evidence = evidence ,
110108 recommendation = recommendation ,
111109 line = line ,
112- )
113- )
110+ ))
114111
115- def _scan_paths (
116- self , text : str , working_directory : str | None , findings : list [SafetyFinding ]
117- ) -> None :
112+ def _scan_paths (self , text : str , working_directory : str | None , findings : list [SafetyFinding ]) -> None :
118113 destructive = re .search (
119114 r"(?im)(rm\s+-[^\n]*r[^\n]*\s+(?:/|~)|shutil\.rmtree\s*\(|os\.remove\s*\(|"
120115 r"(?:open|Path)\s*\([^\n]+['\"]w['\"])" ,
121116 text ,
122117 )
123118 if destructive :
124- self ._add (findings , "dangerous_file_operation" , RiskLevel .CRITICAL ,
125- "FILE_DESTRUCTIVE" , destructive .group (0 ),
126- "Restrict writes to an isolated workspace and require explicit approval." , text )
119+ self ._add (findings , "dangerous_file_operation" , RiskLevel .CRITICAL , "FILE_DESTRUCTIVE" ,
120+ destructive .group (0 ), "Restrict writes to an isolated workspace and require explicit approval." ,
121+ text )
127122 haystack = " " .join (filter (None , [text , working_directory ]))
128123 for forbidden in self .policy .forbidden_paths :
129124 expanded = str (Path (forbidden ).expanduser ())
130125 candidates = {forbidden , expanded , forbidden .replace ("~" , "$HOME" )}
131126 matched = next ((candidate for candidate in candidates if candidate and candidate in haystack ), None )
132127 if matched :
133- self ._add (findings , "sensitive_path_access" , RiskLevel .HIGH ,
134- "PATH_FORBIDDEN" , matched ,
128+ self ._add (findings , "sensitive_path_access" , RiskLevel .HIGH , "PATH_FORBIDDEN" , matched ,
135129 "Remove sensitive paths or use a narrowly scoped secret provider." , haystack )
136130
137131 def _scan_network (self , text : str , findings : list [SafetyFinding ]) -> None :
@@ -142,49 +136,38 @@ def _scan_network(self, text: str, findings: list[SafetyFinding]) -> None:
142136 allowed = any (host == domain .lower () or host .endswith ("." + domain .lower ())
143137 for domain in self .policy .allowed_domains )
144138 if not allowed :
145- self ._add (findings , "network_egress" , RiskLevel .HIGH ,
146- "NET_DOMAIN_NOT_ALLOWED" , url ,
139+ self ._add (findings , "network_egress" , RiskLevel .HIGH , "NET_DOMAIN_NOT_ALLOWED" , url ,
147140 "Add a reviewed domain to allowed_domains or remove the request." , text )
148141 if network_api and not urls :
149- self ._add (findings , "network_egress" , RiskLevel .MEDIUM ,
150- "NET_DYNAMIC_DESTINATION" , network_api .group (0 ),
142+ self ._add (findings , "network_egress" , RiskLevel .MEDIUM , "NET_DYNAMIC_DESTINATION" , network_api .group (0 ),
151143 "Resolve the destination and request human review." , text )
152144
153- def _scan_processes (
154- self , text : str , language : str , findings : list [SafetyFinding ]
155- ) -> None :
145+ def _scan_processes (self , text : str , language : str , findings : list [SafetyFinding ]) -> None :
156146 shell_injection = re .search (r"(?m)(;|&&|\|\||`[^`]+`|\$\([^\)]+\))" , text )
157147 if shell_injection :
158- self ._add (findings , "shell_injection" , RiskLevel .HIGH ,
159- "SHELL_CONTROL_OPERATOR" , shell_injection .group (0 ),
160- "Use an argv list and avoid shell interpolation/control operators." , text )
148+ self ._add (findings , "shell_injection" , RiskLevel .HIGH , "SHELL_CONTROL_OPERATOR" , shell_injection .group (0 ),
149+ "Use an argv list and avoid shell interpolation/control operators." , text )
161150 pipeline = re .search (r"(?<!\|)\|(?!\|)" , text ) if language in ("auto" , "bash" , "shell" ) else None
162151 if pipeline :
163- self ._add (findings , "shell_pipeline" , RiskLevel .MEDIUM ,
164- "SHELL_PIPELINE_REVIEW" , pipeline .group (0 ),
152+ self ._add (findings , "shell_pipeline" , RiskLevel .MEDIUM , "SHELL_PIPELINE_REVIEW" , pipeline .group (0 ),
165153 "Review every pipeline stage and avoid passing untrusted data." , text )
166154 process = re .search (r"(?i)\b(subprocess\.|os\.system\s*\(|sudo\b|nohup\b|&\s*$)" , text )
167155 if process :
168- self ._add (findings , "process_execution" , RiskLevel .MEDIUM ,
169- "PROCESS_SPAWN" , process .group (0 ),
156+ self ._add (findings , "process_execution" , RiskLevel .MEDIUM , "PROCESS_SPAWN" , process .group (0 ),
170157 "Use an allowed command with bounded arguments or request review." , text )
171158 command = self ._first_command (text ) if language in ("auto" , "bash" , "shell" ) else None
172159 if command and command not in self .policy .allowed_commands and not process :
173160 if re .match (r"^[A-Za-z0-9_.-]+(?:\s|$)" , text .strip ()):
174- self ._add (findings , "command_policy" , RiskLevel .MEDIUM ,
175- "COMMAND_NOT_ALLOWED" , command ,
161+ self ._add (findings , "command_policy" , RiskLevel .MEDIUM , "COMMAND_NOT_ALLOWED" , command ,
176162 "Add the reviewed command to allowed_commands." , text )
177163
178164 def _scan_dependencies (self , text : str , findings : list [SafetyFinding ]) -> None :
179165 match = re .search (r"(?i)\b(?:pip(?:3)?|npm|yarn|apt(?:-get)?|brew)\s+install\b" , text )
180166 if match :
181- self ._add (findings , "dependency_install" , RiskLevel .HIGH ,
182- "DEPENDENCY_INSTALL" , match .group (0 ),
167+ self ._add (findings , "dependency_install" , RiskLevel .HIGH , "DEPENDENCY_INSTALL" , match .group (0 ),
183168 "Build dependencies into a reviewed immutable environment." , text )
184169
185- def _scan_resources (
186- self , text : str , metadata : dict , findings : list [SafetyFinding ]
187- ) -> None :
170+ def _scan_resources (self , text : str , metadata : dict , findings : list [SafetyFinding ]) -> None :
188171 patterns : Iterable [tuple [str , RiskLevel , str ]] = (
189172 (r"(?m)while\s+True\s*:" , RiskLevel .HIGH , "RESOURCE_INFINITE_LOOP" ),
190173 (r":\(\)\s*\{\s*:\|:&\s*;\s*\}" , RiskLevel .CRITICAL , "RESOURCE_FORK_BOMB" ),
@@ -199,13 +182,10 @@ def _scan_resources(
199182 "Apply timeout, process, memory and concurrency limits in a sandbox." , text )
200183 timeout = metadata .get ("timeout" )
201184 if isinstance (timeout , (int , float )) and timeout > self .policy .max_timeout_seconds :
202- self ._add (findings , "resource_abuse" , RiskLevel .MEDIUM ,
203- "RESOURCE_TIMEOUT_LIMIT" , str (timeout ),
185+ self ._add (findings , "resource_abuse" , RiskLevel .MEDIUM , "RESOURCE_TIMEOUT_LIMIT" , str (timeout ),
204186 f"Limit timeout to { self .policy .max_timeout_seconds } seconds." , text )
205187
206- def _scan_secrets (
207- self , text : str , environment : dict [str , str ], findings : list [SafetyFinding ]
208- ) -> bool :
188+ def _scan_secrets (self , text : str , environment : dict [str , str ], findings : list [SafetyFinding ]) -> bool :
209189 output = re .search (
210190 r"(?is)(print|logging\.[a-z]+|echo|curl|requests\.(?:post|put))[^\n]{0,160}"
211191 r"(api[_-]?key|token|password|private[_-]?key|secret)" ,
@@ -214,8 +194,7 @@ def _scan_secrets(
214194 sensitive_env = [name for name in environment if _SECRET_RE .search (name )]
215195 if output or sensitive_env :
216196 evidence = output .group (0 ) if output else f"environment keys: { ', ' .join (sensitive_env )} "
217- self ._add (findings , "sensitive_data_exposure" , RiskLevel .HIGH ,
218- "SECRET_EXPOSURE" , evidence ,
197+ self ._add (findings , "sensitive_data_exposure" , RiskLevel .HIGH , "SECRET_EXPOSURE" , evidence ,
219198 "Redact secrets and pass opaque references instead of secret values." , text )
220199 return True
221200 return False
0 commit comments