Skip to content

fix(guardrails): deny a guard row that exits 0 with no verdict under --fail-closed #8996

Closed

Description

@kyle-sexton

Refs: #7667

Problem

#7667 was filed to stop a guardrails fail-open: on Windows, a crashing guard row printed its deny and exited 0, so the tool call went ahead. #7923, #8563 and #8674 fixed the cases #7667 described. One case is still open. If a run-guards row crashes on Windows and ends as plain exit 0 with no output, exec-bash --fail-closed cannot tell it from a clean pass. Its low-byte-zero decode only covers codes ≥0x100 (plugins/guardrails/hooks/lib/exec-bash.mjs:502-509). In a security guard, that is the same fail-open #7667 set out to stop.

Fix direction

Have run-guards write a verdict marker on every completed run (allow or deny). Make exec-bash --fail-closed deny when a guard row exits 0 without that marker. #7681's rebrief covers the same marker idea, so build this with #7681 or fold it into it.

Acceptance criteria

  • A guard row that exits 0 with no verdict marker is denied under --fail-closed.
  • A row that completes normally still passes, on Linux and on Windows Git Bash.
  • The PR's CI runs the exec-bash and run-guards suites, and they pass on both OS lanes.

Activity

  1. added
    priority: highSignificant impact, or blocks an imminent release; staff this cycle.
    area: securitySecurity-relevant: vulnerability, hardening, or disclosure follow-up.
    needs-humanHuman-in-the-loop required; autonomous sessions must not resolve items carrying this.
    status: confirmedA maintainer confirmed the item is real and in scope; ready to be worked.
    on Oct 22, 2026
  2. added theissue type on Oct 22, 2026
  3. removed
    needs-humanHuman-in-the-loop required; autonomous sessions must not resolve items carrying this.
    on Oct 25, 2026
  4. kyle-sexton commented on Oct 25, 2026

    @kyle-sexton
    MemberAuthor

    Released for re-triage under the owner's 2026-10-24 rule: an issue stops for a person only for an intent or vision question, a security call, a host or attended run, or spend outside prepaid credit. The hold label came off and intake triage runs again now; it re-applies a hold, with a numbered question, when one of those applies.

    To hold this issue again, add needs-human (or status: needs-decision) and say why in a comment.

  5. added
    agent-readyFully specified and briefed; eligible for autonomous pickup from the frontier.
    on Oct 25, 2026
  6. melodic-automation-lanes commented on Oct 26, 2026

    @melodic-automation-lanes
    Contributor

    The work-resolve-issue run that agent-ready started failed or was cancelled before it reported, or it stopped or the token broker refused it because its workflow files differ from main's. work-unblock removed and added agent-ready back to start a fresh run on main's workflow files. If this run ends the same way, it re-fires the issue again, waiting longer each time.

  7. added
    agent-readyFully specified and briefed; eligible for autonomous pickup from the frontier.
    and removed
    agent-readyFully specified and briefed; eligible for autonomous pickup from the frontier.
    on Oct 26, 2026
  8. melodic-automation-lanes commented on Oct 26, 2026

    @melodic-automation-lanes
    Contributor

    The token broker refused the work-resolve-issue run that agent-ready started because the lanes kill switch was on, so it did no work. work-unblock removed and added agent-ready back to start a fresh run. A kill-switch refusal is no fault in this issue or the lanes, so it never counts against later re-fires; after limits.limit-stops-per-issue of them since a person last added agent-ready or a run last reported, work-unblock waits longer before each one, up to 24 hours.

  9. added
    agent-readyFully specified and briefed; eligible for autonomous pickup from the frontier.
    needs-humanHuman-in-the-loop required; autonomous sessions must not resolve items carrying this.
    and removed
    agent-readyFully specified and briefed; eligible for autonomous pickup from the frontier.
    on Oct 26, 2026
  10. melodic-automation-lanes commented on Oct 26, 2026

    @melodic-automation-lanes
    Contributor

    The work-resolve-issue lane did not start this issue: it needs a person to make it clearer first.

    Why, in the run's own words:

    Open PR #10120 (for #7681, labeled needs-human) already builds this exact fix in the same files: lib/exec-bash.mjs and its 24 vendored copies, plugins/guardrails/hooks/run-guards.sh, hooks.json, and the exec-bash and run-guards suites. In that PR, run-guards writes a verdict marker to a launcher-made temp file (EXEC_BASH_VERDICT_FILE), and a new per-row --require-verdict flag, passed on all five --fail-closed rows, denies an exit 0 that has no marker. This issue says to build with #7681 or fold into it, and #7681's recorded decision says a human builds or merges it because it is structural. A second implementation from this lane would conflict with #10120 file for file. A person needs to decide: (1) fold #8996 into #10120 (add Closes #8996 there, or close #8996 as covered by it), or (2) keep #8996 as separate work, and if so, say what it should add beyond #10120. One example: making --fail-closed alone require the marker. #10120 makes the marker opt-in through --require-verdict, and this issue's first acceptance criterion says 'under --fail-closed'.

    What the run says it did not do:

    • No implementation, review, build or test: this run pushed back before any edit, because the fix is already in flight in PR #10120.
    • The PR CI exec-bash and run-guards suites on both OS lanes (acceptance criterion 3) were not run: no PR was opened.

    Run: https://github.com/melodic-software/claude-code-plugins/actions/runs/40390864338

    To retry, answer the question here, remove needs-human and add agent-ready again.

  11. kyle-sexton commented on Oct 27, 2026

    @kyle-sexton
    MemberAuthor

    Closing: met on main by #10120 (merged 2026-10-27T02:35:29Z, #10120).

    • A guard row that exits 0 with no verdict marker is denied under --fail-closed: all five --fail-closed rows in plugins/guardrails/hooks/hooks.json (lines 45, 65, 84, 102, 120) pass --require-verdict; the launcher exits 2 when the marker is missing (plugins/guardrails/hooks/exec-bash.test.sh:115-118).
    • A row that completes normally still passes on Linux and Windows Git Bash: run-guards.sh writes the marker at each deliberate exit; test-bash and test-windows-guards / test-windows-hooks / test-windows-lib passed on feat(guardrails): deny a blocking hook row whose dispatcher exits 0 without a verdict #10120.
    • The PR's CI ran the exec-bash and run-guards suites on both OS lanes and they passed (same checks).

    The per-row flag rather than an implicit --fail-closed behavior meets criterion 1, since every fail-closed row carries it.

  12. kyle-sexton commented on Oct 27, 2026

    @kyle-sexton
    MemberAuthor

    Addendum: a structural test guards the pairing, so a future fail-closed row cannot fail open silently. plugins/guardrails/hooks/exec-bash.test.sh:205-210 fails unless every guard row in hooks.json passes --fail-closed and then --require-verdict, and :216 pins the five dispatcher rows.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: securitySecurity-relevant: vulnerability, hardening, or disclosure follow-up.needs-humanHuman-in-the-loop required; autonomous sessions must not resolve items carrying this.priority: highSignificant impact, or blocks an imminent release; staff this cycle.status: confirmedA maintainer confirmed the item is real and in scope; ready to be worked.

    Type

    Fields

    No fields configured for Bug.

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions