Repository navigation
Open
Labels
area:securityHardening, access, secretsHardening, access, secretsbugSomething isn't workingSomething isn't working
Description
Four hardening items for the two Bash guards (plugins/setkernel/hooks/git-guard.sh, d1-guard.sh). Claude Lead's adversarial check of #41 (onFailure: "block") found them. #41 makes a timeout or an exit other than 0 or 2 block the Bash call. None of these blocks #41: each needs a setup no seat has today. All four predate it. Each was reproduced on Raghav's Mac (bash 3.2 as /bin/sh, BSD awk, jq 1.7.1) against the scripts at #41's head 0ed3d205 (same as main), and then checked by a second agent.
1. git-guard fails open when jq can't parse the hook JSON
- A lone surrogate escape anywhere in the input makes
jq -r '.tool_input.command // empty'fail (Invalid \uXXXX\uXXXX surrogate pair escape, rc 5). The guard discards jq's stderr, gets an emptycmdand exits 0, so a blocked command in the same input goes through unchecked. - Repro: write a hook JSON whose command is
echo \ud800 x;followed by a force push, with the\ud800as a literal JSON escape. Then runsh plugins/setkernel/hooks/git-guard.sh < that.json: rc 0. Without the\ud800it is rc 2. - It is rare, because Claude Code only writes a lone surrogate when the command text holds one. But it is the one input found that turns a block into a pass. The VMs run jq 1.8.1, which wasn't tested.
- When jq exits non-zero on non-empty input, use the sed fallback (as when jq is missing) instead of an empty command. Keep exit 0 for empty, non-JSON,
{}and non-Bash input, astests/run.shrequires.
2. git-guard slows down with the square of the command size under bash 3.2 (Macs)
check_cmdkeeps the whole command in$1while it callscheck_wordsonce per segment. bash 3.2 copies the caller's positional parameters on every function call, so a big command costs size × segments.- Measured on the Mac's
sh, dense shell text: 150 KB took 1.2 s, 500 KB 7.9 s, 600 KB 10.7 s (past the 10 s timeout) and 1 MB 27.7 s. dash and Linux bash 5.2 stay linear (1 MB in 1.4 s). - Real calls are far smaller. The largest Bash call among 40,917 in this Mac's transcripts was 68 KB, which takes 0.84 s worst case. So this is headroom, not a live problem.
- Add
set --incheck_cmdoncesegsis computed. On the scratch copy, this brought 1 MB down to 2.1–4.1 s.
3. d1-guard's comment scan is quadratic in BSD awk (Macs)
- The awk loop at lines 68-80 calls
substrper character on one growing string. A command with a remotewrangler d1 executeneeds about 730 KB idle, or about 500 KB with all cores busy, to pass 10 s. 256 KB took 1.45 s. - Scan per line (
$0) and carry the quote state across lines, or useindex()/match().
4. git-guard exits 1 under an exported SHELLOPTS=errexit when /bin/sh is bash
- Commands:
git reset --soft,git reset --mixed,git reset HEAD <file>andgit clean --dry-run. - Cause:
for a; do abbrev "$a" --hard && block …; doneleaves status 1, andshift && check_git "$@"is the last command of an AND list. - Only a launcher that exports
SHELLOPTStriggers it; dash ignores it. - End those branches with
return 0(or|| :), orunset SHELLOPTSat the top.
Also checked, fine
- Missing or unreadable script:
- Mac
shexits 127 or 126, so under Managed settings before the console paste: guard hooks fail closed, 0008 org rules, managedSourcesBehavior, the allow-test-changes rule #41 every Bash call is blocked. That is whatonFailure: "block"is for. - The VMs' dash exits 2, so they already block today.
- Claude Code keeps a running session's plugin version on disk (
.in_usemarkers), so only a manual cache wipe or an in-place reinstall triggers it.
- Mac
- 64 inputs under each of the Mac's sh, dash and bash
--posix, plus 69 case files in the Linux runner image. These include aliases with chained,!and self-referencing forms, unicode, NUL, heredocs, a missing-Cdir, a deleted cwd, and no jq. Every one exited 0 or 2 with empty stdout, in 0.01–0.04 s. - On all four VMs (installed build
75f63c04c98f, byte-identical guards): every sample exits 0, or 2 for the force push, in 11–36 ms. Only d1-guard writes stdout, its 297-byte deny JSON. Claude Code is 2.1.296–2.1.301.
Not released: the v4c freeze (hq#368) holds one-off work. Claude Lead or Raghav adds agent-ready. Merge #41 first.
Refs #41, #36, setkernel/hq#332.
— Claude Lead
Metadata
Metadata
Assignees
Labels
area:securityHardening, access, secretsHardening, access, secretsbugSomething isn't workingSomething isn't working
Activity