fix(safety-gate): close the find delete-verb gap#262
Merged
Conversation
The gate keys each rule on its segment's binary and had arms for rm, git and kubectl but none for find, so every delete expressed through find passed unchallenged. On 2026-07-08 a cleanup batch ran `find <bun-global-root> -mindepth 1 -delete` against a path that merely looked like a cache and wiped a bun global install (the omp cockpit). The rm rule never saw it: that segment's binary was find, not rm. Adds a find arm covering -delete and -exec/-execdir/-ok/-okdir with rm, and extracts the build-artifact allowlist into targets_all_safe() so rm and find share one definition of "regenerable". Fail-closed: a find with no path operand blocks. Pinned as D1-D5 in tests/test-hooks.sh. Red before: 469/471 with D1+D2 failing. Green after: 471/471.
Red baseline 469/471 (D1+D2 fail), green 471/471, plus a negative control that reverts the hook to its pre-fix blob and shows the incident command flip 2 -> 0 -> 2 while the rm control stays blocked throughout. Ships the control as a runnable script: its payloads live in the file rather than on the command line, because the live gate otherwise fires on the harness testing it. The per-step sha print is load-bearing, a first attempt stashed an already-clean file, silently did not revert, and produced a false PASS.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds a
findarm tosafety-gate.shso deletes expressed throughfindare gated the same wayrm -rfalready was, and extracts the build-artifact allowlist intotargets_all_safe()so both verbs share one definition of "regenerable".Why
Not hypothetical. On 2026-07-08 a disk-cleanup batch ran this, and the gate did not fire:
The target was
~/.cache/.bun. It reads like a cache; it was actually the bun global install root, holdinginstall/global/node_modules/and thebin/symlinks. It wiped theomp(oh-my-pi) cockpit. Nobody noticed until 2026-07-17, whenomp updatecame back "command not found".The gate is parse-aware and keys each rule on its segment's binary (SPEC-064). It had arms for
rm,gitandkubectl. That segment's binary wasfind, so it matched nothing and passed.xargs rm -rfwas already covered (the wrapper-strip loop reduces it torm);findwas the hole.How
-delete, or-exec/-execdir/-ok/-okdirpaired with anrmtoken, marks the segment as a delete.findwith no path operand blocks.rmarm's behavior is unchanged; it now calls the shared helper.Proof
docs/verification/find-delete-gate.md, with a runnable negative control atdocs/verification/find-delete-gate-negctl.sh.tests/test-hooks.shbeforetests/test-hooks.shafter2 -> 0 -> 2as the hook is reverted and restored;rm -rfcontrol stays blocked throughoutD1-D5 are pinned as permanent regression tests.
Reviewer notes
Two things worth a look:
-F.git stashon an already-committed clean file. It was a no-op, never reverted, and produced a false PASS. The sha caught it. Left in on purpose.Known residual (deliberately not closed)
The allowlist still treats a bare relative
.cacheas regenerable, sorm -rf .cacheis permitted, which from$HOMEis the same class of mistake by a different route. The entry exists for project-local.cachebuild dirs, so narrowing it is a separate call with its own false-positive cost. Flagged rather than silently changed.