Skip to content

merge-guard: is_dangerous_command under-block — step-4 var-assignment strip launders a literal for a non-shell executor (python os.system(x)) #1055

Description

@michael-wojcik

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions