@@ -188,7 +188,7 @@ def _check_python(self, scan_input: ScanInput, policy: PolicyConfig) -> list[Saf
188188
189189 if lname in {"open" , "builtins.open" } or lname .endswith (".open" ):
190190 target = _first_str_or_path (node ) or "<dynamic>"
191- if isinstance (node .args [0 ], ast .Name ) if node . args else False :
191+ if node . args and isinstance (node .args [0 ], ast .Name ):
192192 var = node .args [0 ].id
193193 if var in path_vars :
194194 target = path_vars [var ]
@@ -366,8 +366,8 @@ def _collect_sensitive_path_vars(
366366 text = ""
367367 if isinstance (value , ast .Call ):
368368 cname = resolve_name (value .func , aliases ).lower ()
369- if cname in join_names or cname .endswith (".join" ) or cname . endswith ( ".joinpath" ) or cname . endswith (
370- ". expanduser" ):
369+ if ( cname in join_names or cname .endswith (".join" )
370+ or cname . endswith ( ".joinpath" ) or cname . endswith ( ". expanduser") ):
371371 text = path_expr_text (value )
372372 else :
373373 text = path_expr_text (value )
@@ -736,10 +736,15 @@ def _check_python(self, scan_input: ScanInput, policy: PolicyConfig) -> list[Saf
736736
737737 for node , name in iter_python_calls (tree , aliases ):
738738 lname = name .lower ()
739- if lname in _PY_PROCESS_CALLS or any (
739+ is_process_call = (
740+ lname in _PY_PROCESS_CALLS
741+ or lname in {c .lower () for c in _PY_PROCESS_CALLS }
742+ or any (
740743 lname .endswith ("." + c .split ("." )[- 1 ]) and c .split ("." )[0 ] in lname
741- for c in _PY_PROCESS_CALLS ) or lname in {c .lower ()
742- for c in _PY_PROCESS_CALLS }:
744+ for c in _PY_PROCESS_CALLS
745+ )
746+ )
747+ if is_process_call :
743748 shell_true = _has_shell_true (node )
744749 findings .append (
745750 self ._finding (
@@ -892,8 +897,9 @@ def _check_bash(self, scan_input: ScanInput, policy: PolicyConfig) -> list[Safet
892897 level = RiskLevel .CRITICAL ,
893898 message = "Use of eval in bash" ,
894899 ))
895- if _DECODE_EXEC_BASH .search (line ) or re .search (r"\|\s*(sh|bash|zsh)\b" , line ) and re .search (
896- r"base64|xxd|openssl" , line , re .IGNORECASE ):
900+ if (_DECODE_EXEC_BASH .search (line )
901+ or (re .search (r"\|\s*(sh|bash|zsh)\b" , line )
902+ and re .search (r"base64|xxd|openssl" , line , re .IGNORECASE ))):
897903 findings .append (
898904 self ._finding (
899905 line ,
@@ -1266,8 +1272,9 @@ def _const_int(node: ast.AST | None) -> int | None:
12661272 "httpx.post" ,
12671273}
12681274
1269- _ENV_SECRET_KEYS = re .compile (r"(?i)(API[_-]?KEY|TOKEN|SECRET|PASSWORD|PASSWD|PRIVATE[_-]?KEY|ACCESS[_-]?KEY|"
1270- r"OPENAI|ANTHROPIC|AWS_SECRET|GITHUB_TOKEN|GH_TOKEN)" )
1275+ _ENV_SECRET_KEYS = re .compile (
1276+ r"(?i)(API[_-]?KEY|TOKEN|SECRET|PASSWORD|PASSWD|PRIVATE[_-]?KEY|"
1277+ r"ACCESS[_-]?KEY|OPENAI|ANTHROPIC|AWS_SECRET|GITHUB_TOKEN|GH_TOKEN)" )
12711278
12721279
12731280class SecretLeakRule (SafetyRule ):
@@ -1369,8 +1376,11 @@ def _check_python(self, scan_input: ScanInput, policy: PolicyConfig) -> list[Saf
13691376 # Direct print(os.environ["X"])
13701377 for node in ast .walk (tree ):
13711378 if isinstance (node , ast .Subscript ) and _is_environ_subscript (node , aliases ):
1372- key = get_string_literal (node .slice ) if hasattr (node , "slice" ) else None
1373- if key is None or _ENV_SECRET_KEYS .search (str (key ) if key else "SECRET" ):
1379+ # ast.Subscript always has .slice in py>=3.9; fall back to None
1380+ # for older interpreters or unusual AST shapes.
1381+ key = getattr (getattr (node , "slice" , None ), "value" , None )
1382+ key_str = get_string_literal (key ) if key is not None else None
1383+ if key_str is None or _ENV_SECRET_KEYS .search (str (key_str if key_str else "SECRET" )):
13741384 # Flag any environ subscript used as expression; severity HIGH
13751385 findings .append (
13761386 self ._finding (
0 commit comments