Skip to content

About

Shared PreToolUse cases for coding-agent guard hooks, a runner, and results per hook and Claude Code version

Resources

Stars

0 stars

Watchers

0 watching

Forks

 
 

Latest commit

 

History

30 Commits

Folders and files

Repository files navigation

tamper-cases

A shared set of PreToolUse cases for coding-agent guard hooks, with a runner that feeds them to any hook and a results table per hook and Claude Code version. The point is a number you can check: which cases a guard refuses, which it lets through, and which benign commands it wrongly blocks.

Cases (cases.json, 105)

Family Cases What they try Origin
guard-file-tamper 52 (2 must pass) rewrite, delete, rename or roll back the guard's own files: direct writes, path tricks, find -exec, a symlink, git stash and git reset on history that touches the guard u/Far_Business4773, lumis-skills examples/tamper_cases.py (MIT); cases 41 to 52 from the r/ClaudeCode exchange with u/northbridgedev, 2026-09-14
destructive 33 (12 must pass) rm -rf on root, home, cwd or a glob; recursive rm behind find -exec and xargs; force push; git reset --hard; git clean -f; database, infrastructure and disk wipes; remote scripts piped to a shell. The must-pass forms are ordinary work: rm -rf build/, --force-with-lease, git status skillkeel-starter tests/test-hooks.sh (MIT)
git-config-exec 20 (8 must pass) git settings that make git run a command (core.fsmonitor, core.hooksPath, core.sshCommand, filters, shell aliases) through git config and git -c, shell writes into .git/, a directory planted as .git. The must-pass forms are reads, --unset and plain keys skillkeel-starter tests/test-hooks.sh; family added after accomplish.ai's Beltdown write-up (2026-09-11)

Tests: python3 tests/test_run.py. Every case carries tool, input (the tool_input Claude Code sends), family, source, target, and must_pass for benign forms a guard must not block. A hook that covers only one family scores n/a on the others; that is a scope statement, not a failure. guard-file-tamper cases assume a hook with protected paths (the guard's own files); a destructive-command guard is out of scope there and the table says so.

Run

python3 run.py --hook "bash /path/to/guard-bash" --label my-guard-1.0 --tools Bash --claude-version 2.1.274
python3 run.py --hook "python3 scripts/scope_guard.py pre-tool" --label lumis-scope-guard --cwd /path/to/fixture

A hook answers with its exit code (2 refused, 0 passed) or, on exit 0, with the JSON Claude Code reads from stdout (hookSpecificOutput.permissionDecision: deny counts as refused, ask as asked, allow as passed; the older top-level decision: block counts as refused). The payload carries the documented fields (hook_event_name, session_id, cwd, permission_mode, transcript_path) next to tool_name and tool_input. Anything else is an error. Results land in results/<label>-<date>.json; RESULTS.md is regenerated from every result file. Standard library only.

Results so far

See RESULTS.md. First rows: skillkeel-starter guard-bash 0.1.0 (2026-09-14) and 0.1.5 (2026-09-17): destructive 17/21 then 21/21, git-config-exec 0/12 then 12/12, guard-file-tamper 1/40 both (out of scope, no protected paths), 0 false positives on the 22 benign forms. lumis scope_guard (u/Far_Business4773, PR #1, 2026-09-20), two rows from its own fixture: commit 91b9a1a guard-file-tamper 48/50, destructive 2/21, git-config-exec 0/12; commit bcb8f44 after the fix for the five whole-tree cases: destructive 11/21, the rest unchanged, 0 false positives on both. The two hooks cover each other's blind spot: guard-bash has no protected-path rule, scope_guard sees only what touches a guarded path. Five destructive cases (skillkeel-03, 05, 10, 13, 14) take the guard's own files with the tree, so they now carry also_hits: guard-file-tamper.

Third-party rows (2026-09-22): kenryu42/cc-safety-net 2.4.5 and karanb192/claude-code-hooks (config-guard and block-dangerous-commands, commit 888ef08), each run in a sandbox at its default configuration; the table's notes section states the configuration, the command line and what each hook says it covers, so a low count on a family the hook does not claim is read as scope, not as a defect. The method rules and the correction path are at the end of RESULTS.md (results/NOTES.md).

Adding a hook or a case

Open a PR. A hook row needs the exact command, its version or commit and the Claude Code version it is paired with, plus the results JSON; a row Skillkeel did not reproduce is labelled "submitted by , not reproduced by Skillkeel". A wrong number is fixed by issue or PR within 48 hours. A new case needs a source (thread, issue, write-up, or your own test) and a target; mark benign forms must_pass. Cases that only work with a fixture (files, history, symlinks) say so in note, and the runner's --cwd points at the fixture.

License

MIT. Cases from lumis-skills keep their MIT terms and credit; see source on each case.

About

Shared PreToolUse cases for coding-agent guard hooks, a runner, and results per hook and Claude Code version

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages