Phase 3 commit #1 per docs/PHASE3.md §12. Static-pattern destructive-op
heuristic; no LLM second-opinion yet (lands in commit #2).
Implementation:
- 34 patterns in DESTRUCTIVE_PATTERNS table, grouped:
9 shell-wrapper patterns (R-B1 — bash -c / sh -c / zsh -c / eval /
python -c / perl -e / pipe-to-sh both forms / pipe-to-bash both
forms / xargs ... rm). HALT on the wrapper itself; user reads
the inner before proceeding.
10 filesystem destructive (rm -rf, find -delete, dd to device, mkfs,
shred, wipefs, truncate -s 0, ...).
5 version-control destructive (git push --force/-f, git reset
--hard, git clean -fd, git branch -D).
5 database/process (DROP TABLE/DATABASE, TRUNCATE TABLE,
kill/pkill -9).
2 permission (chmod 777, chown on root path).
- ci=true flag for case-insensitive SQL patterns; rule patterns must
be lowercase when ci is set (matcher lowercases input).
- pkill -9 ordered BEFORE kill -9; kill rule uses %f[%w] frontier so
"pkill -9 nginx" reports "pkill -9" not "kill -9" substring match.
- M._patterns exposes the rule table for :safety patterns meta (Phase
3 commit #5) and for the test corpus.
- M.norris_step stub stays — lands in commit #4.
Test corpus (test_safety.lua, 87 cases):
- 49 destructive cases across all categories (incl. all 11 wrapper
forms, the canonical curl|sh end-of-string bypass, sudo-prefixed
rm -rf, etc.).
- 38 safe cases (read-only commands, non-destructive variants
of risky verbs like "git push" without --force, "find" without
-delete, "chmod 644", "kill 1234" without -9, etc.).
- Documented one accepted false positive: echo "rm -rf /" matches
the rm pattern by substring — Norris user can proceed after
reading; tradeoff between false positives and false negatives,
biased toward false positives per §5.
- Run from repo root: `luajit test_safety.lua`. Exit 0 on pass.
- Verified all 87 pass at commit time.
R-C4 / readline rebind, broker opts.max_tokens, LLM second-opinion,
norris_step planner, repl driver, and the wider Norris UX land in
subsequent commits per §12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>