cc-safe-setup 29.7.0 → 30.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1022) hide show
  1. package/.claude-plugin/marketplace.json +72 -19
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CONTRIBUTING.md +7 -2
  4. package/COOKBOOK.md +50 -46
  5. package/LICENSE +21 -0
  6. package/MIGRATION.md +8 -8
  7. package/README.md +419 -512
  8. package/SAFETY_CHECKLIST.md +15 -7
  9. package/SERVICES.md +194 -0
  10. package/SETTINGS_REFERENCE.md +236 -17
  11. package/SKILL.md +5 -5
  12. package/TROUBLESHOOTING.md +35 -14
  13. package/action.yml +12 -4
  14. package/audit/README.md +84 -0
  15. package/audit/audit-checklist.md +152 -0
  16. package/audit/boundary-selftest.sh +85 -0
  17. package/audit/boundary.sh +123 -0
  18. package/audit/count-hooks.py +42 -0
  19. package/audit/find-dead-hooks.sh +82 -0
  20. package/audit/find-unfirable-rules.sh +142 -0
  21. package/audit/fire.sh +97 -0
  22. package/audit/selftest.sh +42 -0
  23. package/audit/unfirable-selftest.sh +81 -0
  24. package/audit-web/index.html +9 -9
  25. package/examples/README.md +54 -17
  26. package/examples/account-billing-log.sh +98 -0
  27. package/examples/account-routing-preflight.sh +123 -0
  28. package/examples/agent-sdk-credit-pool-monitor.sh +145 -0
  29. package/examples/agent-view-quota-decrement.sh +32 -0
  30. package/examples/agent-view-quota-warn.sh +94 -0
  31. package/examples/agents-md-edit-drift-warner.sh +223 -0
  32. package/examples/agents-md-loader.sh +160 -0
  33. package/examples/agents-md-sync-checker.sh +102 -0
  34. package/examples/ai-slop-punctuation-arrest.sh +210 -0
  35. package/examples/allowlist.sh +70 -10
  36. package/examples/always-allow-pattern-suggester.sh +125 -0
  37. package/examples/ansible-vault-guard.sh +2 -1
  38. package/examples/anthropic-base-url-guard.sh +76 -0
  39. package/examples/api-busyloop-guard.sh +123 -0
  40. package/examples/api-endpoint-guard.sh +12 -0
  41. package/examples/api-key-in-url-guard.sh +17 -1
  42. package/examples/api-rate-limit-guard.sh +36 -1
  43. package/examples/api-rate-limit-tracker.sh +12 -0
  44. package/examples/articulated-scope-capture.sh +101 -0
  45. package/examples/askuserquestion-autonomy-gate.sh +102 -0
  46. package/examples/attachment-traversal-guard.sh +118 -0
  47. package/examples/aup-block-pattern-logger.sh +161 -0
  48. package/examples/aup-false-positive-helper.sh +124 -0
  49. package/examples/aup-large-tool-output-warner.sh +282 -0
  50. package/examples/aup-retry-loop-guard.sh +229 -0
  51. package/examples/auth-expiry-reminder.sh +157 -0
  52. package/examples/auth-macos-sleep-detector.sh +185 -0
  53. package/examples/auth-path-detector.sh +171 -0
  54. package/examples/auth-status-checker.sh +157 -0
  55. package/examples/authorized-reconfirmation-detector.sh +195 -0
  56. package/examples/auto-approve-build.sh +39 -0
  57. package/examples/auto-approve-cargo.sh +31 -1
  58. package/examples/auto-approve-docker.sh +35 -0
  59. package/examples/auto-approve-git-read.sh +37 -14
  60. package/examples/auto-approve-go.sh +30 -1
  61. package/examples/auto-approve-gradle.sh +29 -1
  62. package/examples/auto-approve-make.sh +29 -1
  63. package/examples/auto-approve-maven.sh +29 -1
  64. package/examples/auto-approve-python.sh +41 -1
  65. package/examples/auto-approve-readonly-tools.sh +1 -0
  66. package/examples/auto-approve-readonly.sh +110 -33
  67. package/examples/auto-approve-ssh.sh +30 -1
  68. package/examples/auto-approve-test.sh +44 -0
  69. package/examples/auto-checkpoint.sh +1 -1
  70. package/examples/auto-compact-prep.sh +0 -0
  71. package/examples/auto-git-checkpoint.sh +0 -0
  72. package/examples/auto-mode-safe-commands.sh +53 -1
  73. package/examples/auto-mode-safety-enforcer.sh +151 -4
  74. package/examples/auto-push-session-branch.sh +22 -0
  75. package/examples/aws-production-guard.sh +13 -1
  76. package/examples/aws-region-guard.sh +1 -1
  77. package/examples/background-cost-launch-guard.sh +82 -0
  78. package/examples/background-task-guard.sh +12 -0
  79. package/examples/backup-before-refactor.sh +0 -0
  80. package/examples/banned-command-guard.sh +12 -0
  81. package/examples/bash-allowlist-secondary-check.sh +157 -0
  82. package/examples/bash-domain-allowlist.sh +12 -0
  83. package/examples/bash-fanout-bounded-rewriter.sh +191 -0
  84. package/examples/bash-heuristic-approver.sh +112 -10
  85. package/examples/bash-project-fence.sh +194 -0
  86. package/examples/bash-safety-auto-deny.sh +12 -0
  87. package/examples/bash-secret-output-detector.sh +44 -3
  88. package/examples/bash-trace-guard.sh +12 -0
  89. package/examples/bashrc-safety-check.sh +0 -0
  90. package/examples/bg-permission-prompt-warner.sh +182 -0
  91. package/examples/bg-task-cooldown-guard.sh +12 -0
  92. package/examples/binary-read-detector.sh +135 -0
  93. package/examples/binary-upload-guard.sh +12 -0
  94. package/examples/block-database-wipe.sh +35 -10
  95. package/examples/branch-name-check.sh +8 -3
  96. package/examples/branch-naming-convention.sh +0 -0
  97. package/examples/broad-find-guard.sh +12 -0
  98. package/examples/broad-prefix-session-trap-warner.sh +106 -0
  99. package/examples/bulk-file-delete-guard.sh +45 -0
  100. package/examples/bypass-mode-effective-verifier.sh +203 -0
  101. package/examples/cache-creation-drift-detector.sh +120 -0
  102. package/examples/cache-residue-detector.sh +195 -0
  103. package/examples/cache-tier-logger.sh +70 -0
  104. package/examples/cache-ttl-eviction-detector.sh +149 -0
  105. package/examples/cargo-publish-guard.sh +14 -1
  106. package/examples/case-insensitive-path-guard.sh +21 -2
  107. package/examples/case-sensitive-guard.sh +23 -2
  108. package/examples/cch-cache-guard.sh +0 -0
  109. package/examples/cch-sentinel-precommit-guard.sh +47 -0
  110. package/examples/check-abort-controller.sh +1 -1
  111. package/examples/check-accessibility.sh +0 -0
  112. package/examples/check-aria-labels.sh +0 -0
  113. package/examples/check-before-act-enforcer.sh +12 -0
  114. package/examples/check-charset-meta.sh +0 -0
  115. package/examples/check-cookie-flags.sh +0 -0
  116. package/examples/check-cors-config.sh +0 -0
  117. package/examples/check-csp-headers.sh +0 -0
  118. package/examples/check-csrf-protection.sh +0 -0
  119. package/examples/check-debounce.sh +1 -1
  120. package/examples/check-dependency-age.sh +0 -0
  121. package/examples/check-dependency-license.sh +0 -0
  122. package/examples/check-dockerfile-best-practice.sh +0 -0
  123. package/examples/check-error-boundaries.sh +0 -0
  124. package/examples/check-error-class.sh +1 -1
  125. package/examples/check-error-handling.sh +0 -0
  126. package/examples/check-error-logging.sh +1 -1
  127. package/examples/check-error-message.sh +0 -0
  128. package/examples/check-error-stack.sh +0 -0
  129. package/examples/check-favicon.sh +0 -0
  130. package/examples/check-git-hooks-compat.sh +0 -0
  131. package/examples/check-gitattributes.sh +0 -0
  132. package/examples/check-https-redirect.sh +0 -0
  133. package/examples/check-input-validation.sh +0 -0
  134. package/examples/check-lang-attribute.sh +0 -0
  135. package/examples/check-npm-scripts-exist.sh +0 -0
  136. package/examples/check-package-size.sh +0 -0
  137. package/examples/check-port-availability.sh +0 -0
  138. package/examples/check-promise-all.sh +1 -1
  139. package/examples/check-rate-limiting.sh +0 -0
  140. package/examples/check-return-types.sh +0 -0
  141. package/examples/check-semantic-versioning.sh +0 -0
  142. package/examples/check-suspense-fallback.sh +1 -1
  143. package/examples/check-test-exists.sh +0 -0
  144. package/examples/check-test-naming.sh +0 -0
  145. package/examples/check-timeout-cleanup.sh +1 -1
  146. package/examples/check-tls-version.sh +0 -0
  147. package/examples/check-unsubscribe.sh +1 -1
  148. package/examples/check-viewport-meta.sh +0 -0
  149. package/examples/check-worker-terminate.sh +1 -1
  150. package/examples/checkpoint-tamper-guard.sh +12 -0
  151. package/examples/chmod-guard.sh +12 -0
  152. package/examples/chown-guard.sh +24 -0
  153. package/examples/ci-workflow-guard.sh +6 -1
  154. package/examples/claim-verify-detector.sh +109 -0
  155. package/examples/claim-vs-caveat-checker.sh +77 -0
  156. package/examples/classifier-fallback-allow.sh +114 -40
  157. package/examples/claude-cache-gc.sh +1 -0
  158. package/examples/claude-code-version-pin-advisor.sh +183 -0
  159. package/examples/claude-md-reinjector.sh +129 -0
  160. package/examples/claude-md-size-monitor.sh +66 -0
  161. package/examples/claude-update-budget-guard.sh +123 -0
  162. package/examples/claude-update-smart.sh +113 -0
  163. package/examples/claudeignore-enforce-guard.sh +12 -0
  164. package/examples/claudemd-enforcer.sh +0 -0
  165. package/examples/claudemd-tool-prohibition.sh +128 -0
  166. package/examples/clear-command-confirm-guard.sh +32 -15
  167. package/examples/cli-config-pinning-detector.sh +162 -0
  168. package/examples/cliff-countdown-advisor.sh +96 -0
  169. package/examples/clipboard-secret-guard.sh +12 -0
  170. package/examples/closure-word-verify-gate.sh +150 -0
  171. package/examples/cloud-cli-guard.sh +39 -2
  172. package/examples/commit-all-scope-guard.sh +44 -0
  173. package/examples/commit-message-check.sh +1 -1
  174. package/examples/commit-message-quality.sh +6 -1
  175. package/examples/commitment-carry-forward-arrest.sh +361 -0
  176. package/examples/compact-alert-notification.sh +0 -0
  177. package/examples/compact-circuit-breaker.sh +72 -0
  178. package/examples/compact-dispatch-watchdog.sh +149 -0
  179. package/examples/completion-claim-without-verification-detector.sh +207 -0
  180. package/examples/composer-guard.sh +12 -0
  181. package/examples/compound-bash-permission-resolver.sh +156 -0
  182. package/examples/compound-command-allow.sh +1 -1
  183. package/examples/compound-command-deny-enforcer.sh +183 -0
  184. package/examples/compound-inject-guard.sh +12 -0
  185. package/examples/concurrent-edit-lock.sh +12 -0
  186. package/examples/conflict-marker-guard.sh +12 -0
  187. package/examples/consecutive-error-breaker.sh +0 -0
  188. package/examples/console-log-count.sh +1 -0
  189. package/examples/context-compact-advisor.sh +29 -2
  190. package/examples/context-length-budget.sh +69 -0
  191. package/examples/context-threshold-alert.sh +12 -0
  192. package/examples/context-usage-drift-alert.sh +41 -13
  193. package/examples/conversation-history-guard.sh +12 -0
  194. package/examples/core-file-protect-guard.sh +12 -0
  195. package/examples/cors-star-warn.sh +0 -0
  196. package/examples/cost-incident-self-audit.sh +162 -0
  197. package/examples/cowork-claude-md-load-checker.sh +100 -0
  198. package/examples/cowork-fuse-staleness-watcher.sh +140 -0
  199. package/examples/cowork-hook-absence-warner.sh +167 -0
  200. package/examples/cowork-model-picker-advisor.sh +115 -0
  201. package/examples/credential-exfil-guard.sh +65 -4
  202. package/examples/credential-file-cat-guard.sh +17 -1
  203. package/examples/cron-create-receipt.sh +112 -0
  204. package/examples/cron-modification-guard.sh +12 -0
  205. package/examples/cross-product-mcp-spawn-detector.sh +178 -0
  206. package/examples/cwd-project-boundary-guard.sh +0 -0
  207. package/examples/daemon-zombie-worker-surfacer.sh +118 -0
  208. package/examples/daily-cost-guard.sh +164 -0
  209. package/examples/daily-usage-tracker.sh +0 -0
  210. package/examples/dangerous-pip-flag-guard.sh +12 -0
  211. package/examples/db-connect-guard.sh +13 -1
  212. package/examples/denied-action-retry-guard.sh +12 -0
  213. package/examples/deny-bypass-detector.sh +12 -0
  214. package/examples/deny-rule-integrity-verifier.sh +181 -0
  215. package/examples/dependency-install-guard.sh +12 -0
  216. package/examples/deploy-guard.sh +14 -2
  217. package/examples/deploy-path-verify-guard.sh +12 -0
  218. package/examples/deployment-readback-gate.sh +135 -0
  219. package/examples/deployment-readback-gh-adapter.sh +247 -0
  220. package/examples/deployment-verify-guard.sh +25 -2
  221. package/examples/destructive-db-script-write-guard.sh +166 -0
  222. package/examples/destructive-migration-write-guard.sh +163 -0
  223. package/examples/diff-size-guard.sh +12 -0
  224. package/examples/disabled-feature-toggle-advisor.sh +88 -0
  225. package/examples/disk-partition-guard.sh +12 -0
  226. package/examples/dispatch-allowlist-preflight.sh +301 -0
  227. package/examples/dispatch-liveness-watchdog.sh +216 -0
  228. package/examples/dispatch-receipt.sh +174 -0
  229. package/examples/django-migrate-guard.sh +12 -0
  230. package/examples/dns-config-guard.sh +12 -0
  231. package/examples/docker-dangerous-guard.sh +12 -0
  232. package/examples/docker-volume-guard.sh +0 -0
  233. package/examples/dockerfile-latest-guard.sh +1 -0
  234. package/examples/dotenv-anthropic-key-billing-guard.sh +99 -0
  235. package/examples/dotenv-commit-guard.sh +12 -0
  236. package/examples/dotenv-read-guard.sh +12 -0
  237. package/examples/dotfile-protection-guard.sh +12 -0
  238. package/examples/dotnet-build-on-edit.sh +1 -1
  239. package/examples/drizzle-migrate-guard.sh +27 -2
  240. package/examples/echo-probe-spam-detector.sh +176 -0
  241. package/examples/edit-counter-test-gate.sh +12 -0
  242. package/examples/edit-error-counter.sh +1 -1
  243. package/examples/edit-guard.sh +12 -0
  244. package/examples/edit-old-string-validator.sh +12 -0
  245. package/examples/edit-quote-ambiguity-guard.sh +97 -0
  246. package/examples/edit-retry-loop-guard.sh +0 -0
  247. package/examples/edit-verify.sh +0 -0
  248. package/examples/env-inline-secret-guard.sh +14 -2
  249. package/examples/env-naming-convention.sh +0 -0
  250. package/examples/env-prod-guard.sh +0 -0
  251. package/examples/env-required-check.sh +0 -0
  252. package/examples/env-source-guard.sh +12 -0
  253. package/examples/env-var-check.sh +24 -1
  254. package/examples/ephemeral-container-detector.sh +147 -0
  255. package/examples/error-memory-guard.sh +12 -0
  256. package/examples/evidence-claim-gate.sh +187 -0
  257. package/examples/exploration-budget-guard.sh +89 -0
  258. package/examples/expo-eject-guard.sh +13 -0
  259. package/examples/extended-thinking-loop-guard.sh +139 -0
  260. package/examples/extended-thinking-resume-warning.sh +119 -0
  261. package/examples/extended-thinking-tool-use-mismatch-detector.sh +212 -0
  262. package/examples/fabricated-command-detector.sh +145 -0
  263. package/examples/false-disk-full-guard.sh +63 -0
  264. package/examples/feature-deprecation-detector.sh +109 -0
  265. package/examples/file-change-tracker.sh +1 -1
  266. package/examples/file-change-undo-tracker.sh +0 -0
  267. package/examples/file-edit-backup.sh +63 -14
  268. package/examples/file-reference-check.sh +0 -0
  269. package/examples/file-size-limit.sh +12 -0
  270. package/examples/financial-operation-guard.sh +12 -0
  271. package/examples/firewall-guard.sh +12 -0
  272. package/examples/fish-shell-wrapper.sh +125 -7
  273. package/examples/five-hundred-milestone.sh +1 -0
  274. package/examples/flask-static-route-guard.sh +89 -0
  275. package/examples/forbidden-response-phrase-detector.sh +161 -0
  276. package/examples/gem-push-guard.sh +13 -0
  277. package/examples/gh-cli-destructive-guard.sh +13 -1
  278. package/examples/git-checkout-safety-guard.sh +12 -0
  279. package/examples/git-checkout-uncommitted-guard.sh +12 -0
  280. package/examples/git-config-guard.sh +12 -0
  281. package/examples/git-crypt-worktree-guard.sh +12 -0
  282. package/examples/git-filter-repo-guard.sh +56 -0
  283. package/examples/git-history-rewrite-guard.sh +12 -0
  284. package/examples/git-hook-bypass-guard.sh +0 -0
  285. package/examples/git-maintenance-guard.sh +83 -0
  286. package/examples/git-merge-conflict-prevent.sh +0 -0
  287. package/examples/git-message-length-check.sh +0 -0
  288. package/examples/git-message-length.sh +0 -0
  289. package/examples/git-operations-require-approval.sh +68 -14
  290. package/examples/git-pack-temp-cleanup-guard.sh +102 -0
  291. package/examples/git-push-blast-radius-guard.sh +218 -0
  292. package/examples/git-remote-guard.sh +24 -3
  293. package/examples/git-rm-orphan-wipe-guard.sh +85 -0
  294. package/examples/git-stash-before-checkout.sh +12 -0
  295. package/examples/git-stash-before-danger.sh +0 -0
  296. package/examples/git-submodule-guard.sh +69 -2
  297. package/examples/git-tag-guard.sh +12 -0
  298. package/examples/github-actions-secret-guard.sh +0 -0
  299. package/examples/gitignore-check.sh +0 -0
  300. package/examples/gitops-drift-guard.sh +12 -0
  301. package/examples/go-mod-tidy-warn.sh +1 -0
  302. package/examples/go-vet-after-edit.sh +12 -0
  303. package/examples/goal-iteration-limit-warner.sh +152 -0
  304. package/examples/grep-nul-false-negative-guard.sh +30 -0
  305. package/examples/growthbook-flag-monitor.sh +257 -0
  306. package/examples/hallucination-url-check.sh +1 -1
  307. package/examples/halt-signal-detector.sh +135 -0
  308. package/examples/handoff-on-clear.sh +65 -0
  309. package/examples/hardcoded-ip-guard.sh +1 -0
  310. package/examples/headless-empty-result-guard.sh +0 -0
  311. package/examples/headless-stop-guard.sh +0 -0
  312. package/examples/helm-install-guard.sh +2 -1
  313. package/examples/home-critical-bash-guard.sh +17 -1
  314. package/examples/hook-debug-wrapper.sh +6 -2
  315. package/examples/hook-permission-fixer.sh +0 -0
  316. package/examples/hook-self-disable-detector.sh +99 -0
  317. package/examples/hook-stdout-sanitizer.sh +6 -2
  318. package/examples/hook-tamper-guard.sh +12 -0
  319. package/examples/idle-session-cost-alert.sh +0 -0
  320. package/examples/image-dimension-guard.sh +109 -0
  321. package/examples/image-file-validator.sh +0 -0
  322. package/examples/iteration-quality-degradation-detector.sh +170 -0
  323. package/examples/java-compile-on-edit.sh +1 -1
  324. package/examples/json-syntax-check.sh +0 -0
  325. package/examples/k8s-production-guard.sh +13 -1
  326. package/examples/kill-process-guard.sh +12 -0
  327. package/examples/kubernetes-guard.sh +13 -1
  328. package/examples/laravel-artisan-guard.sh +13 -0
  329. package/examples/large-file-guard.sh +1 -1
  330. package/examples/log-level-guard.sh +0 -0
  331. package/examples/log-truncation-guard.sh +12 -0
  332. package/examples/long-session-malformed-tool-call-detector.sh +183 -0
  333. package/examples/loop-detector.sh +12 -0
  334. package/examples/magic-number-warn.sh +1 -0
  335. package/examples/main-branch-warn.sh +12 -0
  336. package/examples/markdown-link-check.sh +0 -0
  337. package/examples/max-concurrent-agents.sh +12 -0
  338. package/examples/max-edit-size-guard.sh +13 -0
  339. package/examples/max-file-count-guard.sh +0 -0
  340. package/examples/max-file-delete-count.sh +0 -0
  341. package/examples/max-function-length.sh +1 -0
  342. package/examples/max-import-count.sh +0 -0
  343. package/examples/max-subagent-count.sh +0 -0
  344. package/examples/mcp-config-freeze.sh +12 -0
  345. package/examples/mcp-config-poisoning-audit.sh +150 -0
  346. package/examples/mcp-data-boundary.sh +1 -1
  347. package/examples/mcp-misdiagnosis-arrest.sh +209 -0
  348. package/examples/mcp-orphan-leak-warner.sh +49 -0
  349. package/examples/mcp-permissions-ask-warner.sh +151 -0
  350. package/examples/mcp-routine-approval-detector.sh +154 -0
  351. package/examples/mcp-server-allowlist.sh +12 -0
  352. package/examples/mcp-server-guard.sh +12 -0
  353. package/examples/mcp-startup-bloat-detector.sh +87 -0
  354. package/examples/mcp-stdio-compatibility-test.sh +153 -0
  355. package/examples/mcp-tool-audit-log.sh +0 -0
  356. package/examples/mcp-tool-guard.sh +101 -1
  357. package/examples/mcp-warmup-wait.sh +0 -0
  358. package/examples/memory-chain-audit.sh +56 -0
  359. package/examples/memory-orphan-detector.sh +188 -0
  360. package/examples/memory-write-guard.sh +99 -17
  361. package/examples/migration-verify-guard.sh +12 -0
  362. package/examples/model-swap-suggester.sh +177 -0
  363. package/examples/model-version-alert.sh +5 -1
  364. package/examples/model-version-lock.sh +63 -0
  365. package/examples/move-delete-sequence-guard.sh +33 -2
  366. package/examples/multi-file-plan-routing-gate.sh +192 -0
  367. package/examples/multi-vendor-concurrent-warner.sh +108 -0
  368. package/examples/multi-window-auth-drift-detector.sh +183 -0
  369. package/examples/multiline-command-approver.sh +161 -23
  370. package/examples/nested-background-agent-guard.sh +109 -0
  371. package/examples/nested-spawn-inflight-guard.sh +178 -0
  372. package/examples/network-exfil-guard.sh +12 -0
  373. package/examples/network-guard.sh +12 -0
  374. package/examples/network-interface-guard.sh +12 -0
  375. package/examples/nextjs-env-guard.sh +1 -1
  376. package/examples/no-absolute-import.sh +0 -0
  377. package/examples/no-alert-confirm-prompt.sh +0 -0
  378. package/examples/no-anonymous-default-export.sh +0 -0
  379. package/examples/no-any-type.sh +0 -0
  380. package/examples/no-any-typescript.sh +1 -0
  381. package/examples/no-ask-human.sh +12 -0
  382. package/examples/no-assignment-in-condition.sh +0 -0
  383. package/examples/no-base64-exfil.sh +12 -0
  384. package/examples/no-callback-hell.sh +0 -0
  385. package/examples/no-circular-dependency.sh +0 -0
  386. package/examples/no-cleartext-storage.sh +0 -0
  387. package/examples/no-commented-code.sh +0 -0
  388. package/examples/no-commit-fixup.sh +0 -0
  389. package/examples/no-console-assert.sh +0 -0
  390. package/examples/no-console-error-swallow.sh +0 -0
  391. package/examples/no-console-in-prod.sh +0 -0
  392. package/examples/no-console-log-commit.sh +12 -0
  393. package/examples/no-console-log.sh +0 -0
  394. package/examples/no-console-time.sh +0 -0
  395. package/examples/no-cors-wildcard.sh +4 -1
  396. package/examples/no-curl-upload.sh +0 -0
  397. package/examples/no-dangerouslySetInnerHTML.sh +0 -0
  398. package/examples/no-dangling-await.sh +1 -0
  399. package/examples/no-debug-commit.sh +12 -0
  400. package/examples/no-debug-in-commit.sh +0 -0
  401. package/examples/no-deep-nesting.sh +0 -0
  402. package/examples/no-deep-relative-import.sh +1 -0
  403. package/examples/no-default-credentials.sh +0 -0
  404. package/examples/no-deploy-friday.sh +13 -1
  405. package/examples/no-disabled-test.sh +0 -0
  406. package/examples/no-document-write.sh +0 -0
  407. package/examples/no-empty-function.sh +0 -0
  408. package/examples/no-eval-in-template.sh +0 -0
  409. package/examples/no-eval-template.sh +3 -1
  410. package/examples/no-eval.sh +0 -0
  411. package/examples/no-exec-user-input.sh +0 -0
  412. package/examples/no-fixme-ship.sh +12 -0
  413. package/examples/no-floating-promises.sh +0 -0
  414. package/examples/no-force-flag.sh +22 -5
  415. package/examples/no-force-install.sh +0 -0
  416. package/examples/no-git-amend.sh +12 -0
  417. package/examples/no-git-rebase-public.sh +0 -0
  418. package/examples/no-global-install.sh +12 -0
  419. package/examples/no-global-state.sh +0 -0
  420. package/examples/no-hardcoded-port.sh +1 -0
  421. package/examples/no-hardcoded-url.sh +0 -0
  422. package/examples/no-helmet-missing.sh +0 -0
  423. package/examples/no-http-url.sh +1 -0
  424. package/examples/no-http-without-https.sh +0 -0
  425. package/examples/no-inline-style.sh +0 -0
  426. package/examples/no-inline-styles.sh +1 -0
  427. package/examples/no-innerhtml.sh +0 -0
  428. package/examples/no-install-global.sh +12 -0
  429. package/examples/no-jwt-in-url.sh +0 -0
  430. package/examples/no-large-commit.sh +0 -0
  431. package/examples/no-localhost-expose.sh +0 -0
  432. package/examples/no-magic-number.sh +0 -0
  433. package/examples/no-md5-sha1.sh +0 -0
  434. package/examples/no-mixed-line-endings.sh +8 -1
  435. package/examples/no-mutation-in-reducer.sh +0 -0
  436. package/examples/no-nested-ternary.sh +0 -0
  437. package/examples/no-network-exfil.sh +0 -0
  438. package/examples/no-open-redirect.sh +0 -0
  439. package/examples/no-output-truncation.sh +12 -0
  440. package/examples/no-package-downgrade.sh +0 -0
  441. package/examples/no-package-lock-edit.sh +12 -0
  442. package/examples/no-path-join-user-input.sh +0 -0
  443. package/examples/no-port-bind.sh +0 -0
  444. package/examples/no-process-exit.sh +0 -0
  445. package/examples/no-prototype-pollution.sh +0 -0
  446. package/examples/no-push-without-ci.sh +0 -0
  447. package/examples/no-push-without-tests.sh +12 -0
  448. package/examples/no-raw-password-in-url.sh +0 -0
  449. package/examples/no-root-user-docker.sh +1 -0
  450. package/examples/no-root-write.sh +12 -0
  451. package/examples/no-secrets-in-args.sh +4 -1
  452. package/examples/no-secrets-in-logs.sh +0 -0
  453. package/examples/no-sensitive-log.sh +0 -0
  454. package/examples/no-sleep-in-hooks.sh +0 -0
  455. package/examples/no-star-import-python.sh +1 -0
  456. package/examples/no-string-concat-sql.sh +0 -0
  457. package/examples/no-sudo-guard.sh +12 -0
  458. package/examples/no-sync-fs.sh +0 -0
  459. package/examples/no-todo-in-merge.sh +0 -0
  460. package/examples/no-todo-in-production.sh +1 -0
  461. package/examples/no-todo-without-issue.sh +0 -0
  462. package/examples/no-unused-import.sh +0 -0
  463. package/examples/no-var-keyword.sh +0 -0
  464. package/examples/no-verify-blocker.sh +12 -0
  465. package/examples/no-wget-piped-bash.sh +12 -0
  466. package/examples/no-wildcard-cors.sh +0 -0
  467. package/examples/no-wildcard-delete.sh +0 -0
  468. package/examples/no-wildcard-import.sh +0 -0
  469. package/examples/no-with-statement.sh +0 -0
  470. package/examples/no-write-outside-src.sh +0 -0
  471. package/examples/no-xml-external-entity.sh +0 -0
  472. package/examples/non-english-quality-warner.sh +156 -0
  473. package/examples/notify-waiting.sh +1 -1
  474. package/examples/npm-audit-warn.sh +0 -0
  475. package/examples/npm-global-install-guard.sh +12 -0
  476. package/examples/npm-publish-guard.sh +12 -0
  477. package/examples/npm-script-injection.sh +0 -0
  478. package/examples/npm-supply-chain-guard.sh +0 -0
  479. package/examples/nuxt-config-guard.sh +1 -0
  480. package/examples/oauth-refresh-monitor.sh +210 -0
  481. package/examples/opus-degradation-tracker.sh +187 -0
  482. package/examples/opus48-routine-task-warning.sh +196 -0
  483. package/examples/opus48-thinking-wedge-advisor.sh +143 -0
  484. package/examples/output-credential-scan.sh +1 -1
  485. package/examples/output-secret-mask.sh +31 -9
  486. package/examples/output-token-spike-detector.sh +145 -0
  487. package/examples/package-json-guard.sh +12 -0
  488. package/examples/package-lock-frozen.sh +12 -0
  489. package/examples/parallel-batch-size-limiter.sh +121 -0
  490. package/examples/parallel-cascade-detector.sh +116 -0
  491. package/examples/partial-view-claim-arrest.sh +174 -0
  492. package/examples/path-deny-bash-guard.sh +12 -0
  493. package/examples/path-traversal-guard.sh +12 -0
  494. package/examples/per-prompt-token-cap.sh +124 -0
  495. package/examples/permission-audit-log.sh +1 -1
  496. package/examples/permission-denial-enforcer.sh +12 -0
  497. package/examples/permission-entry-validator.sh +0 -0
  498. package/examples/persisted-1m-model-advisor.sh +88 -0
  499. package/examples/php-lint-on-edit.sh +1 -1
  500. package/examples/pii-upload-guard.sh +12 -0
  501. package/examples/pip-publish-guard.sh +13 -0
  502. package/examples/pip-requirements-guard.sh +12 -0
  503. package/examples/pip-venv-required.sh +12 -0
  504. package/examples/plan-mode-edit-guard.sh +12 -0
  505. package/examples/plan-mode-enforcer.sh +12 -0
  506. package/examples/plan-mode-strict-guard.sh +12 -0
  507. package/examples/plugin-hooks-json-bloat-detector.sh +169 -0
  508. package/examples/plugin-process-cleanup.sh +0 -0
  509. package/examples/polyglot-rm-guard.sh +12 -0
  510. package/examples/post-compact-safety.sh +12 -0
  511. package/examples/post-edit-disk-verify.sh +152 -0
  512. package/examples/powershell-remove-item-guard.sh +85 -0
  513. package/examples/pr-description-check.sh +0 -0
  514. package/examples/pr-duplicate-guard.sh +13 -0
  515. package/examples/pre-bash-sed-line-ending-windows.sh +87 -0
  516. package/examples/pre-compact-knowledge-save.sh +0 -0
  517. package/examples/pre-compact-transcript-backup.sh +0 -0
  518. package/examples/pre-execution-claim-detector.sh +179 -0
  519. package/examples/prefer-builtin-tools.sh +10 -1
  520. package/examples/prefer-const.sh +0 -0
  521. package/examples/prefer-dedicated-tools.sh +12 -0
  522. package/examples/prefer-optional-chaining.sh +0 -0
  523. package/examples/prisma-migrate-guard.sh +24 -7
  524. package/examples/production-port-kill-guard.sh +12 -0
  525. package/examples/prompt-injection-guard.sh +2 -1
  526. package/examples/prompt-usage-logger.sh +0 -0
  527. package/examples/protect-claudemd.sh +12 -0
  528. package/examples/protect-dotfiles.sh +12 -0
  529. package/examples/proxy-capture-suggester.sh +159 -0
  530. package/examples/public-artefact-socratic-narrowing.sh +232 -0
  531. package/examples/public-repo-push-guard.sh +12 -0
  532. package/examples/push-requires-test-pass-record.sh +0 -0
  533. package/examples/push-requires-test-pass.sh +12 -0
  534. package/examples/quota-anomaly-detector.sh +173 -0
  535. package/examples/quota-reset-audit.sh +58 -0
  536. package/examples/quoted-flag-approver.sh +99 -10
  537. package/examples/rails-migration-guard.sh +12 -0
  538. package/examples/rate-limit-guard.sh +0 -0
  539. package/examples/read-all-files-enforcer.sh +0 -0
  540. package/examples/read-budget-guard.sh +12 -0
  541. package/examples/read-loop-detector.sh +59 -0
  542. package/examples/read-once-guard.sh +96 -0
  543. package/examples/read-only-mode.sh +12 -0
  544. package/examples/readme-exists-check.sh +0 -0
  545. package/examples/record-read-coverage.sh +73 -0
  546. package/examples/redirect-fragment-warner.sh +149 -0
  547. package/examples/redis-flushall-guard.sh +13 -0
  548. package/examples/redundant-read-blocker.sh +132 -0
  549. package/examples/refusal-arrest-gate.sh +248 -0
  550. package/examples/registry-publish-guard.sh +13 -1
  551. package/examples/remote-control-billing-classifier.sh +135 -0
  552. package/examples/replace-all-guard.sh +12 -0
  553. package/examples/repo-visibility-guard.sh +12 -0
  554. package/examples/reroute-after-block-guard.sh +150 -0
  555. package/examples/response-budget-guard.sh +0 -0
  556. package/examples/resume-drift-watcher.sh +57 -0
  557. package/examples/rg-replace-flag-detector.sh +125 -0
  558. package/examples/rhetorical-verification-prompt-detector.sh +102 -0
  559. package/examples/ripgrep-permission-fix.sh +0 -0
  560. package/examples/rm-safety-net.sh +151 -8
  561. package/examples/role-tool-guard.sh +12 -0
  562. package/examples/route-handler-emptiness-gate.sh +324 -0
  563. package/examples/ruby-lint-on-edit.sh +1 -1
  564. package/examples/runtime-binary-change-detector.sh +102 -0
  565. package/examples/same-command-repeat-detector.sh +196 -0
  566. package/examples/same-correction-arrest.sh +169 -0
  567. package/examples/sandbox-denywrite-enforcer.sh +78 -0
  568. package/examples/sandbox-relative-path-audit.sh +83 -25
  569. package/examples/sandbox-write-verify.sh +12 -0
  570. package/examples/schema-migration-guard.sh +0 -0
  571. package/examples/scope-expansion-receipt.sh +178 -0
  572. package/examples/scope-guard.sh +88 -9
  573. package/examples/secret-file-read-guard.sh +12 -0
  574. package/examples/secret-file-write-guard.sh +150 -0
  575. package/examples/self-modify-bypass-guard.sh +0 -0
  576. package/examples/sensitive-file-read-guard.sh +23 -3
  577. package/examples/sensitive-log-guard.sh +1 -0
  578. package/examples/server-side-prompt-injection-detector.sh +69 -0
  579. package/examples/session-agent-cost-limiter.sh +12 -0
  580. package/examples/session-backup-on-start.sh +1 -1
  581. package/examples/session-cost-alert.sh +12 -0
  582. package/examples/session-drift-guard.sh +12 -0
  583. package/examples/session-duration-guard.sh +25 -2
  584. package/examples/session-end-logger.sh +0 -0
  585. package/examples/session-health-monitor.sh +0 -0
  586. package/examples/session-index-repair.sh +1 -1
  587. package/examples/session-memory-watchdog.sh +1 -0
  588. package/examples/session-permission-reset-guard.sh +12 -0
  589. package/examples/session-persistence-verifier.sh +93 -0
  590. package/examples/session-plugin-pin-watch.sh +68 -0
  591. package/examples/session-rate-monitor.sh +176 -0
  592. package/examples/session-resume-env-fix.sh +0 -0
  593. package/examples/session-start-quota-status.sh +193 -0
  594. package/examples/session-start-safety-check.sh +55 -3
  595. package/examples/session-state-saver.sh +0 -0
  596. package/examples/session-summary-stop.sh +1 -1
  597. package/examples/session-summary.sh +0 -0
  598. package/examples/session-token-counter.sh +1 -1
  599. package/examples/settings-hooks-key-restorer.sh +99 -0
  600. package/examples/settings-json-health-check.sh +57 -0
  601. package/examples/settings-json-model-guard.sh +0 -0
  602. package/examples/settings-regression-tester.sh +188 -0
  603. package/examples/shell-config-truncation-guard.sh +12 -0
  604. package/examples/shell-wrapper-guard.sh +12 -0
  605. package/examples/skill-cumulative-size-detector.sh +104 -0
  606. package/examples/skill-description-drop-detector.sh +148 -0
  607. package/examples/skill-gate.sh +0 -0
  608. package/examples/skill-truncation-detector.sh +154 -0
  609. package/examples/skills-context-recorder.sh +167 -0
  610. package/examples/skills-load-verifier.sh +96 -0
  611. package/examples/skills-settings-validator.sh +139 -0
  612. package/examples/sonnet-45-deprecation-helper.sh +85 -0
  613. package/examples/spec-file-scope-guard.sh +12 -0
  614. package/examples/spring-profile-guard.sh +1 -0
  615. package/examples/spurious-malformed-notice-detector.sh +247 -0
  616. package/examples/sql-bulk-delete-warn.sh +88 -0
  617. package/examples/sql-injection-detect.sh +0 -0
  618. package/examples/ssh-key-protect.sh +12 -0
  619. package/examples/staged-secret-scan.sh +22 -1
  620. package/examples/stale-temp-settings-detector.sh +86 -0
  621. package/examples/standing-rule-session-surfacer.sh +137 -0
  622. package/examples/stop-hook-sigterm-wrapper.sh +182 -0
  623. package/examples/stop-transcript-write-health.sh +64 -0
  624. package/examples/strict-allowlist.sh +57 -15
  625. package/examples/strip-coauthored-by.sh +50 -10
  626. package/examples/subagent-blast-radius-guard.sh +184 -0
  627. package/examples/subagent-boundary-precheck.sh +126 -0
  628. package/examples/subagent-budget-guard.sh +12 -0
  629. package/examples/subagent-claudemd-inject.sh +0 -0
  630. package/examples/subagent-closure-verify-gate.sh +164 -0
  631. package/examples/subagent-destructive-git-guard.sh +120 -0
  632. package/examples/subagent-error-detector.sh +0 -0
  633. package/examples/subagent-forged-system-reminder-guard.sh +156 -0
  634. package/examples/subagent-identity-leak-guard.sh +102 -0
  635. package/examples/subagent-identity-reassertion.sh +51 -0
  636. package/examples/subagent-inheritance-tester.sh +179 -0
  637. package/examples/subagent-permission-mode-guard.sh +110 -0
  638. package/examples/subagent-scope-guard.sh +12 -0
  639. package/examples/subagent-spawn-verification-enforcer.sh +102 -0
  640. package/examples/subagent-tool-allowlist-enforcer.sh +101 -0
  641. package/examples/subagent-tool-call-limiter.sh +87 -15
  642. package/examples/subcommand-chain-guard.sh +12 -0
  643. package/examples/subscription-api-billing-warner.sh +160 -0
  644. package/examples/subscription-bypass-detector.sh +152 -0
  645. package/examples/svelte-lint-on-edit.sh +1 -0
  646. package/examples/swift-build-on-edit.sh +1 -1
  647. package/examples/symlink-guard.sh +27 -2
  648. package/examples/symlink-protect.sh +1 -0
  649. package/examples/system-dir-protection-guard.sh +73 -8
  650. package/examples/system-message-workaround.sh +38 -13
  651. package/examples/system-package-guard.sh +12 -0
  652. package/examples/systemd-service-guard.sh +12 -0
  653. package/examples/task-integrity-guard.sh +12 -0
  654. package/examples/temp-file-cleanup.sh +1 -1
  655. package/examples/temporal-suggestion-detector.sh +135 -0
  656. package/examples/terraform-guard.sh +27 -6
  657. package/examples/test-after-edit.sh +1 -1
  658. package/examples/test-before-push.sh +12 -0
  659. package/examples/test-coverage-reminder.sh +24 -1
  660. package/examples/thinking-budget-effort-mismatch-detector.sh +149 -0
  661. package/examples/thinking-stall-detector.sh +61 -0
  662. package/examples/timezone-guard.sh +0 -0
  663. package/examples/tmp-output-size-guard.sh +0 -0
  664. package/examples/todo-check.sh +1 -1
  665. package/examples/token-budget-guard.sh +12 -0
  666. package/examples/token-budget-per-task.sh +12 -0
  667. package/examples/token-usage-tracker.sh +7 -1
  668. package/examples/tokenizer-ratio-alert-corpus.sh +90 -0
  669. package/examples/tokenizer-ratio-alert.sh +104 -0
  670. package/examples/tool-call-rate-limiter.sh +0 -0
  671. package/examples/tool-result-correlation-checker.sh +167 -0
  672. package/examples/tool-result-size-guard.sh +132 -0
  673. package/examples/tool-retry-budget-guard.sh +12 -0
  674. package/examples/transcript-contamination-detector.sh +138 -0
  675. package/examples/trustfall-mcp-injection-guard.sh +103 -0
  676. package/examples/ultrareview-large-diff-advisor.sh +184 -0
  677. package/examples/unbounded-output-guard.sh +94 -0
  678. package/examples/uncommitted-changes-stop.sh +1 -0
  679. package/examples/uncommitted-discard-guard.sh +23 -5
  680. package/examples/uncommitted-work-guard.sh +12 -0
  681. package/examples/usage-warn.sh +0 -0
  682. package/examples/user-account-guard.sh +12 -0
  683. package/examples/userprompt-submit-receipt.sh +123 -0
  684. package/examples/variable-expansion-guard.sh +12 -0
  685. package/examples/verify-after-publish-reminder.sh +93 -0
  686. package/examples/verify-before-commit.sh +12 -0
  687. package/examples/version-bump-detector.sh +156 -0
  688. package/examples/version-regression-warner.sh +144 -0
  689. package/examples/vue-lint-on-edit.sh +1 -0
  690. package/examples/warn-cron-cost-trap.sh +173 -0
  691. package/examples/webfetch-runaway-guard.sh +89 -0
  692. package/examples/wellness-break-reminder.sh +118 -0
  693. package/examples/windows-destructive-command-guard.sh +177 -0
  694. package/examples/windows-path-guard.sh +12 -0
  695. package/examples/windows-python-stub-detector.sh +63 -0
  696. package/examples/work-hours-guard.sh +12 -0
  697. package/examples/working-directory-fence.sh +12 -0
  698. package/examples/workspace-lease-guard.sh +205 -0
  699. package/examples/worktree-create-log.sh +1 -0
  700. package/examples/worktree-delete-guard.sh +12 -0
  701. package/examples/worktree-edit-boundary-guard.sh +127 -0
  702. package/examples/worktree-escape-write-guard.sh +114 -0
  703. package/examples/worktree-hook-linker.sh +0 -0
  704. package/examples/worktree-hooks-path-fix.sh +111 -0
  705. package/examples/worktree-parent-write-guard.sh +43 -0
  706. package/examples/worktree-project-unify.sh +2 -1
  707. package/examples/worktree-remove-uncommitted-guard.sh +13 -0
  708. package/examples/worktree-unmerged-guard.sh +7 -1
  709. package/examples/write-byte-integrity-verifier.sh +190 -0
  710. package/examples/write-empty-content-guard.sh +67 -0
  711. package/examples/write-nul-corruption-detector.sh +61 -0
  712. package/examples/write-overwrite-confirm.sh +160 -14
  713. package/examples/write-secret-guard.sh +27 -5
  714. package/examples/write-shrink-guard.sh +12 -0
  715. package/examples/write-test-ratio.sh +0 -0
  716. package/examples/wsl-host-disk-space-guard.sh +97 -0
  717. package/examples/xml-format-leak-detector.sh +266 -0
  718. package/examples/yaml-syntax-check.sh +12 -0
  719. package/hooks/hooks.json +6 -6
  720. package/index.mjs +1699 -183
  721. package/mission.md +2 -0
  722. package/package.json +4 -4
  723. package/plugins/credential-guard/.claude-plugin/plugin.json +9 -7
  724. package/plugins/git-protection/.claude-plugin/plugin.json +10 -8
  725. package/plugins/safety-essentials/.claude-plugin/plugin.json +9 -7
  726. package/plugins/token-guard/.claude-plugin/plugin.json +10 -8
  727. package/scripts/agents-md-sync-setup.sh +156 -0
  728. package/scripts/autonomy-blocked-summary.sh +148 -0
  729. package/scripts/build-search-index.py +43 -0
  730. package/scripts/build-sitemap.py +119 -0
  731. package/scripts/cc-doctor.sh +192 -0
  732. package/scripts/check-hook-event-names.py +127 -0
  733. package/scripts/check-misfire-instrument.sh +64 -0
  734. package/scripts/claim-verify-audit.sh +467 -0
  735. package/scripts/cowork-claudemd-helper.sh +136 -0
  736. package/scripts/june-15-deprecated-model-scan.sh +111 -0
  737. package/scripts/measure-heredoc-shapes.sh +67 -0
  738. package/scripts/measure-literal-match-misfire.sh +110 -0
  739. package/scripts/probe-scope-guard-branches.sh +48 -0
  740. package/scripts/probe-skip-line-both-ways.sh +65 -0
  741. package/scripts/receipts-aggregate.py +199 -0
  742. package/scripts/session-forensic-audit.py +360 -0
  743. package/scripts.json +9 -9
  744. package/scripts.json.bak-2026-07-27-find +13 -0
  745. package/skills/safety-setup/SKILL.md +5 -5
  746. package/tests/approve-side-remaining-two.test.sh +129 -0
  747. package/tests/approver-list-granularity.test.sh +145 -0
  748. package/tests/askuserquestion-autonomy-gate.test.sh +181 -0
  749. package/tests/audit-ci-gate.test.sh +87 -0
  750. package/tests/audit-cross-layer-claudemd.test.sh +170 -0
  751. package/tests/audit-fail-open-parse.test.sh +100 -0
  752. package/tests/auto-approve-compound-tail.test.sh +194 -0
  753. package/tests/auto-mode-path-invoked-commands.test.sh +89 -0
  754. package/tests/auto-mode-quoted-targets.test.sh +127 -0
  755. package/tests/auto-mode-unquoted-targets.test.sh +112 -0
  756. package/tests/bash-secret-output-detector-detects.test.sh +102 -0
  757. package/tests/blindspots-cross-layer.test.sh +177 -0
  758. package/tests/block-database-wipe.test.sh +64 -0
  759. package/tests/bundled-short-flags-bypass.test.sh +85 -0
  760. package/tests/cd-git-allow-compound-tail.test.sh +67 -0
  761. package/tests/chown-guard-filesystem-root.test.sh +64 -0
  762. package/tests/ci-workflow-guard-skip-rule.test.sh +115 -0
  763. package/tests/compound-deny-safe-rm-targets.test.sh +105 -0
  764. package/tests/core-scripts-parser-fallback.test.sh +140 -0
  765. package/tests/destructive-guard-bare-glob.test.sh +74 -0
  766. package/tests/destructive-guard-every-operand.test.sh +100 -0
  767. package/tests/destructive-guard-find-deletion.test.sh +114 -0
  768. package/tests/destructive-guard-flag-order.test.sh +106 -0
  769. package/tests/destructive-guard-mentions-vs-invocations.test.sh +107 -0
  770. package/tests/destructive-guard-quoted-target.test.sh +89 -0
  771. package/tests/destructive-guard-separators.test.sh +105 -0
  772. package/tests/destructive-guard-variable-target.test.sh +98 -0
  773. package/tests/destructive-guard-wrapped-command.test.sh +91 -0
  774. package/tests/doctor-hook-disabling-env.test.sh +92 -0
  775. package/tests/doctor-misspelled-settings-keys.test.sh +106 -0
  776. package/tests/example-guards-wrapped-command.test.sh +169 -0
  777. package/tests/file-edit-backup.test.sh +93 -0
  778. package/tests/git-filter-repo-guard.test.sh +60 -0
  779. package/tests/git-global-options-bypass.test.sh +141 -0
  780. package/tests/git-invocation-prefixes-all-guards.test.sh +114 -0
  781. package/tests/git-maintenance-guard.test.sh +83 -0
  782. package/tests/git-pack-temp-cleanup-guard.test.sh +133 -0
  783. package/tests/git-rm-orphan-wipe-guard.test.sh +66 -0
  784. package/tests/git-submodule-guard.test.sh +50 -0
  785. package/tests/grep-pcre-portability.test.sh +155 -0
  786. package/tests/hook-registration-matches-input.test.sh +73 -0
  787. package/tests/hook-trigger-matches-envelope.test.sh +178 -0
  788. package/tests/install-does-not-claim-protection.test.sh +110 -0
  789. package/tests/install-example-standalone-tools.test.sh +139 -0
  790. package/tests/line-continuation-bypass.test.sh +114 -0
  791. package/tests/mcp-config-poisoning-audit.test.sh +106 -0
  792. package/tests/nested-background-agent-guard.test.sh +58 -0
  793. package/tests/no-unqualified-safety-claims.test.sh +114 -0
  794. package/tests/option-eating-grep-patterns.test.sh +130 -0
  795. package/tests/outdated-core-guards.test.sh +134 -0
  796. package/tests/outdated-report.test.sh +155 -0
  797. package/tests/output-secret-mask-warns.test.sh +73 -0
  798. package/tests/plugin-hooks-fail-open-under-sh.test.sh +165 -0
  799. package/tests/plugins-word-order.test.sh +126 -0
  800. package/tests/powershell-remove-item-guard.test.sh +85 -0
  801. package/tests/project-dir-naming.test.sh +103 -0
  802. package/tests/quoted-safe-targets-and-mentions.test.sh +190 -0
  803. package/tests/reroute-after-block-guard.test.sh +104 -0
  804. package/tests/secret-guard-documented-promises.test.sh +97 -0
  805. package/tests/session-plugin-pin-watch.test.sh +45 -0
  806. package/tests/settings-unreadable-refuses-to-write.test.sh +95 -0
  807. package/tests/shield-no-silent-adoption.test.py +110 -0
  808. package/tests/shield-no-silent-adoption.test.sh +46 -0
  809. package/tests/stop-transcript-write-health.test.sh +46 -0
  810. package/tests/system-dir-guard-root-erased.test.sh +66 -0
  811. package/tests/test-account-billing-log.sh +100 -0
  812. package/tests/test-account-routing-preflight.sh +77 -0
  813. package/tests/test-agent-sdk-credit-pool-monitor.sh +157 -0
  814. package/tests/test-agent-view-quota-warn.sh +184 -0
  815. package/tests/test-agents-md-edit-drift-warner.sh +176 -0
  816. package/tests/test-agents-md-loader.sh +204 -0
  817. package/tests/test-agents-md-sync-checker.sh +125 -0
  818. package/tests/test-ai-slop-punctuation-arrest.sh +280 -0
  819. package/tests/test-allowlist.sh +60 -0
  820. package/tests/test-always-allow-pattern-suggester.sh +147 -0
  821. package/tests/test-anthropic-base-url-guard.sh +88 -0
  822. package/tests/test-articulated-scope-capture.sh +139 -0
  823. package/tests/test-attachment-traversal-guard.sh +168 -0
  824. package/tests/test-aup-block-pattern-logger.sh +315 -0
  825. package/tests/test-aup-false-positive-helper.sh +188 -0
  826. package/tests/test-aup-large-tool-output-warner.sh +370 -0
  827. package/tests/test-aup-retry-loop-guard.sh +341 -0
  828. package/tests/test-auth-expiry-reminder.sh +303 -0
  829. package/tests/test-auth-macos-sleep-detector.sh +254 -0
  830. package/tests/test-auth-path-detector.sh +232 -0
  831. package/tests/test-auth-status-checker.sh +233 -0
  832. package/tests/test-authorized-reconfirmation-detector.sh +280 -0
  833. package/tests/test-background-cost-launch-guard.sh +62 -0
  834. package/tests/test-bash-allowlist-secondary-check.sh +143 -0
  835. package/tests/test-bash-fanout-bounded-rewriter.sh +186 -0
  836. package/tests/test-bash-project-fence.sh +127 -0
  837. package/tests/test-bg-permission-prompt-warner.sh +221 -0
  838. package/tests/test-binary-read-detector.sh +173 -0
  839. package/tests/test-broad-prefix-session-trap-warner.sh +167 -0
  840. package/tests/test-bulk-delete-target-resolution.sh +49 -0
  841. package/tests/test-bypass-mode-effective-verifier.sh +179 -0
  842. package/tests/test-cache-creation-drift-detector.sh +244 -0
  843. package/tests/test-cache-residue-detector.sh +429 -0
  844. package/tests/test-cache-tier-logger.sh +59 -0
  845. package/tests/test-cache-ttl-eviction-detector.sh +282 -0
  846. package/tests/test-cch-sentinel-precommit-guard.sh +64 -0
  847. package/tests/test-claim-verify-detector.sh +222 -0
  848. package/tests/test-claim-vs-caveat-checker.sh +134 -0
  849. package/tests/test-claude-code-version-pin-advisor.sh +353 -0
  850. package/tests/test-claude-md-reinjector.sh +173 -0
  851. package/tests/test-claude-md-size-monitor.sh +107 -0
  852. package/tests/test-claude-update-budget-guard.sh +132 -0
  853. package/tests/test-claude-update-smart.sh +91 -0
  854. package/tests/test-claudemd-tool-prohibition.sh +239 -0
  855. package/tests/test-cli-config-pinning-detector.sh +233 -0
  856. package/tests/test-cliff-countdown-advisor.sh +138 -0
  857. package/tests/test-closure-word-verify-gate.sh +158 -0
  858. package/tests/test-commit-all-scope-guard.sh +73 -0
  859. package/tests/test-commitment-carry-forward-arrest.sh +250 -0
  860. package/tests/test-compact-circuit-breaker.sh +134 -0
  861. package/tests/test-compact-dispatch-watchdog.sh +147 -0
  862. package/tests/test-completion-claim-without-verification-detector.sh +326 -0
  863. package/tests/test-compound-bash-permission-resolver.sh +264 -0
  864. package/tests/test-compound-command-deny-enforcer.sh +67 -0
  865. package/tests/test-context-length-budget.sh +68 -0
  866. package/tests/test-context-usage-drift-alert.sh +52 -22
  867. package/tests/test-cost-incident-self-audit.sh +94 -0
  868. package/tests/test-cowork-claude-md-load-checker.sh +257 -0
  869. package/tests/test-cowork-claudemd-helper.sh +141 -0
  870. package/tests/test-cowork-fuse-staleness-watcher.sh +287 -0
  871. package/tests/test-cowork-hook-absence-warner.sh +230 -0
  872. package/tests/test-cowork-model-picker-advisor.sh +317 -0
  873. package/tests/test-credential-exfil-guard.sh +62 -0
  874. package/tests/test-cron-create-receipt.sh +165 -0
  875. package/tests/test-cross-product-mcp-spawn-detector.sh +112 -0
  876. package/tests/test-daemon-zombie-worker-surfacer.sh +71 -0
  877. package/tests/test-daily-cost-guard.sh +131 -0
  878. package/tests/test-deny-rule-integrity-verifier.sh +288 -0
  879. package/tests/test-deployment-readback-gate.sh +92 -0
  880. package/tests/test-deployment-readback-gh-adapter.sh +163 -0
  881. package/tests/test-disabled-feature-toggle-advisor.sh +78 -0
  882. package/tests/test-dispatch-allowlist-preflight.sh +385 -0
  883. package/tests/test-dispatch-liveness-watchdog.sh +319 -0
  884. package/tests/test-dispatch-receipt.sh +314 -0
  885. package/tests/test-dotenv-anthropic-key-billing-guard.sh +160 -0
  886. package/tests/test-echo-probe-spam-detector.sh +143 -0
  887. package/tests/test-edit-quote-ambiguity-guard.sh +75 -0
  888. package/tests/test-ephemeral-container-detector.sh +251 -0
  889. package/tests/test-evidence-claim-gate.sh +291 -0
  890. package/tests/test-exploration-budget-guard.sh +164 -0
  891. package/tests/test-extended-thinking-loop-guard.sh +289 -0
  892. package/tests/test-extended-thinking-resume-warning.sh +240 -0
  893. package/tests/test-extended-thinking-tool-use-mismatch-detector.sh +236 -0
  894. package/tests/test-fabricated-command-detector.sh +296 -0
  895. package/tests/test-false-disk-full-guard.sh +36 -0
  896. package/tests/test-feature-deprecation-detector.sh +117 -0
  897. package/tests/test-forbidden-response-phrase-detector.sh +246 -0
  898. package/tests/test-git-operations-require-approval.sh +53 -0
  899. package/tests/test-git-push-blast-radius-guard.sh +115 -0
  900. package/tests/test-goal-iteration-limit-warner.sh +128 -0
  901. package/tests/test-growthbook-flag-monitor.sh +428 -0
  902. package/tests/test-halt-signal-detector.sh +120 -0
  903. package/tests/test-hook-self-disable-detector.sh +232 -0
  904. package/tests/test-iteration-quality-degradation-detector.sh +244 -0
  905. package/tests/test-long-session-malformed-tool-call-detector.sh +185 -0
  906. package/tests/test-marketplace-plugins.sh +193 -0
  907. package/tests/test-mcp-misdiagnosis-arrest.sh +235 -0
  908. package/tests/test-mcp-orphan-leak-warner.sh +71 -0
  909. package/tests/test-mcp-permissions-ask-warner.sh +207 -0
  910. package/tests/test-mcp-routine-approval-detector.sh +229 -0
  911. package/tests/test-mcp-startup-bloat-detector.sh +141 -0
  912. package/tests/test-mcp-stdio-compatibility-test.sh +112 -0
  913. package/tests/test-memory-chain-audit.sh +59 -0
  914. package/tests/test-memory-orphan-detector.sh +227 -0
  915. package/tests/test-model-swap-suggester.sh +368 -0
  916. package/tests/test-model-version-lock.sh +83 -0
  917. package/tests/test-multi-file-plan-routing-gate.sh +277 -0
  918. package/tests/test-multi-window-auth-drift-detector.sh +262 -0
  919. package/tests/test-nested-spawn-inflight-guard.sh +281 -0
  920. package/tests/test-no-force-flag.sh +49 -0
  921. package/tests/test-non-english-quality-warner.sh +229 -0
  922. package/tests/test-oauth-refresh-monitor.sh +311 -0
  923. package/tests/test-opus-degradation-tracker.sh +274 -0
  924. package/tests/test-opus48-routine-task-warning.sh +201 -0
  925. package/tests/test-opus48-thinking-wedge-advisor.sh +249 -0
  926. package/tests/test-output-token-spike-detector.sh +315 -0
  927. package/tests/test-parallel-batch-size-limiter.sh +228 -0
  928. package/tests/test-parallel-cascade-detector.sh +191 -0
  929. package/tests/test-partial-view-claim-arrest.sh +272 -0
  930. package/tests/test-per-prompt-token-cap.sh +136 -0
  931. package/tests/test-persisted-1m-model-advisor.sh +71 -0
  932. package/tests/test-plugin-hooks-json-bloat-detector.sh +267 -0
  933. package/tests/test-post-edit-disk-verify.sh +294 -0
  934. package/tests/test-pre-bash-sed-line-ending-windows.sh +108 -0
  935. package/tests/test-pre-execution-claim-detector.sh +366 -0
  936. package/tests/test-private-key-backup-and-git-credentials.sh +100 -0
  937. package/tests/test-proxy-capture-suggester.sh +214 -0
  938. package/tests/test-public-artefact-socratic-narrowing.sh +346 -0
  939. package/tests/test-quota-anomaly-detector.sh +321 -0
  940. package/tests/test-quota-reset-audit.sh +81 -0
  941. package/tests/test-read-loop-detector.sh +137 -0
  942. package/tests/test-read-once-guard.sh +85 -0
  943. package/tests/test-receipts-aggregate.sh +143 -0
  944. package/tests/test-redirect-fragment-warner.sh +130 -0
  945. package/tests/test-redundant-read-blocker.sh +155 -0
  946. package/tests/test-refusal-arrest-gate.sh +303 -0
  947. package/tests/test-remote-control-billing-classifier.sh +125 -0
  948. package/tests/test-resume-drift-watcher.sh +65 -0
  949. package/tests/test-rg-replace-flag-detector.sh +175 -0
  950. package/tests/test-rhetorical-verification-prompt-detector.sh +237 -0
  951. package/tests/test-rm-safety-net.sh +115 -0
  952. package/tests/test-route-handler-emptiness-gate.sh +300 -0
  953. package/tests/test-runtime-binary-change-detector.sh +196 -0
  954. package/tests/test-same-command-repeat-detector.sh +165 -0
  955. package/tests/test-same-correction-arrest.sh +165 -0
  956. package/tests/test-scope-expansion-receipt.sh +207 -0
  957. package/tests/test-server-side-prompt-injection-detector.sh +181 -0
  958. package/tests/test-session-persistence-verifier.sh +128 -0
  959. package/tests/test-session-rate-monitor.sh +284 -0
  960. package/tests/test-session-start-quota-status.sh +210 -0
  961. package/tests/test-session-start-safety-check.sh +138 -0
  962. package/tests/test-settings-hooks-key-restorer.sh +257 -0
  963. package/tests/test-settings-json-health-check.sh +70 -0
  964. package/tests/test-settings-regression-tester.sh +197 -0
  965. package/tests/test-skill-cumulative-size-detector.sh +198 -0
  966. package/tests/test-skill-description-drop-detector.sh +236 -0
  967. package/tests/test-skill-truncation-detector.sh +263 -0
  968. package/tests/test-skills-context-recorder.sh +329 -0
  969. package/tests/test-skills-load-verifier.sh +184 -0
  970. package/tests/test-skills-settings-validator.sh +242 -0
  971. package/tests/test-sonnet-45-deprecation-helper.sh +150 -0
  972. package/tests/test-spurious-malformed-notice-detector.sh +290 -0
  973. package/tests/test-sql-bulk-delete-warn.sh +123 -0
  974. package/tests/test-ssh-key-protect.sh +81 -0
  975. package/tests/test-stale-temp-settings-detector.sh +161 -0
  976. package/tests/test-standing-rule-session-surfacer.sh +215 -0
  977. package/tests/test-starter-hooks-manifest.sh +96 -0
  978. package/tests/test-stop-hook-sigterm-wrapper.sh +258 -0
  979. package/tests/test-strict-allowlist.sh +72 -0
  980. package/tests/test-subagent-boundary-precheck.sh +137 -0
  981. package/tests/test-subagent-closure-verify-gate.sh +193 -0
  982. package/tests/test-subagent-destructive-git-guard.sh +86 -0
  983. package/tests/test-subagent-identity-leak-guard.sh +62 -0
  984. package/tests/test-subagent-identity-reassertion.sh +61 -0
  985. package/tests/test-subagent-inheritance-tester.sh +319 -0
  986. package/tests/test-subagent-permission-mode-guard.sh +61 -0
  987. package/tests/test-subagent-spawn-verification-enforcer.sh +60 -0
  988. package/tests/test-subagent-tool-allowlist-enforcer.sh +62 -0
  989. package/tests/test-subagent-tool-call-limiter.sh +97 -0
  990. package/tests/test-subscription-api-billing-warner.sh +138 -0
  991. package/tests/test-subscription-bypass-detector.sh +255 -0
  992. package/tests/test-temporal-suggestion-detector.sh +183 -0
  993. package/tests/test-thinking-budget-effort-mismatch-detector.sh +327 -0
  994. package/tests/test-thinking-stall-detector.sh +151 -0
  995. package/tests/test-tokenizer-ratio-alert-corpus.sh +77 -0
  996. package/tests/test-tokenizer-ratio-alert.sh +93 -0
  997. package/tests/test-tool-result-correlation-checker.sh +264 -0
  998. package/tests/test-tool-result-size-guard.sh +121 -0
  999. package/tests/test-transcript-contamination-detector.sh +264 -0
  1000. package/tests/test-trigger-header-parsing.sh +151 -0
  1001. package/tests/test-ultrareview-large-diff-advisor.sh +192 -0
  1002. package/tests/test-userprompt-submit-receipt.sh +181 -0
  1003. package/tests/test-verify-after-publish-reminder.sh +184 -0
  1004. package/tests/test-version-bump-detector.sh +212 -0
  1005. package/tests/test-version-regression-warner.sh +165 -0
  1006. package/tests/test-warn-cron-cost-trap.sh +82 -0
  1007. package/tests/test-webfetch-runaway-guard.sh +77 -0
  1008. package/tests/test-wellness-break-reminder.sh +138 -0
  1009. package/tests/test-windows-destructive-command-guard.sh +169 -0
  1010. package/tests/test-windows-python-stub-detector.sh +146 -0
  1011. package/tests/test-worktree-edit-boundary-guard.sh +219 -0
  1012. package/tests/test-write-byte-integrity-verifier.sh +192 -0
  1013. package/tests/test-xml-format-leak-detector.sh +299 -0
  1014. package/tests/trustfall-mcp-injection-guard.test.sh +183 -0
  1015. package/tests/workspace-lease-guard.test.sh +144 -0
  1016. package/tests/worktree-escape-write-guard.test.sh +68 -0
  1017. package/tests/worktree-hooks-path-fix.test.sh +181 -0
  1018. package/tests/worktree-parent-write-guard.test.sh +51 -0
  1019. package/tests/write-empty-content-guard.test.sh +39 -0
  1020. package/tests/write-nul-corruption-detector.test.sh +29 -0
  1021. package/tests/write-overwrite-unread-content.test.sh +178 -0
  1022. package/tests/wsl-host-disk-space-guard.test.sh +55 -0
@@ -0,0 +1,141 @@
1
+ #!/bin/bash
2
+ # The three git guards never saw `git -C <path> <verb>`
3
+ #
4
+ # git accepts its own options between the program name and the verb:
5
+ #
6
+ # git -C /repo push --force
7
+ # git -c core.pager=cat reset --hard
8
+ # git --git-dir=/repo/.git clean -fd
9
+ #
10
+ # All three do exactly what the unprefixed form does. Every git pattern in
11
+ # branch-guard, secret-guard and destructive-guard expected the verb to sit
12
+ # directly after the word `git`, so all three hooks exited 0 -- which is an
13
+ # affirmative approval, not a shrug.
14
+ #
15
+ # Measured 2026-09-03 against the shipped copies: 10 bypasses.
16
+ # branch-guard git -C push to a protected branch, git -C force push,
17
+ # --git-dir force push, and `git push origin +branch`
18
+ # (a force push with no --force anywhere in it)
19
+ # secret-guard git -C add .env, git -C add id_rsa
20
+ # destructive-guard git -C reset --hard, git -C clean -fd,
21
+ # git -C checkout --force, --git-dir reset --hard
22
+ #
23
+ # destructive-guard needed two fixes, not one. The checks themselves were only
24
+ # half of it: the mentions-vs-invocations pre-filter counted git verbs in the
25
+ # raw text, scored `git -C /repo reset --hard` as zero verbs, and exited before
26
+ # any check ran. Fixing the checks alone changed nothing measurable. Same shape,
27
+ # one layer earlier -- which is where it decided the outcome.
28
+ #
29
+ # The fix normalises the globals away once (cc_strip_git_globals) and leaves the
30
+ # patterns readable, rather than threading an optional-globals group through
31
+ # every one of them.
32
+ ROOT="$(cd "$(dirname "$0")/.." && pwd)"
33
+ BG=$(mktemp); SG=$(mktemp); DG=$(mktemp)
34
+ trap 'rm -f "$BG" "$SG" "$DG"' EXIT
35
+ python3 -c "
36
+ import json,sys
37
+ d=json.load(open('$ROOT/scripts.json'))
38
+ open('$BG','w').write(d['branch-guard'])
39
+ open('$SG','w').write(d['secret-guard'])
40
+ open('$DG','w').write(d['destructive-guard'])"
41
+
42
+ PASS=0; FAIL=0
43
+
44
+ check() {
45
+ local guard="$1" desc="$2" cmd="$3" want="$4" # want: 2 = blocked, 0 = allowed
46
+ local payload code
47
+ payload=$(python3 -c '
48
+ import json,sys
49
+ print(json.dumps({"tool_name":"Bash","tool_input":{"command":sys.argv[1]}}))' "$cmd")
50
+ printf '%s' "$payload" | bash "$guard" >/dev/null 2>&1
51
+ code=$?
52
+ if [ "$code" = "$want" ]; then
53
+ echo "PASS: $desc"; PASS=$((PASS+1))
54
+ else
55
+ echo "FAIL: $desc (expected exit $want, got $code)"; FAIL=$((FAIL+1))
56
+ fi
57
+ }
58
+
59
+ # --- branch-guard: globals before `push` ---
60
+ check "$BG" "bg: -C, protected branch" 'git -C /repo push origin main' 2
61
+ check "$BG" "bg: -C, force push" 'git -C /repo push origin feature --force' 2
62
+ check "$BG" "bg: --git-dir, force push" 'git --git-dir=/r/.git push --force' 2
63
+ check "$BG" "bg: -c, force push" 'git -c core.pager=cat push --force' 2
64
+ check "$BG" "bg: --no-pager then -C" 'git --no-pager -C /r push --force' 2
65
+ check "$BG" "bg: canonical force push" 'git push --force origin feature' 2
66
+ check "$BG" "bg: canonical protected push" 'git push origin main' 2
67
+
68
+ # --- branch-guard: `+branch` is a force push with no --force in it ---
69
+ check "$BG" "bg: +refspec, feature branch" 'git push origin +feature:feature' 2
70
+ check "$BG" "bg: +refspec, no colon" 'git push origin +feature' 2
71
+ check "$BG" "bg: -C plus +refspec" 'git -C /repo push origin +feature' 2
72
+
73
+ # --- branch-guard: still lets ordinary work through ---
74
+ check "$BG" "bg: ordinary push" 'git push origin feature' 0
75
+ check "$BG" "bg: -C ordinary push" 'git -C /repo push origin feature' 0
76
+ check "$BG" "bg: dry run" 'git push --dry-run origin feature' 0
77
+ check "$BG" "bg: rm -f after the push is not a force push" \
78
+ 'git push origin feature && rm -f tmp.txt' 0
79
+
80
+ # --- secret-guard: globals before `add` ---
81
+ check "$SG" "sg: -C, .env" 'git -C /repo add .env' 2
82
+ check "$SG" "sg: -C, private key" 'git -C /repo add id_rsa' 2
83
+ check "$SG" "sg: --git-dir, .env" 'git --git-dir=/r/.git add .env' 2
84
+ check "$SG" "sg: canonical .env" 'git add .env' 2
85
+ check "$SG" "sg: ordinary add" 'git add src/main.py' 0
86
+ check "$SG" "sg: -C ordinary add" 'git -C /repo add src/main.py' 0
87
+
88
+ # --- destructive-guard: globals before the verb ---
89
+ check "$DG" "dg: -C, reset --hard" 'git -C /repo reset --hard HEAD~5' 2
90
+ check "$DG" "dg: --git-dir, reset --hard" 'git --git-dir=/r/.git reset --hard' 2
91
+ check "$DG" "dg: -c, reset --hard" 'git -c user.name=x reset --hard' 2
92
+ check "$DG" "dg: -C, clean -fd" 'git -C /repo clean -fd' 2
93
+ check "$DG" "dg: -C, checkout --force" 'git -C /repo checkout --force main' 2
94
+ check "$DG" "dg: canonical reset --hard" 'git reset --hard HEAD~1' 2
95
+ check "$DG" "dg: canonical clean" 'git clean -fd' 2
96
+
97
+ # --- destructive-guard: the pre-filter still tells mentions from invocations ---
98
+ check "$DG" "dg: reset --hard inside a quoted string" \
99
+ "echo 'git reset --hard は危険'" 0
100
+ check "$DG" "dg: -C read-only" 'git -C /repo log' 0
101
+ check "$DG" "dg: -C dry-run clean" 'git -C /repo clean --dry-run' 0
102
+ check "$DG" "dg: reset --soft" 'git reset --soft HEAD~1' 0
103
+ check "$DG" "dg: status" 'git status' 0
104
+
105
+ # --- 2026-09-14: the same family, found by applying an outside report (#1117) ---
106
+ # The normaliser only knew the `=` spelling of the long options, and its list
107
+ # was missing several that git accepts. git takes `--git-dir <path>` with a
108
+ # space just as happily as `--git-dir=<path>`.
109
+ check "$DG" "dg: --git-dir with a separate value" 'git --git-dir /tmp/r/repo.d reset --hard' 2
110
+ check "$DG" "dg: --config-env" 'git --config-env=a=B reset --hard' 2
111
+ check "$DG" "dg: --no-optional-locks" 'git --no-optional-locks reset --hard' 2
112
+ check "$DG" "dg: --work-tree with a separate value" 'git --work-tree /tmp/w reset --hard' 2
113
+ check "$DG" "dg: --no-advice" 'git --no-advice clean -fd' 2
114
+ # and the same options must not start blocking ordinary work
115
+ check "$DG" "dg: --no-optional-locks status" 'git --no-optional-locks status' 0
116
+ check "$DG" "dg: --git-dir separate value, log" 'git --git-dir /tmp/r/repo.d log --oneline' 0
117
+
118
+ # --- 2026-09-14, second pass: `git` did not have to be the first word ---
119
+ # The git checks anchored on `(^|;|&+|\|\|)\s*git`, so anything in front of the
120
+ # word - an absolute path, a wrapper, an environment assignment - put the
121
+ # command out of reach. Check 0y, the pre-filter that decides whether a
122
+ # destructive verb is present at all, already counted every one of these as an
123
+ # invocation; only the checks it feeds were narrower. Both now share CC_CMDPOS.
124
+ check "$DG" "dg: git by absolute path" '/usr/bin/git reset --hard' 2
125
+ check "$DG" "dg: git by relative path" './git reset --hard' 2
126
+ check "$DG" "dg: sudo git" 'sudo git reset --hard' 2
127
+ check "$DG" "dg: env assignment in front" 'env FOO=1 git reset --hard' 2
128
+ check "$DG" "dg: bare assignment in front" 'FOO=1 git reset --hard' 2
129
+ check "$DG" "dg: time wrapper" 'time git reset --hard' 2
130
+ check "$DG" "dg: nohup wrapper" 'nohup git reset --hard' 2
131
+ check "$DG" "dg: absolute path, clean -fd" '/usr/bin/git clean -fd' 2
132
+ check "$DG" "dg: absolute path, checkout -f" '/usr/bin/git checkout --force main' 2
133
+ # The widened head must not start blocking the things it never blocked before.
134
+ check "$DG" "dg: mentions it in an echo" "echo 'git reset --hard' >> notes.md" 0
135
+ check "$DG" "dg: greps for the words" "grep -r 'git reset --hard' ./docs" 0
136
+ check "$DG" "dg: reset --soft" 'git reset --soft HEAD~1' 0
137
+ check "$DG" "dg: a path that merely ends in git" 'ls /usr/bin/git' 0
138
+
139
+ echo
140
+ echo "PASS: $PASS FAIL: $FAIL"
141
+ [ "$FAIL" = 0 ]
@@ -0,0 +1,114 @@
1
+ #!/bin/bash
2
+ # `git` did not have to be the first word -- in branch-guard and secret-guard too
3
+ #
4
+ # 2026-09-14, #1121 moved destructive-guard's git checks off the "start of a
5
+ # segment" anchor and onto CC_CMDPOS, which also recognises a path, a sudo, an
6
+ # environment assignment and the usual wrappers. branch-guard and secret-guard
7
+ # have the same gate and were not touched, so the fix covered one guard out of
8
+ # three.
9
+ #
10
+ # Measured the same day, nine invocation forms against each subcommand, feeding
11
+ # each guard the same JSON on stdin:
12
+ #
13
+ # destructive-guard 9 of 9 blocked (fixed in #1121)
14
+ # branch-guard 2 of 9 blocked
15
+ # secret-guard 2 of 9 blocked
16
+ #
17
+ # The seven that walked through were `/usr/bin/git`, `./git`, `sudo git`,
18
+ # `env FOO=1 git`, `FOO=1 git`, `time git` and `nohup git`. Each one exited 0,
19
+ # which Claude Code reads as "this hook has no objection", not as "this hook did
20
+ # not look".
21
+ #
22
+ # Also here: `git send-pack --force origin main`. send-pack is the plumbing
23
+ # spelling of a push, and branch-guard's own header says it blocks force pushes
24
+ # on all branches and pushes to protected branches -- but its gate only knew the
25
+ # word `push`, so both of those promises were spelled around.
26
+ ROOT="$(cd "$(dirname "$0")/.." && pwd)"
27
+ BG=$(mktemp); SG=$(mktemp); DG=$(mktemp)
28
+ trap 'rm -f "$BG" "$SG" "$DG"' EXIT
29
+ python3 -c "
30
+ import json,sys
31
+ d=json.load(open('$ROOT/scripts.json'))
32
+ open('$BG','w').write(d['branch-guard'])
33
+ open('$SG','w').write(d['secret-guard'])
34
+ open('$DG','w').write(d['destructive-guard'])"
35
+
36
+ PASS=0; FAIL=0
37
+
38
+ check() {
39
+ local guard="$1" desc="$2" cmd="$3" want="$4" # want: 2 = blocked, 0 = allowed
40
+ local payload code
41
+ payload=$(python3 -c '
42
+ import json,sys
43
+ print(json.dumps({"tool_name":"Bash","tool_input":{"command":sys.argv[1]}}))' "$cmd")
44
+ printf '%s' "$payload" | bash "$guard" >/dev/null 2>&1
45
+ code=$?
46
+ if [ "$code" = "$want" ]; then
47
+ echo "PASS: $desc"; PASS=$((PASS+1))
48
+ else
49
+ echo "FAIL: $desc (expected exit $want, got $code)"; FAIL=$((FAIL+1))
50
+ fi
51
+ }
52
+
53
+ # The nine invocation forms. `%s` is where the git command goes.
54
+ PREFIXES=(
55
+ '%s'
56
+ '/usr/bin/%s'
57
+ './%s'
58
+ 'sudo %s'
59
+ 'env FOO=1 %s'
60
+ 'FOO=1 %s'
61
+ 'time %s'
62
+ 'nohup %s'
63
+ 'cd /tmp && %s'
64
+ )
65
+ PREFIX_NAMES=(bare abspath relpath sudo env-assign leading-assign time nohup separator)
66
+
67
+ # Each guard with a subcommand it owns. All nine forms must be refused.
68
+ run_grid() {
69
+ local guard="$1" label="$2" sub="$3"
70
+ local i fmt name cmd
71
+ for i in "${!PREFIXES[@]}"; do
72
+ fmt="${PREFIXES[$i]}"; name="${PREFIX_NAMES[$i]}"
73
+ # shellcheck disable=SC2059
74
+ cmd=$(printf "$fmt" "$sub")
75
+ check "$guard" "$label [$name]" "$cmd" 2
76
+ done
77
+ }
78
+
79
+ run_grid "$DG" "dg: reset --hard" 'git reset --hard HEAD~5'
80
+ run_grid "$DG" "dg: clean -fd" 'git clean -fd'
81
+ run_grid "$BG" "bg: force push" 'git push --force origin main'
82
+ run_grid "$BG" "bg: +main is a force" 'git push origin +main'
83
+ run_grid "$BG" "bg: -uf short cluster" 'git push -uf origin feature'
84
+ run_grid "$SG" "sg: stage .env" 'git add .env'
85
+
86
+ # --- send-pack: the plumbing spelling of a push ---
87
+ check "$BG" "bg: send-pack --force" 'git send-pack --force origin main' 2
88
+ check "$BG" "bg: send-pack --force with -C" 'git -C /repo send-pack --force origin main' 2
89
+ check "$BG" "bg: send-pack to main" 'git send-pack origin main' 2
90
+ check "$BG" "bg: send-pack to a feature" 'git send-pack origin feature' 0
91
+
92
+ # --- the approve side: widening the gate must not start refusing ordinary work ---
93
+ check "$BG" "bg: ordinary push to a feature" 'git push origin feature' 0
94
+ check "$BG" "bg: -u to a feature" 'git push -u origin feature' 0
95
+ check "$SG" "sg: staging a normal file" 'git add README.md' 0
96
+ check "$SG" "sg: staging a source file" 'git add src/config.ts' 0
97
+ check "$DG" "dg: a dry run is not a delete" 'git clean -nd' 0
98
+
99
+ # CC_CMDPOS accepts a path in front of git. It must still require the word to be
100
+ # `git` and not merely to start with it.
101
+ check "$BG" "bg: gitk is not git" '/usr/bin/gitk push --force origin main' 0
102
+ check "$DG" "dg: a lookalike binary" './gitfoo reset --hard HEAD~5' 0
103
+
104
+ # Mentions are not invocations. A space in front of `git` is not a segment break,
105
+ # so quoting or grepping for the command must stay allowed.
106
+ check "$BG" "bg: the command inside a string" 'echo "run git push --force if you must"' 0
107
+ check "$DG" "dg: searching for the command" "grep -rn 'git reset --hard' ." 0
108
+
109
+ # ...but the wrappers that really do run it are invocations.
110
+ check "$BG" "bg: xargs runs it" 'echo main | xargs git push --force origin' 2
111
+
112
+ echo
113
+ echo "PASS: $PASS FAIL: $FAIL"
114
+ [ "$FAIL" -eq 0 ]
@@ -0,0 +1,83 @@
1
+ #!/bin/bash
2
+ # Tests for git-maintenance-guard.sh
3
+ # Run: bash tests/git-maintenance-guard.test.sh
4
+ set -euo pipefail
5
+
6
+ PASS=0
7
+ FAIL=0
8
+ HOOK="$(dirname "$0")/../examples/git-maintenance-guard.sh"
9
+
10
+ run_hook() {
11
+ local input="$1" expected_exit="$2" desc="$3" extra_env="${4:-}"
12
+ local actual_exit=0
13
+ if [ -n "$extra_env" ]; then
14
+ # shellcheck disable=SC2086
15
+ echo "$input" | env $extra_env bash "$HOOK" >/dev/null 2>/dev/null || actual_exit=$?
16
+ else
17
+ echo "$input" | bash "$HOOK" >/dev/null 2>/dev/null || actual_exit=$?
18
+ fi
19
+ if [ "$actual_exit" -eq "$expected_exit" ]; then
20
+ echo " PASS: $desc"
21
+ PASS=$((PASS + 1))
22
+ else
23
+ echo " FAIL: $desc (expected exit $expected_exit, got $actual_exit)"
24
+ FAIL=$((FAIL + 1))
25
+ fi
26
+ }
27
+
28
+ echo "git-maintenance-guard.sh tests"
29
+ echo ""
30
+
31
+ # --- Block git gc ---
32
+ run_hook '{"tool_input":{"command":"git gc"}}' 2 "Block bare git gc"
33
+ run_hook '{"tool_input":{"command":"git gc --auto"}}' 2 "Block git gc --auto"
34
+ run_hook '{"tool_input":{"command":"git gc --aggressive"}}' 2 "Block git gc --aggressive"
35
+ run_hook '{"tool_input":{"command":"git gc --prune=now"}}' 2 "Block git gc --prune=now"
36
+ run_hook '{"tool_input":{"command":"cd ~/repo && git gc"}}' 2 "Block git gc after cd"
37
+
38
+ # --- Block git repack ---
39
+ run_hook '{"tool_input":{"command":"git repack"}}' 2 "Block bare git repack"
40
+ run_hook '{"tool_input":{"command":"git repack -a -d"}}' 2 "Block git repack -a -d"
41
+ run_hook '{"tool_input":{"command":"git repack -A -d --depth=250 --window=250"}}' 2 "Block heavy git repack"
42
+
43
+ # --- Block git maintenance ---
44
+ run_hook '{"tool_input":{"command":"git maintenance run"}}' 2 "Block git maintenance run"
45
+ run_hook '{"tool_input":{"command":"git maintenance start"}}' 2 "Block git maintenance start"
46
+ run_hook '{"tool_input":{"command":"git maintenance stop"}}' 2 "Block git maintenance stop"
47
+ run_hook '{"tool_input":{"command":"git maintenance register"}}' 2 "Block git maintenance register"
48
+
49
+ # --- Block git prune ---
50
+ run_hook '{"tool_input":{"command":"git prune"}}' 2 "Block bare git prune"
51
+ run_hook '{"tool_input":{"command":"git prune --expire=now"}}' 2 "Block git prune --expire=now"
52
+ run_hook '{"tool_input":{"command":"git prune-packed"}}' 2 "Block git prune-packed"
53
+
54
+ # --- Allow safe git commands ---
55
+ run_hook '{"tool_input":{"command":"git status"}}' 0 "Allow git status"
56
+ run_hook '{"tool_input":{"command":"git log --oneline"}}' 0 "Allow git log"
57
+ run_hook '{"tool_input":{"command":"git commit -m fix"}}' 0 "Allow git commit"
58
+ run_hook '{"tool_input":{"command":"git push"}}' 0 "Allow git push"
59
+ run_hook '{"tool_input":{"command":"git fetch"}}' 0 "Allow git fetch"
60
+ run_hook '{"tool_input":{"command":"git pull"}}' 0 "Allow git pull"
61
+ run_hook '{"tool_input":{"command":"git checkout main"}}' 0 "Allow git checkout"
62
+ run_hook '{"tool_input":{"command":"git rebase main"}}' 0 "Allow git rebase"
63
+ run_hook '{"tool_input":{"command":"git count-objects -v"}}' 0 "Allow git count-objects"
64
+ run_hook '{"tool_input":{"command":"git fsck"}}' 0 "Allow git fsck"
65
+ run_hook '{"tool_input":{"command":"git verify-pack -v test.idx"}}' 0 "Allow git verify-pack"
66
+
67
+ # --- Allow unrelated commands containing the words ---
68
+ run_hook '{"tool_input":{"command":"echo gc"}}' 0 "Allow non-git gc"
69
+ run_hook '{"tool_input":{"command":"echo repack"}}' 0 "Allow non-git repack"
70
+ run_hook '{"tool_input":{"command":"echo maintenance"}}' 0 "Allow non-git maintenance"
71
+ run_hook '{"tool_input":{"command":"prune-old-files.sh"}}' 0 "Allow non-git prune"
72
+
73
+ # --- Override flags ---
74
+ run_hook '{"tool_input":{"command":"git gc"}}' 0 "ALLOW flag bypasses block" "CC_GIT_MAINTENANCE_ALLOW=1"
75
+ run_hook '{"tool_input":{"command":"git repack"}}' 0 "DISABLE flag bypasses block" "CC_GIT_MAINTENANCE_DISABLE=1"
76
+
77
+ # --- Empty / missing input ---
78
+ run_hook '{}' 0 "Allow empty input"
79
+ run_hook '{"tool_input":{}}' 0 "Allow missing command"
80
+
81
+ echo ""
82
+ echo "Results: $PASS passed, $FAIL failed out of $((PASS + FAIL)) tests"
83
+ [ "$FAIL" -eq 0 ] && exit 0 || exit 1
@@ -0,0 +1,133 @@
1
+ #!/bin/bash
2
+ # Tests for git-pack-temp-cleanup-guard.sh
3
+ # Run: bash tests/git-pack-temp-cleanup-guard.test.sh
4
+ set -euo pipefail
5
+
6
+ PASS=0
7
+ FAIL=0
8
+ HOOK="$(cd "$(dirname "$0")" && pwd)/../examples/git-pack-temp-cleanup-guard.sh"
9
+
10
+ run_hook() {
11
+ local input="$1" expected_exit="$2" desc="$3" extra_env="${4:-}"
12
+ local actual_exit=0
13
+ local stderr_out
14
+ if [ -n "$extra_env" ]; then
15
+ # shellcheck disable=SC2086
16
+ stderr_out=$(echo "$input" | env $extra_env bash "$HOOK" 2>&1 >/dev/null) || actual_exit=$?
17
+ else
18
+ stderr_out=$(echo "$input" | bash "$HOOK" 2>&1 >/dev/null) || actual_exit=$?
19
+ fi
20
+ if [ "$actual_exit" -eq "$expected_exit" ]; then
21
+ echo " PASS: $desc"
22
+ PASS=$((PASS + 1))
23
+ else
24
+ echo " FAIL: $desc (expected exit $expected_exit, got $actual_exit)"
25
+ echo " stderr: $stderr_out"
26
+ FAIL=$((FAIL + 1))
27
+ fi
28
+ }
29
+
30
+ # Each test sets up an isolated git repo in a temp dir so we never
31
+ # touch the user's real .git directory.
32
+ setup_repo() {
33
+ local dir
34
+ dir=$(mktemp -d)
35
+ (cd "$dir" && git init -q)
36
+ echo "$dir"
37
+ }
38
+
39
+ teardown_repo() {
40
+ rm -rf "$1"
41
+ }
42
+
43
+ echo "git-pack-temp-cleanup-guard.sh tests"
44
+ echo ""
45
+
46
+ # --- No-op cases ---
47
+ run_hook '{}' 0 "Allow empty input"
48
+ run_hook '{"tool_input":{}}' 0 "Allow missing command"
49
+ run_hook '{"tool_input":{"command":"echo hello"}}' 0 "Allow non-git command"
50
+ run_hook '{"tool_input":{"command":"ls -la"}}' 0 "Allow ls command"
51
+
52
+ # --- Git commands outside a repo ---
53
+ TMP=$(mktemp -d)
54
+ pushd "$TMP" >/dev/null
55
+ run_hook '{"tool_input":{"command":"git status"}}' 0 "Allow git command outside a repo (no .git found)"
56
+ popd >/dev/null
57
+ rm -rf "$TMP"
58
+
59
+ # --- Git commands inside a clean repo (no tmp_pack_*) ---
60
+ REPO=$(setup_repo)
61
+ pushd "$REPO" >/dev/null
62
+ run_hook '{"tool_input":{"command":"git status"}}' 0 "Allow git status in clean repo"
63
+ run_hook '{"tool_input":{"command":"git log"}}' 0 "Allow git log in clean repo"
64
+ run_hook '{"tool_input":{"command":"git commit -m fix"}}' 0 "Allow git commit in clean repo"
65
+ popd >/dev/null
66
+ teardown_repo "$REPO"
67
+
68
+ # --- Cleanup of stale tmp_pack_* (older than threshold) ---
69
+ REPO=$(setup_repo)
70
+ mkdir -p "$REPO/.git/objects/pack"
71
+ # Create a fake stale tmp_pack_ file. Use touch -d to backdate it 2 hours.
72
+ touch -d "2 hours ago" "$REPO/.git/objects/pack/tmp_pack_stale1"
73
+ touch -d "2 hours ago" "$REPO/.git/objects/pack/tmp_pack_stale2"
74
+ echo "x" > "$REPO/.git/objects/pack/tmp_pack_stale1"
75
+ touch -d "2 hours ago" "$REPO/.git/objects/pack/tmp_pack_stale1"
76
+ pushd "$REPO" >/dev/null
77
+ run_hook '{"tool_input":{"command":"git status"}}' 0 "Allow git status, stale tmp_pack_* should be removed"
78
+ remaining=$(find "$REPO/.git/objects/pack" -name 'tmp_pack_*' | wc -l)
79
+ if [ "$remaining" -eq 0 ]; then
80
+ echo " PASS: stale tmp_pack_* removed"
81
+ PASS=$((PASS + 1))
82
+ else
83
+ echo " FAIL: $remaining stale tmp_pack_* still present"
84
+ FAIL=$((FAIL + 1))
85
+ fi
86
+ popd >/dev/null
87
+ teardown_repo "$REPO"
88
+
89
+ # --- Recent tmp_pack_* must NOT be removed ---
90
+ REPO=$(setup_repo)
91
+ mkdir -p "$REPO/.git/objects/pack"
92
+ echo "x" > "$REPO/.git/objects/pack/tmp_pack_fresh"
93
+ pushd "$REPO" >/dev/null
94
+ run_hook '{"tool_input":{"command":"git status"}}' 0 "Allow git status, recent tmp_pack_* preserved"
95
+ remaining=$(find "$REPO/.git/objects/pack" -name 'tmp_pack_*' | wc -l)
96
+ if [ "$remaining" -eq 1 ]; then
97
+ echo " PASS: recent tmp_pack_* preserved"
98
+ PASS=$((PASS + 1))
99
+ else
100
+ echo " FAIL: recent tmp_pack_* changed (remaining=$remaining)"
101
+ FAIL=$((FAIL + 1))
102
+ fi
103
+ popd >/dev/null
104
+ teardown_repo "$REPO"
105
+
106
+ # --- Block when tmp_pack_* total exceeds CC_GIT_PACK_BLOCK_GB ---
107
+ # Use a tiny block threshold so a small synthetic file trips it.
108
+ REPO=$(setup_repo)
109
+ mkdir -p "$REPO/.git/objects/pack"
110
+ # Create a 2 MiB recent file; with CC_GIT_PACK_BLOCK_GB=0 even the
111
+ # smallest non-empty tmp_pack_ trips the block branch (>= 0 GiB).
112
+ dd if=/dev/zero of="$REPO/.git/objects/pack/tmp_pack_big" bs=1M count=2 status=none
113
+ pushd "$REPO" >/dev/null
114
+ run_hook '{"tool_input":{"command":"git status"}}' 2 "Block when remaining > block threshold" "CC_GIT_PACK_BLOCK_GB=0 CC_GIT_PACK_AGE_MIN=10000"
115
+ popd >/dev/null
116
+ teardown_repo "$REPO"
117
+
118
+ # --- DISABLE flag should bypass everything ---
119
+ REPO=$(setup_repo)
120
+ mkdir -p "$REPO/.git/objects/pack"
121
+ dd if=/dev/zero of="$REPO/.git/objects/pack/tmp_pack_big" bs=1M count=2 status=none
122
+ pushd "$REPO" >/dev/null
123
+ run_hook '{"tool_input":{"command":"git status"}}' 0 "DISABLE flag bypasses all checks" "CC_GIT_PACK_DISABLE=1 CC_GIT_PACK_BLOCK_GB=0"
124
+ popd >/dev/null
125
+ teardown_repo "$REPO"
126
+
127
+ # --- Non-git binaries with 'git' substring ---
128
+ run_hook '{"tool_input":{"command":"echo /usr/local/lib/gitlab"}}' 0 "Allow path with 'gitlab' substring"
129
+ run_hook '{"tool_input":{"command":"npm install gitignore-cli"}}' 0 "Allow npm install with gitignore-cli"
130
+
131
+ echo ""
132
+ echo "Results: $PASS passed, $FAIL failed out of $((PASS + FAIL)) tests"
133
+ [ "$FAIL" -eq 0 ] && exit 0 || exit 1
@@ -0,0 +1,66 @@
1
+ #!/bin/bash
2
+ # Tests for git-rm-orphan-wipe-guard.sh
3
+ # Run: bash tests/git-rm-orphan-wipe-guard.test.sh
4
+ set -euo pipefail
5
+
6
+ PASS=0
7
+ FAIL=0
8
+ HOOK="$(dirname "$0")/../examples/git-rm-orphan-wipe-guard.sh"
9
+
10
+ test_hook() {
11
+ local input="$1" expected_exit="$2" desc="$3"
12
+ local actual_exit=0
13
+ echo "$input" | bash "$HOOK" > /dev/null 2>/dev/null || actual_exit=$?
14
+ if [ "$actual_exit" -eq "$expected_exit" ]; then
15
+ echo " PASS: $desc"
16
+ PASS=$((PASS + 1))
17
+ else
18
+ echo " FAIL: $desc (expected exit $expected_exit, got $actual_exit)"
19
+ FAIL=$((FAIL + 1))
20
+ fi
21
+ }
22
+
23
+ echo "git-rm-orphan-wipe-guard.sh tests"
24
+ echo ""
25
+
26
+ # --- Block: the #70687 compound command ---
27
+ test_hook '{"tool_input":{"command":"git checkout --orphan wipe && git rm -rf . --quiet"}}' 2 "Block the #70687 orphan+rm compound"
28
+
29
+ # --- Block: git checkout/switch --orphan ---
30
+ test_hook '{"tool_input":{"command":"git checkout --orphan newroot"}}' 2 "Block git checkout --orphan"
31
+ test_hook '{"tool_input":{"command":"git switch --orphan fresh"}}' 2 "Block git switch --orphan"
32
+ test_hook '{"tool_input":{"command":"git checkout --orphan"}}' 2 "Block bare git checkout --orphan"
33
+
34
+ # --- Block: recursive git rm ---
35
+ test_hook '{"tool_input":{"command":"git rm -rf ."}}' 2 "Block git rm -rf ."
36
+ test_hook '{"tool_input":{"command":"git rm -r src"}}' 2 "Block git rm -r <dir>"
37
+ test_hook '{"tool_input":{"command":"git rm -fr ."}}' 2 "Block git rm -fr ."
38
+ test_hook '{"tool_input":{"command":"git rm --recursive build"}}' 2 "Block git rm --recursive"
39
+
40
+ # --- Block: whole-tree target without -r ---
41
+ test_hook '{"tool_input":{"command":"git rm ."}}' 2 "Block git rm ."
42
+ test_hook '{"tool_input":{"command":"git rm *"}}' 2 "Block git rm *"
43
+
44
+ # --- Allow: --cached (untracks only, disk files survive) ---
45
+ test_hook '{"tool_input":{"command":"git rm -r --cached ."}}' 0 "Allow git rm -r --cached . (untrack, no disk loss)"
46
+ test_hook '{"tool_input":{"command":"git rm --cached secrets.env"}}' 0 "Allow git rm --cached <file>"
47
+
48
+ # --- Allow: single-file git rm ---
49
+ test_hook '{"tool_input":{"command":"git rm old.txt"}}' 0 "Allow git rm <file>"
50
+ test_hook '{"tool_input":{"command":"git rm path/to/file.js"}}' 0 "Allow git rm <path/file>"
51
+
52
+ # --- Allow: safe git / unrelated commands ---
53
+ test_hook '{"tool_input":{"command":"git checkout main"}}' 0 "Allow git checkout <branch>"
54
+ test_hook '{"tool_input":{"command":"git switch feature"}}' 0 "Allow git switch <branch>"
55
+ test_hook '{"tool_input":{"command":"git status"}}' 0 "Allow git status"
56
+ test_hook '{"tool_input":{"command":"git commit -m fix"}}' 0 "Allow git commit"
57
+ test_hook '{"tool_input":{"command":"grep orphan README.md"}}' 0 "Allow grep orphan (substring, not git)"
58
+ test_hook '{"tool_input":{"command":"rm -rf node_modules"}}' 0 "Allow plain rm (rm-safety-net territory, not this guard)"
59
+
60
+ # --- Allow: empty/missing input ---
61
+ test_hook '{}' 0 "Allow empty input"
62
+ test_hook '{"tool_input":{}}' 0 "Allow missing command"
63
+
64
+ echo ""
65
+ echo "Results: $PASS passed, $FAIL failed out of $((PASS + FAIL)) tests"
66
+ [ "$FAIL" -eq 0 ] && exit 0 || exit 1
@@ -0,0 +1,50 @@
1
+ #!/bin/bash
2
+ # Tests for git-submodule-guard.sh
3
+ # Verifies that the data-destroying forced deinit (#68920) is blocked,
4
+ # while unforced deinit / submodule rm are warn-only.
5
+ HOOK="examples/git-submodule-guard.sh"
6
+ PASS=0 FAIL=0
7
+
8
+ HOOK_ABS="$PWD/$HOOK"
9
+
10
+ # run_hook <command> [env assignments...] -> sets $OUT and $RC
11
+ run_hook() {
12
+ local cmd="$1"; shift
13
+ OUT=$(printf '{"tool_input":{"command":"%s"}}' "$cmd" | env "$@" bash "$HOOK_ABS" 2>&1)
14
+ RC=$?
15
+ }
16
+
17
+ assert_exit() { if [ "$2" = "$3" ]; then PASS=$((PASS+1)); else FAIL=$((FAIL+1)); echo "FAIL: $1 (expected exit $3, got $2)"; fi; }
18
+ assert_contains() { if echo "$2" | grep -q "$3"; then PASS=$((PASS+1)); else FAIL=$((FAIL+1)); echo "FAIL: $1 (expected '$3')"; fi; }
19
+
20
+ # Forced deinit must be blocked (exit 2) by default.
21
+ run_hook "git submodule deinit -f libs/foo"
22
+ assert_exit "forced -f blocks" "$RC" 2
23
+ assert_contains "block message cites #68920" "$OUT" "#68920"
24
+
25
+ run_hook "git submodule deinit --force libs/foo"
26
+ assert_exit "forced --force blocks" "$RC" 2
27
+
28
+ run_hook "git submodule deinit --all -f"
29
+ assert_exit "deinit --all -f blocks" "$RC" 2
30
+
31
+ # Unforced deinit: git's own safety still applies, so warn-only (exit 0).
32
+ run_hook "git submodule deinit libs/foo"
33
+ assert_exit "unforced deinit warns (exit 0)" "$RC" 0
34
+ assert_contains "unforced deinit warns" "$OUT" "WARNING"
35
+
36
+ # submodule rm: warn-only.
37
+ run_hook "git submodule rm libs/foo"
38
+ assert_exit "submodule rm warns (exit 0)" "$RC" 0
39
+
40
+ # Unrelated command: untouched.
41
+ run_hook "git status"
42
+ assert_exit "unrelated passes" "$RC" 0
43
+
44
+ # Opt-out: warn-only mode lets the forced deinit through (exit 0).
45
+ run_hook "git submodule deinit -f libs/foo" SUBMODULE_DEINIT_BLOCK=0
46
+ assert_exit "opt-out warn-only (exit 0)" "$RC" 0
47
+ assert_contains "opt-out still warns" "$OUT" "WARNING"
48
+
49
+ echo "PASS=$PASS FAIL=$FAIL"
50
+ [ "$FAIL" -eq 0 ]