Repository navigation
Description
Agent Brief
Type: Bug
Summary: block-dangerous-git's alias walk reaches its deny on a 512 KB stack instead of overflowing first.
Current behavior:
#7923 changes run-guards.sh so a guard's exit() unwinds before the next guard runs, which keeps a deep guard's deny. That PR could not meet the 512 KB criterion, and its Not verified section says why: "the first guard's alias walk itself overflows below about 700 KB before it exits, so the test uses 1 MB and the default stack." The recursion is the alias re-expansion in plugins/guardrails/hooks/block-dangerous-git.sh (around lines 658-706), capped by HOOK_ALIAS_WORK_MAX=128 (:694). On a stack below about 700 KB the guard crashes (SIGSEGV, exit 139) before it prints its deny. Claude Code blocks only on exit 2, so the crash lets the command through.
Desired behavior:
- With
ulimit -s 512, the 141-hop inline alias chain from fix(guardrails): run-guards loses a deep guard's deny on a small stack (Windows exits 0) #7667 (git -c alias.a1=a2 … -c alias.a141=a142 a1), run through the shipped Bash row, exits 2 with the alias-budget deny. - Walks that stay inside the budget keep today's verdicts.
- The alias walk is iterative, with a hard depth cap, and the guard denies when the cap is hit (GitHub Security & Cost's fix shape).
- The guard fails closed: any internal error or signal in it exits 2 with a deny message, never another status.
Key interfaces:
- The alias re-expansion in
block-dangerous-git.sh(alias_reexpand_admit,hook::git_alias_admit). - The run-guards Bash row named in fix(guardrails): run-guards loses a deep guard's deny on a small stack (Windows exits 0) #7667.
Acceptance criteria:
- The diff adds a test that runs the 141-hop chain through the Bash row under
ulimit -s 512and expects exit 2 and the alias-budget deny. - The diff adds a stay-loud test that forces an internal failure or deep recursion in the guard and expects exit 2 with a deny message.
-
plugins/guardrails/hooks/block-dangerous-git.test.shpasses unchanged, apart from added cases. -
scripts/check-changelog-fragments.shpasses, with oneguardrailspatch fragment added. - CI's
test-bashpasses on the PR.
Out of scope:
- The run-guards unwinding (fix(guardrails): unwind a guard's exit before running the next guard #7923). An iterative walk does not need it, so this item does not wait on it.
- Other guards' stack depth.
Filed by the Issue Triage session as the follow-up to #7923's unmet criterion. The peer sessions agreed to it under the owner's overnight procedure.
Activity
melodic-automation-lanes commentedon Oct 20, 2026
The work-resolve-issue lane stopped partway through this issue and needs a person.
Why, in the run's own words:
What the run says it did not do:
Partial work is on the branch
work-resolve-issue/issue-8530-39382060403-1. A retry continues from it; delete the branch first to start again from the default branch.Run: https://github.com/melodic-software/claude-code-plugins/actions/runs/39382060403
To retry, remove
needs-humanand addagent-readyagain.melodic-automation-lanes commentedon Oct 20, 2026
The work-resolve-issue lane stopped partway through this issue and needs a person.
Why, in the run's own words:
Partial work is on the branch
work-resolve-issue/issue-8530-39382060403-1. A retry continues from it; delete the branch first to start again from the default branch.Run: https://github.com/melodic-software/claude-code-plugins/actions/runs/39386636889
To retry, remove
needs-humanand addagent-readyagain.kyle-sexton commentedon Oct 20, 2026
Remaining scope after #8563 (merged 2026-10-20T08:07:50Z as 85d44db):
block-dangerous-gitandblock-noncanonical-commitcap the alias walk's nesting depth at a value sized to the stack: about 20 KB a level from/proc/self/limits, a fallback of 25 without/procor with an unlimited stack, and a ceiling of 409. At the cap they deny with exit 2. Test overrides can only lower the cap. Covered: the 141-hop chain underulimit -s 512, both direct and dispatched.GitHub Security & Cost agreed to this split.
kyle-sexton commentedon Oct 20, 2026
The remaining fail-closed requirement moves to #8569. bash cannot trap its own SIGSEGV, so the parent launcher enforces it:
exec-bash.mjs --fail-closedmaps any guard child outcome other than 0 or 2 to a deny, and only guardrails guard rows pass the flag, because the launcher is shared with formatter and lint hooks. GitHub Security & Cost chose this route. The run-guards unwinding half is #7923, which is held for the owner. This issue can close when #8569 merges.