Skip to content

Commit 78757f0

Browse files
committed
fix: re.compile/getattr false positive DENY, unused variable
- _python_scanner: exempt re.compile and regex.compile from dynamic execution detection (pattern compilation is safe) - _python_scanner: exempt getattr(obj, attr, default) with 3 args - _scanner: remove unused has_critical variable (F841)
1 parent e579bc1 commit 78757f0

2 files changed

Lines changed: 20 additions & 8 deletions

File tree

trpc_agent_sdk/tools/safety/_python_scanner.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -476,13 +476,26 @@ def _handle_call(self, node: ast.Call) -> None:
476476

477477
# --- Eval / exec / dynamic execution ---
478478
if canonical in _DYNAMIC_EXEC_CALLS:
479-
self._findings.append(
480-
PythonScanFinding(
481-
kind="eval_exec",
482-
canonical_name=canonical,
483-
line_number=line_no,
484-
evidence=evidence,
485-
))
479+
# Exempt re.compile / regex.compile (pattern compilation, not code exec).
480+
# Also exempt getattr(obj, attr, default) — property access, not exec.
481+
if canonical in ("compile", "getattr"):
482+
receiver = self._resolve_canonical(node.func.value) if isinstance(node.func, ast.Attribute) else ""
483+
if canonical == "compile" and receiver in ("re", "regex"):
484+
pass # re.compile(pattern) is safe — not dynamic exec
485+
elif canonical == "getattr" and len(node.args) >= 3:
486+
pass # getattr(obj, attr, default) — safe property access
487+
else:
488+
self._findings.append(
489+
PythonScanFinding(kind="eval_exec",
490+
canonical_name=canonical,
491+
line_number=line_no,
492+
evidence=evidence))
493+
else:
494+
self._findings.append(
495+
PythonScanFinding(kind="eval_exec",
496+
canonical_name=canonical,
497+
line_number=line_no,
498+
evidence=evidence))
486499

487500
# --- Concurrent / fork ---
488501
if canonical in _CONCURRENCY_CALLS:

trpc_agent_sdk/tools/safety/_scanner.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,6 @@ def scan(self, scan_input: SafetyScanInput) -> SafetyScanReport:
280280
# Only upgrades NEEDS_HUMAN_REVIEW; never overrides DENY (blocklist wins).
281281
# Also refuses to upgrade when any CRITICAL finding exists, regardless
282282
# of how the policy maps CRITICAL → decision.
283-
has_critical = any(f.risk_level == RiskLevel.CRITICAL for f in all_findings)
284283
has_high_or_critical = any(f.risk_level in (RiskLevel.HIGH, RiskLevel.CRITICAL) for f in all_findings)
285284
allow_upgraded = False
286285
# Only upgrade MEDIUM or lower (NEEDS_HUMAN_REVIEW) — never upgrade HIGH/CRITICAL

0 commit comments

Comments
 (0)