Problem
merge_guard_pre.py's is_dangerous_command detection under-blocks a real destructive op when a python -c program launders a dangerous literal through a variable assignment and then executes it via a non-shell executor:
python3 -c "x='gh pr merge 42'; import os; os.system(x)" → is_dangerous_command = False (UNDER-BLOCK)
The op actually executes (os.system(x) runs gh pr merge 42), but the guard allows it.
Mechanism (verified at baseline bee5da8 AND HEAD — pre-existing, NOT introduced by #1037)
_strip_non_executable_content step 4 (var-assignment strip) is verb-agnostic: it blanks x='gh pr merge 42' → x=STRIPPED for any command, including python -c.
_var_is_expanded only detects shell expansion ($x), not Python's os.system(x).
- Result: the scanned copy loses the literal (
x=STRIPPED) while the real command re-introduces and executes it via the Python variable. The substring scan never sees gh pr merge.
Correctly BLOCKED (for contrast): the direct form python3 -c "...os.system('gh pr merge 42')" and the eval form — only the var-laundered path slips.
Class / scope
Same broad class as #1053 (subprocess.run list-form) — the detection layer (strip + substring scan) does not model python -c program semantics — but a distinct root cause (step-4 strip laundering + shell-only _var_is_expanded, vs #1053's substring-miss on a reconstructed list). Both are within the authored-text threat model boundary the merge-guard accepts (it protects an honest operator from accidental/unapproved destructive ops, not an adversary reconstructing the op in an interpreter) → low–medium priority.
Provenance
Surfaced by the concurrent auditor during PR #1054 (#1037 over-block suppressor), confirmed BASE==HEAD (PR A did not introduce or worsen it). Explicitly NOT gating PR A.
Possible direction (for discussion, route to security-engineer)
Detection-coverage expansion for python -c / interpreter program semantics is a larger change to the detection model — likely coordinated with #1053 across the python-c-programmatic-reconstruction class. Must preserve INV-D2 (over-block acceptable, under-block never) and not regress the #1042/#1031/#1032 binding. Candidate: make the step-4 var-assignment strip verb-aware (do not strip inside an interpreter -c/-e program body), or refuse-to-suppress when the leading verb is an interpreter.
Sibling: #1053.
Problem
merge_guard_pre.py'sis_dangerous_commanddetection under-blocks a real destructive op when apython -cprogram launders a dangerous literal through a variable assignment and then executes it via a non-shell executor:The op actually executes (
os.system(x)runsgh pr merge 42), but the guard allows it.Mechanism (verified at baseline bee5da8 AND HEAD — pre-existing, NOT introduced by #1037)
_strip_non_executable_contentstep 4 (var-assignment strip) is verb-agnostic: it blanksx='gh pr merge 42'→x=STRIPPEDfor any command, includingpython -c._var_is_expandedonly detects shell expansion ($x), not Python'sos.system(x).x=STRIPPED) while the real command re-introduces and executes it via the Python variable. The substring scan never seesgh pr merge.Correctly BLOCKED (for contrast): the direct form
python3 -c "...os.system('gh pr merge 42')"and theevalform — only the var-laundered path slips.Class / scope
Same broad class as #1053 (
subprocess.runlist-form) — the detection layer (strip + substring scan) does not modelpython -cprogram semantics — but a distinct root cause (step-4 strip laundering + shell-only_var_is_expanded, vs #1053's substring-miss on a reconstructed list). Both are within the authored-text threat model boundary the merge-guard accepts (it protects an honest operator from accidental/unapproved destructive ops, not an adversary reconstructing the op in an interpreter) → low–medium priority.Provenance
Surfaced by the concurrent auditor during PR #1054 (#1037 over-block suppressor), confirmed BASE==HEAD (PR A did not introduce or worsen it). Explicitly NOT gating PR A.
Possible direction (for discussion, route to security-engineer)
Detection-coverage expansion for
python -c/ interpreter program semantics is a larger change to the detection model — likely coordinated with #1053 across the python-c-programmatic-reconstruction class. Must preserve INV-D2 (over-block acceptable, under-block never) and not regress the #1042/#1031/#1032 binding. Candidate: make the step-4 var-assignment strip verb-aware (do not strip inside an interpreter-c/-eprogram body), or refuse-to-suppress when the leading verb is an interpreter.Sibling: #1053.