Skip to content

Close blind held-out-4 guard misses and false positives; score on a second blind corpus - #1990

Merged
remigiusz-antczak merged 1 commit into
mainfrom
land/guard-h4
Oct 25, 2026
Merged

remigiusz-antczak merged 1 commit into
mainfrom
land/guard-h4

Conversation

@remigiusz-antczak

Copy link
Copy Markdown
Collaborator

This closes the destructive-guard misses and false positives found on a blind corpus, and checks the result on a second blind corpus frozen before the rules were written.

Held-out-5 (blind, 60 dangerous / 60 benign) Blocked Benign blocked
Perun guard, this PR (rules frozen at 372fd3336 before the corpus existed) 50/60 4/60
Perun guard, main before this PR 41/60 6/60
claude-code-safety-net, ported subset 20/60 (2 unported) 2/60
claude-code-hooks-mastery, ported 8/60 4/60
  • Held-out-4 was scored blind on main: 35/60 blocked, 9/60 benign blocked. Safety-net blocked 14/60 (FP 1) and hooks-mastery 7/60 (FP 4). The rules in this PR were written against held-out-4, so its 60/60 with 0 FP is labelled in-sample. The blind row is kept beside it.
  • Both corpora were preregistered and pushed before scoring: prereg/heldout4 (e8dd4d4) and prereg/heldout5 (384ae0d). Ledger line 1 of each is byte-identical to its prereg commit.
  • No rule was tuned toward held-out-5. Its 10 remaining misses and 4 false positives are listed in its RESULTS.md as leads for the next wave, which will be scored on a fresh corpus. Container passthrough (docker exec, kubectl exec -- rm …) is the largest gap.

Rule changes in destructive_guard.py:

  • Now allowed: usage flags, --dry-run forms for aws, kubectl and helm (judged on the last dry-run token), and rm/find under a named child of $TMPDIR or /tmp.
  • Now blocked: package unpublish, yank and bulk purge; cloud and cluster deletes; terraform state rm; parted mklabel; hex and printf decode-then-execute; alias and one-step variable indirection; truncating redirects onto system files; git filter-branch.

The independent review found 2 🔴, both fixed, and a re-review checked the fixes with probes:

  • A --help anywhere in the arguments exempted the whole command. On macOS, rm -rf ~ --help runs for real. The exemption now never applies to rm, chmod, chown, dd, shred, mkfs or diskutil, including behind sudo, env, xargs, nohup or timeout.
  • A variable assignment ending in a backslash made shlex raise, and the check then fell back to the regex path and skipped every new rule. That path no longer calls shlex.

Of the 7 🟡 from the first review:

  • The decode-exec regexes hung on sh $(xxd repeated. They are now a windowed, capped scan with timing tests; 10k-character adversarial inputs finish in about 0.1 s.
  • The other 6 are fixed too: dry-run judged on the last token, // and >| redirects, apt purge scope, the /tmp residuals documented, the ledger key, and the RESULTS citation.

The re-review's 0 🔴 / 3 🟡 are folded into the next guard wave:

  • >> onto system files.
  • Nested-substitution and openssl enc decode shapes.
  • A decode-scan cap that can fire without a decoder.

Local gates:

  • test_security_gates*.py: all pass. The held-out-4 and held-out-5 pins are new; held-out-1024 and held-out-2 pins were raised only.
  • test-destructive-guard.sh: 16/16. bench_check tests: 9/9 and 2/2.
  • bench_check --changed: 2 dirs clean. Size-ratchet: ok. Privacy on changed files: clean. git diff --check: clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_014QNFFPWVcPUbeuvwM5fk2Z

… 4 and 5

Adds the blind, preregistered held-out-4 and held-out-5 destructive-command corpora (60 dangerous, 60 benign each). Held-out-4 blind score on main: guard 35/60 blocked, 9/60 benign blocked; ported safety-net subset 14/60 (FP 1), hooks-mastery 7/60 (FP 4). Held-out-5 (rules frozen first): guard 50/60 FP 4/60, main before the fixes 41/60 FP 6/60, safety-net 20/60, hooks-mastery 8/60.

destructive_guard.py now allows usage flags, aws/kubectl/helm --dry-run forms and rm/find under a named $TMPDIR or /tmp child, and blocks package unpublish/yank/purge, cloud and cluster deletes, terraform state rm, parted mklabel, hex/printf decode-eval, alias and one-step variable indirection, redirects over system files and git filter-branch. A --help after other arguments of rm/chmod/chown/dd/shred/mkfs/diskutil is still checked. The decode-eval scan is windowed and capped, with timing tests. The held-out-4 in-sample re-score is 60/60 with 0/60 FP and is labelled in-sample in RESULTS.md and docs/perun-vs-rivals.md, with the blind row kept. score.py tolerates absent secret corpus files; bench_check accepts the sha256_frozen_txt ledger key. Held-out-1024 and held-out-2 pins raised upward only, with notes.

Tests: all security gates, test-destructive-guard 16/16, size-ratchet, bench_check green

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014QNFFPWVcPUbeuvwM5fk2Z
@remigiusz-antczak
remigiusz-antczak merged commit 352be03 into main Oct 25, 2026
1 check passed
@remigiusz-antczak
remigiusz-antczak deleted the land/guard-h4 branch October 25, 2026 02:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant