Skip to content

merge_guard: decline-veto false-positives on command-literal tokens (--no-* flags, branch names) #1049

Description

@michael-wojcik

Problem

_has_decline_defer (merge_guard_post.py:186) scans the selected approval option's label + description against the SACROSANCT decline/defer veto _DECLINE_DEFER_RE (\b(?:cancel|abort|…|stop|no|nope|never|skip|…|review|reviewing|hold|wait|pending|once|after|…)\b). Per the merge-authorization checkpoint convention, the affirmative option's description carries the literal command. So a \b-delimited veto-word substring inside the command literal falsely trips the veto → the bundle is vetoed → no token is minted → the operation is HELD.

This is over-block-safe (it only ever blocks an approval; it never under-blocks / never authorizes), so it is not a security hole — but it is a real ergonomics defect with a non-trivial blast radius.

Blast radius (all over-block-safe)

  • The whole --no-* flag family — --no-verify, --no-edit, --no-ff, --no-commit, --no-rebase, … (the -no- segment is a \b-delimited no).
  • --skip-* flags.
  • Ordinary branch names containing a \b-delimited veto word: feature/review-ui, hotfix/no-cache, release/pending-qa, stop-gap, wait-for-ci, etc.

How it surfaced

Found during the #1042 review (the privileged-flag binding) by the test-engineer's coverage matrix: a force-push --no-verify approval can never mint a token (the "no" trips the veto), which means #1042's force-push:{--no-verify} binding is effectively moot until this is fixed. The security-engineer confirmed the veto is pre-existing (#1031/#1032), not a #1042 regression.

Proposed direction

The veto must detect the user's decline intent without tripping on veto-word substrings inside the command literal. Candidate approach:

  • Scan the label for decline intent (decline options encode intent in their label: "Continue reviewing", "Pause work for now"), and exclude the command-literal region — already located by locate_command_region — from the description scan.
  • ⚠️ This touches a SACROSANCT mechanism: the change MUST be verified to not weaken the veto (a genuine decline answer must still be caught). Mis-scoping the exclusion could create an under-block in the approval path (a decline that nonetheless mints). Warrants its own PREPARE→ARCHITECT→TEST cycle with an independent adversarial security review — exactly because the failure direction here is the dangerous one.

Severity / priority

Ergonomics (over-block-safe; no security exposure). Low–medium priority. Splits out of the #1042 review (PR #1048) as a focused follow-up rather than expanding that PR into a second SACROSANCT mechanism.

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