cc-safe-setup 29.8.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.
- package/.claude-plugin/marketplace.json +72 -19
- package/.claude-plugin/plugin.json +1 -1
- package/CONTRIBUTING.md +7 -2
- package/COOKBOOK.md +50 -46
- package/LICENSE +21 -0
- package/MIGRATION.md +8 -8
- package/README.md +419 -512
- package/SAFETY_CHECKLIST.md +15 -7
- package/SERVICES.md +194 -0
- package/SETTINGS_REFERENCE.md +236 -17
- package/SKILL.md +5 -5
- package/TROUBLESHOOTING.md +35 -14
- package/action.yml +12 -4
- package/audit/README.md +84 -0
- package/audit/audit-checklist.md +152 -0
- package/audit/boundary-selftest.sh +85 -0
- package/audit/boundary.sh +123 -0
- package/audit/count-hooks.py +42 -0
- package/audit/find-dead-hooks.sh +82 -0
- package/audit/find-unfirable-rules.sh +142 -0
- package/audit/fire.sh +97 -0
- package/audit/selftest.sh +42 -0
- package/audit/unfirable-selftest.sh +81 -0
- package/audit-web/index.html +9 -9
- package/examples/README.md +54 -17
- package/examples/account-billing-log.sh +98 -0
- package/examples/account-routing-preflight.sh +123 -0
- package/examples/agent-sdk-credit-pool-monitor.sh +145 -0
- package/examples/agent-view-quota-decrement.sh +32 -0
- package/examples/agent-view-quota-warn.sh +94 -0
- package/examples/agents-md-edit-drift-warner.sh +223 -0
- package/examples/agents-md-loader.sh +160 -0
- package/examples/agents-md-sync-checker.sh +102 -0
- package/examples/ai-slop-punctuation-arrest.sh +210 -0
- package/examples/allowlist.sh +70 -10
- package/examples/always-allow-pattern-suggester.sh +125 -0
- package/examples/ansible-vault-guard.sh +2 -1
- package/examples/anthropic-base-url-guard.sh +76 -0
- package/examples/api-busyloop-guard.sh +123 -0
- package/examples/api-endpoint-guard.sh +12 -0
- package/examples/api-key-in-url-guard.sh +17 -1
- package/examples/api-rate-limit-guard.sh +36 -1
- package/examples/api-rate-limit-tracker.sh +12 -0
- package/examples/articulated-scope-capture.sh +101 -0
- package/examples/askuserquestion-autonomy-gate.sh +102 -0
- package/examples/attachment-traversal-guard.sh +118 -0
- package/examples/aup-block-pattern-logger.sh +161 -0
- package/examples/aup-false-positive-helper.sh +124 -0
- package/examples/aup-large-tool-output-warner.sh +282 -0
- package/examples/aup-retry-loop-guard.sh +229 -0
- package/examples/auth-expiry-reminder.sh +157 -0
- package/examples/auth-macos-sleep-detector.sh +185 -0
- package/examples/auth-path-detector.sh +171 -0
- package/examples/auth-status-checker.sh +157 -0
- package/examples/authorized-reconfirmation-detector.sh +195 -0
- package/examples/auto-approve-build.sh +39 -0
- package/examples/auto-approve-cargo.sh +31 -1
- package/examples/auto-approve-docker.sh +35 -0
- package/examples/auto-approve-git-read.sh +37 -14
- package/examples/auto-approve-go.sh +30 -1
- package/examples/auto-approve-gradle.sh +29 -1
- package/examples/auto-approve-make.sh +29 -1
- package/examples/auto-approve-maven.sh +29 -1
- package/examples/auto-approve-python.sh +41 -1
- package/examples/auto-approve-readonly-tools.sh +1 -0
- package/examples/auto-approve-readonly.sh +110 -33
- package/examples/auto-approve-ssh.sh +30 -1
- package/examples/auto-approve-test.sh +44 -0
- package/examples/auto-checkpoint.sh +1 -1
- package/examples/auto-compact-prep.sh +0 -0
- package/examples/auto-git-checkpoint.sh +0 -0
- package/examples/auto-mode-safe-commands.sh +53 -1
- package/examples/auto-mode-safety-enforcer.sh +151 -4
- package/examples/auto-push-session-branch.sh +22 -0
- package/examples/aws-production-guard.sh +13 -1
- package/examples/aws-region-guard.sh +1 -1
- package/examples/background-cost-launch-guard.sh +82 -0
- package/examples/background-task-guard.sh +12 -0
- package/examples/backup-before-refactor.sh +0 -0
- package/examples/banned-command-guard.sh +12 -0
- package/examples/bash-allowlist-secondary-check.sh +157 -0
- package/examples/bash-domain-allowlist.sh +12 -0
- package/examples/bash-fanout-bounded-rewriter.sh +191 -0
- package/examples/bash-heuristic-approver.sh +112 -10
- package/examples/bash-project-fence.sh +194 -0
- package/examples/bash-safety-auto-deny.sh +12 -0
- package/examples/bash-secret-output-detector.sh +44 -3
- package/examples/bash-trace-guard.sh +12 -0
- package/examples/bashrc-safety-check.sh +0 -0
- package/examples/bg-permission-prompt-warner.sh +182 -0
- package/examples/bg-task-cooldown-guard.sh +12 -0
- package/examples/binary-read-detector.sh +135 -0
- package/examples/binary-upload-guard.sh +12 -0
- package/examples/block-database-wipe.sh +35 -10
- package/examples/branch-name-check.sh +8 -3
- package/examples/branch-naming-convention.sh +0 -0
- package/examples/broad-find-guard.sh +12 -0
- package/examples/broad-prefix-session-trap-warner.sh +106 -0
- package/examples/bulk-file-delete-guard.sh +45 -0
- package/examples/bypass-mode-effective-verifier.sh +203 -0
- package/examples/cache-creation-drift-detector.sh +120 -0
- package/examples/cache-residue-detector.sh +195 -0
- package/examples/cache-tier-logger.sh +70 -0
- package/examples/cache-ttl-eviction-detector.sh +149 -0
- package/examples/cargo-publish-guard.sh +14 -1
- package/examples/case-insensitive-path-guard.sh +21 -2
- package/examples/case-sensitive-guard.sh +23 -2
- package/examples/cch-cache-guard.sh +0 -0
- package/examples/cch-sentinel-precommit-guard.sh +47 -0
- package/examples/check-abort-controller.sh +1 -1
- package/examples/check-accessibility.sh +0 -0
- package/examples/check-aria-labels.sh +0 -0
- package/examples/check-before-act-enforcer.sh +12 -0
- package/examples/check-charset-meta.sh +0 -0
- package/examples/check-cookie-flags.sh +0 -0
- package/examples/check-cors-config.sh +0 -0
- package/examples/check-csp-headers.sh +0 -0
- package/examples/check-csrf-protection.sh +0 -0
- package/examples/check-debounce.sh +1 -1
- package/examples/check-dependency-age.sh +0 -0
- package/examples/check-dependency-license.sh +0 -0
- package/examples/check-dockerfile-best-practice.sh +0 -0
- package/examples/check-error-boundaries.sh +0 -0
- package/examples/check-error-class.sh +1 -1
- package/examples/check-error-handling.sh +0 -0
- package/examples/check-error-logging.sh +1 -1
- package/examples/check-error-message.sh +0 -0
- package/examples/check-error-stack.sh +0 -0
- package/examples/check-favicon.sh +0 -0
- package/examples/check-git-hooks-compat.sh +0 -0
- package/examples/check-gitattributes.sh +0 -0
- package/examples/check-https-redirect.sh +0 -0
- package/examples/check-input-validation.sh +0 -0
- package/examples/check-lang-attribute.sh +0 -0
- package/examples/check-npm-scripts-exist.sh +0 -0
- package/examples/check-package-size.sh +0 -0
- package/examples/check-port-availability.sh +0 -0
- package/examples/check-promise-all.sh +1 -1
- package/examples/check-rate-limiting.sh +0 -0
- package/examples/check-return-types.sh +0 -0
- package/examples/check-semantic-versioning.sh +0 -0
- package/examples/check-suspense-fallback.sh +1 -1
- package/examples/check-test-exists.sh +0 -0
- package/examples/check-test-naming.sh +0 -0
- package/examples/check-timeout-cleanup.sh +1 -1
- package/examples/check-tls-version.sh +0 -0
- package/examples/check-unsubscribe.sh +1 -1
- package/examples/check-viewport-meta.sh +0 -0
- package/examples/check-worker-terminate.sh +1 -1
- package/examples/checkpoint-tamper-guard.sh +12 -0
- package/examples/chmod-guard.sh +12 -0
- package/examples/chown-guard.sh +24 -0
- package/examples/ci-workflow-guard.sh +6 -1
- package/examples/claim-verify-detector.sh +109 -0
- package/examples/claim-vs-caveat-checker.sh +77 -0
- package/examples/classifier-fallback-allow.sh +114 -40
- package/examples/claude-cache-gc.sh +1 -0
- package/examples/claude-code-version-pin-advisor.sh +183 -0
- package/examples/claude-md-reinjector.sh +129 -0
- package/examples/claude-md-size-monitor.sh +66 -0
- package/examples/claude-update-budget-guard.sh +123 -0
- package/examples/claude-update-smart.sh +113 -0
- package/examples/claudeignore-enforce-guard.sh +12 -0
- package/examples/claudemd-enforcer.sh +0 -0
- package/examples/claudemd-tool-prohibition.sh +128 -0
- package/examples/clear-command-confirm-guard.sh +32 -15
- package/examples/cli-config-pinning-detector.sh +162 -0
- package/examples/cliff-countdown-advisor.sh +96 -0
- package/examples/clipboard-secret-guard.sh +12 -0
- package/examples/closure-word-verify-gate.sh +150 -0
- package/examples/cloud-cli-guard.sh +39 -2
- package/examples/commit-all-scope-guard.sh +44 -0
- package/examples/commit-message-check.sh +1 -1
- package/examples/commit-message-quality.sh +6 -1
- package/examples/commitment-carry-forward-arrest.sh +361 -0
- package/examples/compact-alert-notification.sh +0 -0
- package/examples/compact-dispatch-watchdog.sh +149 -0
- package/examples/completion-claim-without-verification-detector.sh +207 -0
- package/examples/composer-guard.sh +12 -0
- package/examples/compound-bash-permission-resolver.sh +156 -0
- package/examples/compound-command-allow.sh +1 -1
- package/examples/compound-command-deny-enforcer.sh +183 -0
- package/examples/compound-inject-guard.sh +12 -0
- package/examples/concurrent-edit-lock.sh +12 -0
- package/examples/conflict-marker-guard.sh +12 -0
- package/examples/consecutive-error-breaker.sh +0 -0
- package/examples/console-log-count.sh +1 -0
- package/examples/context-compact-advisor.sh +29 -2
- package/examples/context-length-budget.sh +69 -0
- package/examples/context-threshold-alert.sh +12 -0
- package/examples/context-usage-drift-alert.sh +41 -13
- package/examples/conversation-history-guard.sh +12 -0
- package/examples/core-file-protect-guard.sh +12 -0
- package/examples/cors-star-warn.sh +0 -0
- package/examples/cost-incident-self-audit.sh +162 -0
- package/examples/cowork-claude-md-load-checker.sh +100 -0
- package/examples/cowork-fuse-staleness-watcher.sh +140 -0
- package/examples/cowork-hook-absence-warner.sh +167 -0
- package/examples/cowork-model-picker-advisor.sh +115 -0
- package/examples/credential-exfil-guard.sh +65 -4
- package/examples/credential-file-cat-guard.sh +17 -1
- package/examples/cron-create-receipt.sh +112 -0
- package/examples/cron-modification-guard.sh +12 -0
- package/examples/cross-product-mcp-spawn-detector.sh +178 -0
- package/examples/cwd-project-boundary-guard.sh +0 -0
- package/examples/daemon-zombie-worker-surfacer.sh +118 -0
- package/examples/daily-cost-guard.sh +164 -0
- package/examples/daily-usage-tracker.sh +0 -0
- package/examples/dangerous-pip-flag-guard.sh +12 -0
- package/examples/db-connect-guard.sh +13 -1
- package/examples/denied-action-retry-guard.sh +12 -0
- package/examples/deny-bypass-detector.sh +12 -0
- package/examples/deny-rule-integrity-verifier.sh +181 -0
- package/examples/dependency-install-guard.sh +12 -0
- package/examples/deploy-guard.sh +14 -2
- package/examples/deploy-path-verify-guard.sh +12 -0
- package/examples/deployment-readback-gate.sh +135 -0
- package/examples/deployment-readback-gh-adapter.sh +247 -0
- package/examples/deployment-verify-guard.sh +25 -2
- package/examples/destructive-db-script-write-guard.sh +166 -0
- package/examples/destructive-migration-write-guard.sh +163 -0
- package/examples/diff-size-guard.sh +12 -0
- package/examples/disabled-feature-toggle-advisor.sh +88 -0
- package/examples/disk-partition-guard.sh +12 -0
- package/examples/dispatch-allowlist-preflight.sh +301 -0
- package/examples/dispatch-liveness-watchdog.sh +216 -0
- package/examples/dispatch-receipt.sh +174 -0
- package/examples/django-migrate-guard.sh +12 -0
- package/examples/dns-config-guard.sh +12 -0
- package/examples/docker-dangerous-guard.sh +12 -0
- package/examples/docker-volume-guard.sh +0 -0
- package/examples/dockerfile-latest-guard.sh +1 -0
- package/examples/dotenv-anthropic-key-billing-guard.sh +99 -0
- package/examples/dotenv-commit-guard.sh +12 -0
- package/examples/dotenv-read-guard.sh +12 -0
- package/examples/dotfile-protection-guard.sh +12 -0
- package/examples/dotnet-build-on-edit.sh +1 -1
- package/examples/drizzle-migrate-guard.sh +27 -2
- package/examples/echo-probe-spam-detector.sh +176 -0
- package/examples/edit-counter-test-gate.sh +12 -0
- package/examples/edit-error-counter.sh +1 -1
- package/examples/edit-guard.sh +12 -0
- package/examples/edit-old-string-validator.sh +12 -0
- package/examples/edit-quote-ambiguity-guard.sh +97 -0
- package/examples/edit-retry-loop-guard.sh +0 -0
- package/examples/edit-verify.sh +0 -0
- package/examples/env-inline-secret-guard.sh +14 -2
- package/examples/env-naming-convention.sh +0 -0
- package/examples/env-prod-guard.sh +0 -0
- package/examples/env-required-check.sh +0 -0
- package/examples/env-source-guard.sh +12 -0
- package/examples/env-var-check.sh +24 -1
- package/examples/ephemeral-container-detector.sh +147 -0
- package/examples/error-memory-guard.sh +12 -0
- package/examples/evidence-claim-gate.sh +187 -0
- package/examples/exploration-budget-guard.sh +12 -0
- package/examples/expo-eject-guard.sh +13 -0
- package/examples/extended-thinking-loop-guard.sh +139 -0
- package/examples/extended-thinking-resume-warning.sh +119 -0
- package/examples/extended-thinking-tool-use-mismatch-detector.sh +212 -0
- package/examples/fabricated-command-detector.sh +145 -0
- package/examples/false-disk-full-guard.sh +63 -0
- package/examples/feature-deprecation-detector.sh +109 -0
- package/examples/file-change-tracker.sh +1 -1
- package/examples/file-change-undo-tracker.sh +0 -0
- package/examples/file-edit-backup.sh +63 -14
- package/examples/file-reference-check.sh +0 -0
- package/examples/file-size-limit.sh +12 -0
- package/examples/financial-operation-guard.sh +12 -0
- package/examples/firewall-guard.sh +12 -0
- package/examples/fish-shell-wrapper.sh +125 -7
- package/examples/five-hundred-milestone.sh +1 -0
- package/examples/flask-static-route-guard.sh +89 -0
- package/examples/forbidden-response-phrase-detector.sh +161 -0
- package/examples/gem-push-guard.sh +13 -0
- package/examples/gh-cli-destructive-guard.sh +13 -1
- package/examples/git-checkout-safety-guard.sh +12 -0
- package/examples/git-checkout-uncommitted-guard.sh +12 -0
- package/examples/git-config-guard.sh +12 -0
- package/examples/git-crypt-worktree-guard.sh +12 -0
- package/examples/git-filter-repo-guard.sh +56 -0
- package/examples/git-history-rewrite-guard.sh +12 -0
- package/examples/git-hook-bypass-guard.sh +0 -0
- package/examples/git-maintenance-guard.sh +83 -0
- package/examples/git-merge-conflict-prevent.sh +0 -0
- package/examples/git-message-length-check.sh +0 -0
- package/examples/git-message-length.sh +0 -0
- package/examples/git-operations-require-approval.sh +68 -14
- package/examples/git-pack-temp-cleanup-guard.sh +102 -0
- package/examples/git-push-blast-radius-guard.sh +218 -0
- package/examples/git-remote-guard.sh +24 -3
- package/examples/git-rm-orphan-wipe-guard.sh +85 -0
- package/examples/git-stash-before-checkout.sh +12 -0
- package/examples/git-stash-before-danger.sh +0 -0
- package/examples/git-submodule-guard.sh +69 -2
- package/examples/git-tag-guard.sh +12 -0
- package/examples/github-actions-secret-guard.sh +0 -0
- package/examples/gitignore-check.sh +0 -0
- package/examples/gitops-drift-guard.sh +12 -0
- package/examples/go-mod-tidy-warn.sh +1 -0
- package/examples/go-vet-after-edit.sh +12 -0
- package/examples/goal-iteration-limit-warner.sh +152 -0
- package/examples/grep-nul-false-negative-guard.sh +30 -0
- package/examples/growthbook-flag-monitor.sh +257 -0
- package/examples/hallucination-url-check.sh +1 -1
- package/examples/halt-signal-detector.sh +135 -0
- package/examples/handoff-on-clear.sh +65 -0
- package/examples/hardcoded-ip-guard.sh +1 -0
- package/examples/headless-empty-result-guard.sh +0 -0
- package/examples/headless-stop-guard.sh +0 -0
- package/examples/helm-install-guard.sh +2 -1
- package/examples/home-critical-bash-guard.sh +17 -1
- package/examples/hook-debug-wrapper.sh +6 -2
- package/examples/hook-permission-fixer.sh +0 -0
- package/examples/hook-self-disable-detector.sh +99 -0
- package/examples/hook-stdout-sanitizer.sh +6 -2
- package/examples/hook-tamper-guard.sh +12 -0
- package/examples/idle-session-cost-alert.sh +0 -0
- package/examples/image-dimension-guard.sh +109 -0
- package/examples/image-file-validator.sh +0 -0
- package/examples/iteration-quality-degradation-detector.sh +170 -0
- package/examples/java-compile-on-edit.sh +1 -1
- package/examples/json-syntax-check.sh +0 -0
- package/examples/k8s-production-guard.sh +13 -1
- package/examples/kill-process-guard.sh +12 -0
- package/examples/kubernetes-guard.sh +13 -1
- package/examples/laravel-artisan-guard.sh +13 -0
- package/examples/large-file-guard.sh +1 -1
- package/examples/log-level-guard.sh +0 -0
- package/examples/log-truncation-guard.sh +12 -0
- package/examples/long-session-malformed-tool-call-detector.sh +183 -0
- package/examples/loop-detector.sh +12 -0
- package/examples/magic-number-warn.sh +1 -0
- package/examples/main-branch-warn.sh +12 -0
- package/examples/markdown-link-check.sh +0 -0
- package/examples/max-concurrent-agents.sh +12 -0
- package/examples/max-edit-size-guard.sh +13 -0
- package/examples/max-file-count-guard.sh +0 -0
- package/examples/max-file-delete-count.sh +0 -0
- package/examples/max-function-length.sh +1 -0
- package/examples/max-import-count.sh +0 -0
- package/examples/max-subagent-count.sh +0 -0
- package/examples/mcp-config-freeze.sh +12 -0
- package/examples/mcp-config-poisoning-audit.sh +150 -0
- package/examples/mcp-data-boundary.sh +1 -1
- package/examples/mcp-misdiagnosis-arrest.sh +209 -0
- package/examples/mcp-orphan-leak-warner.sh +49 -0
- package/examples/mcp-permissions-ask-warner.sh +151 -0
- package/examples/mcp-routine-approval-detector.sh +154 -0
- package/examples/mcp-server-allowlist.sh +12 -0
- package/examples/mcp-server-guard.sh +12 -0
- package/examples/mcp-startup-bloat-detector.sh +87 -0
- package/examples/mcp-stdio-compatibility-test.sh +153 -0
- package/examples/mcp-tool-audit-log.sh +0 -0
- package/examples/mcp-tool-guard.sh +101 -1
- package/examples/mcp-warmup-wait.sh +0 -0
- package/examples/memory-chain-audit.sh +56 -0
- package/examples/memory-orphan-detector.sh +188 -0
- package/examples/memory-write-guard.sh +99 -17
- package/examples/migration-verify-guard.sh +12 -0
- package/examples/model-swap-suggester.sh +177 -0
- package/examples/model-version-alert.sh +5 -1
- package/examples/model-version-lock.sh +63 -0
- package/examples/move-delete-sequence-guard.sh +33 -2
- package/examples/multi-file-plan-routing-gate.sh +192 -0
- package/examples/multi-vendor-concurrent-warner.sh +108 -0
- package/examples/multi-window-auth-drift-detector.sh +183 -0
- package/examples/multiline-command-approver.sh +161 -23
- package/examples/nested-background-agent-guard.sh +109 -0
- package/examples/nested-spawn-inflight-guard.sh +178 -0
- package/examples/network-exfil-guard.sh +12 -0
- package/examples/network-guard.sh +12 -0
- package/examples/network-interface-guard.sh +12 -0
- package/examples/nextjs-env-guard.sh +1 -1
- package/examples/no-absolute-import.sh +0 -0
- package/examples/no-alert-confirm-prompt.sh +0 -0
- package/examples/no-anonymous-default-export.sh +0 -0
- package/examples/no-any-type.sh +0 -0
- package/examples/no-any-typescript.sh +1 -0
- package/examples/no-ask-human.sh +12 -0
- package/examples/no-assignment-in-condition.sh +0 -0
- package/examples/no-base64-exfil.sh +12 -0
- package/examples/no-callback-hell.sh +0 -0
- package/examples/no-circular-dependency.sh +0 -0
- package/examples/no-cleartext-storage.sh +0 -0
- package/examples/no-commented-code.sh +0 -0
- package/examples/no-commit-fixup.sh +0 -0
- package/examples/no-console-assert.sh +0 -0
- package/examples/no-console-error-swallow.sh +0 -0
- package/examples/no-console-in-prod.sh +0 -0
- package/examples/no-console-log-commit.sh +12 -0
- package/examples/no-console-log.sh +0 -0
- package/examples/no-console-time.sh +0 -0
- package/examples/no-cors-wildcard.sh +4 -1
- package/examples/no-curl-upload.sh +0 -0
- package/examples/no-dangerouslySetInnerHTML.sh +0 -0
- package/examples/no-dangling-await.sh +1 -0
- package/examples/no-debug-commit.sh +12 -0
- package/examples/no-debug-in-commit.sh +0 -0
- package/examples/no-deep-nesting.sh +0 -0
- package/examples/no-deep-relative-import.sh +1 -0
- package/examples/no-default-credentials.sh +0 -0
- package/examples/no-deploy-friday.sh +13 -1
- package/examples/no-disabled-test.sh +0 -0
- package/examples/no-document-write.sh +0 -0
- package/examples/no-empty-function.sh +0 -0
- package/examples/no-eval-in-template.sh +0 -0
- package/examples/no-eval-template.sh +3 -1
- package/examples/no-eval.sh +0 -0
- package/examples/no-exec-user-input.sh +0 -0
- package/examples/no-fixme-ship.sh +12 -0
- package/examples/no-floating-promises.sh +0 -0
- package/examples/no-force-flag.sh +22 -5
- package/examples/no-force-install.sh +0 -0
- package/examples/no-git-amend.sh +12 -0
- package/examples/no-git-rebase-public.sh +0 -0
- package/examples/no-global-install.sh +12 -0
- package/examples/no-global-state.sh +0 -0
- package/examples/no-hardcoded-port.sh +1 -0
- package/examples/no-hardcoded-url.sh +0 -0
- package/examples/no-helmet-missing.sh +0 -0
- package/examples/no-http-url.sh +1 -0
- package/examples/no-http-without-https.sh +0 -0
- package/examples/no-inline-style.sh +0 -0
- package/examples/no-inline-styles.sh +1 -0
- package/examples/no-innerhtml.sh +0 -0
- package/examples/no-install-global.sh +12 -0
- package/examples/no-jwt-in-url.sh +0 -0
- package/examples/no-large-commit.sh +0 -0
- package/examples/no-localhost-expose.sh +0 -0
- package/examples/no-magic-number.sh +0 -0
- package/examples/no-md5-sha1.sh +0 -0
- package/examples/no-mixed-line-endings.sh +8 -1
- package/examples/no-mutation-in-reducer.sh +0 -0
- package/examples/no-nested-ternary.sh +0 -0
- package/examples/no-network-exfil.sh +0 -0
- package/examples/no-open-redirect.sh +0 -0
- package/examples/no-output-truncation.sh +12 -0
- package/examples/no-package-downgrade.sh +0 -0
- package/examples/no-package-lock-edit.sh +12 -0
- package/examples/no-path-join-user-input.sh +0 -0
- package/examples/no-port-bind.sh +0 -0
- package/examples/no-process-exit.sh +0 -0
- package/examples/no-prototype-pollution.sh +0 -0
- package/examples/no-push-without-ci.sh +0 -0
- package/examples/no-push-without-tests.sh +12 -0
- package/examples/no-raw-password-in-url.sh +0 -0
- package/examples/no-root-user-docker.sh +1 -0
- package/examples/no-root-write.sh +12 -0
- package/examples/no-secrets-in-args.sh +4 -1
- package/examples/no-secrets-in-logs.sh +0 -0
- package/examples/no-sensitive-log.sh +0 -0
- package/examples/no-sleep-in-hooks.sh +0 -0
- package/examples/no-star-import-python.sh +1 -0
- package/examples/no-string-concat-sql.sh +0 -0
- package/examples/no-sudo-guard.sh +12 -0
- package/examples/no-sync-fs.sh +0 -0
- package/examples/no-todo-in-merge.sh +0 -0
- package/examples/no-todo-in-production.sh +1 -0
- package/examples/no-todo-without-issue.sh +0 -0
- package/examples/no-unused-import.sh +0 -0
- package/examples/no-var-keyword.sh +0 -0
- package/examples/no-verify-blocker.sh +12 -0
- package/examples/no-wget-piped-bash.sh +12 -0
- package/examples/no-wildcard-cors.sh +0 -0
- package/examples/no-wildcard-delete.sh +0 -0
- package/examples/no-wildcard-import.sh +0 -0
- package/examples/no-with-statement.sh +0 -0
- package/examples/no-write-outside-src.sh +0 -0
- package/examples/no-xml-external-entity.sh +0 -0
- package/examples/non-english-quality-warner.sh +156 -0
- package/examples/notify-waiting.sh +1 -1
- package/examples/npm-audit-warn.sh +0 -0
- package/examples/npm-global-install-guard.sh +12 -0
- package/examples/npm-publish-guard.sh +12 -0
- package/examples/npm-script-injection.sh +0 -0
- package/examples/npm-supply-chain-guard.sh +0 -0
- package/examples/nuxt-config-guard.sh +1 -0
- package/examples/oauth-refresh-monitor.sh +210 -0
- package/examples/opus-degradation-tracker.sh +187 -0
- package/examples/opus48-routine-task-warning.sh +196 -0
- package/examples/opus48-thinking-wedge-advisor.sh +143 -0
- package/examples/output-credential-scan.sh +1 -1
- package/examples/output-secret-mask.sh +31 -9
- package/examples/output-token-spike-detector.sh +145 -0
- package/examples/package-json-guard.sh +12 -0
- package/examples/package-lock-frozen.sh +12 -0
- package/examples/parallel-batch-size-limiter.sh +121 -0
- package/examples/parallel-cascade-detector.sh +116 -0
- package/examples/partial-view-claim-arrest.sh +174 -0
- package/examples/path-deny-bash-guard.sh +12 -0
- package/examples/path-traversal-guard.sh +12 -0
- package/examples/per-prompt-token-cap.sh +124 -0
- package/examples/permission-audit-log.sh +1 -1
- package/examples/permission-denial-enforcer.sh +12 -0
- package/examples/permission-entry-validator.sh +0 -0
- package/examples/persisted-1m-model-advisor.sh +88 -0
- package/examples/php-lint-on-edit.sh +1 -1
- package/examples/pii-upload-guard.sh +12 -0
- package/examples/pip-publish-guard.sh +13 -0
- package/examples/pip-requirements-guard.sh +12 -0
- package/examples/pip-venv-required.sh +12 -0
- package/examples/plan-mode-edit-guard.sh +12 -0
- package/examples/plan-mode-enforcer.sh +12 -0
- package/examples/plan-mode-strict-guard.sh +12 -0
- package/examples/plugin-hooks-json-bloat-detector.sh +169 -0
- package/examples/plugin-process-cleanup.sh +0 -0
- package/examples/polyglot-rm-guard.sh +12 -0
- package/examples/post-compact-safety.sh +12 -0
- package/examples/post-edit-disk-verify.sh +152 -0
- package/examples/powershell-remove-item-guard.sh +85 -0
- package/examples/pr-description-check.sh +0 -0
- package/examples/pr-duplicate-guard.sh +13 -0
- package/examples/pre-bash-sed-line-ending-windows.sh +87 -0
- package/examples/pre-compact-knowledge-save.sh +0 -0
- package/examples/pre-compact-transcript-backup.sh +0 -0
- package/examples/pre-execution-claim-detector.sh +179 -0
- package/examples/prefer-builtin-tools.sh +10 -1
- package/examples/prefer-const.sh +0 -0
- package/examples/prefer-dedicated-tools.sh +12 -0
- package/examples/prefer-optional-chaining.sh +0 -0
- package/examples/prisma-migrate-guard.sh +24 -7
- package/examples/production-port-kill-guard.sh +12 -0
- package/examples/prompt-injection-guard.sh +2 -1
- package/examples/prompt-usage-logger.sh +0 -0
- package/examples/protect-claudemd.sh +12 -0
- package/examples/protect-dotfiles.sh +12 -0
- package/examples/proxy-capture-suggester.sh +159 -0
- package/examples/public-artefact-socratic-narrowing.sh +232 -0
- package/examples/public-repo-push-guard.sh +12 -0
- package/examples/push-requires-test-pass-record.sh +0 -0
- package/examples/push-requires-test-pass.sh +12 -0
- package/examples/quota-anomaly-detector.sh +173 -0
- package/examples/quota-reset-audit.sh +58 -0
- package/examples/quoted-flag-approver.sh +99 -10
- package/examples/rails-migration-guard.sh +12 -0
- package/examples/rate-limit-guard.sh +0 -0
- package/examples/read-all-files-enforcer.sh +0 -0
- package/examples/read-budget-guard.sh +12 -0
- package/examples/read-loop-detector.sh +59 -0
- package/examples/read-once-guard.sh +96 -0
- package/examples/read-only-mode.sh +12 -0
- package/examples/readme-exists-check.sh +0 -0
- package/examples/record-read-coverage.sh +73 -0
- package/examples/redirect-fragment-warner.sh +149 -0
- package/examples/redis-flushall-guard.sh +13 -0
- package/examples/redundant-read-blocker.sh +132 -0
- package/examples/refusal-arrest-gate.sh +248 -0
- package/examples/registry-publish-guard.sh +13 -1
- package/examples/remote-control-billing-classifier.sh +135 -0
- package/examples/replace-all-guard.sh +12 -0
- package/examples/repo-visibility-guard.sh +12 -0
- package/examples/reroute-after-block-guard.sh +150 -0
- package/examples/response-budget-guard.sh +0 -0
- package/examples/resume-drift-watcher.sh +57 -0
- package/examples/rg-replace-flag-detector.sh +125 -0
- package/examples/rhetorical-verification-prompt-detector.sh +102 -0
- package/examples/ripgrep-permission-fix.sh +0 -0
- package/examples/rm-safety-net.sh +151 -8
- package/examples/role-tool-guard.sh +12 -0
- package/examples/route-handler-emptiness-gate.sh +324 -0
- package/examples/ruby-lint-on-edit.sh +1 -1
- package/examples/runtime-binary-change-detector.sh +102 -0
- package/examples/same-command-repeat-detector.sh +196 -0
- package/examples/same-correction-arrest.sh +169 -0
- package/examples/sandbox-denywrite-enforcer.sh +78 -0
- package/examples/sandbox-relative-path-audit.sh +83 -25
- package/examples/sandbox-write-verify.sh +12 -0
- package/examples/schema-migration-guard.sh +0 -0
- package/examples/scope-expansion-receipt.sh +178 -0
- package/examples/scope-guard.sh +88 -9
- package/examples/secret-file-read-guard.sh +12 -0
- package/examples/secret-file-write-guard.sh +150 -0
- package/examples/self-modify-bypass-guard.sh +0 -0
- package/examples/sensitive-file-read-guard.sh +23 -3
- package/examples/sensitive-log-guard.sh +1 -0
- package/examples/server-side-prompt-injection-detector.sh +69 -0
- package/examples/session-agent-cost-limiter.sh +12 -0
- package/examples/session-backup-on-start.sh +1 -1
- package/examples/session-cost-alert.sh +12 -0
- package/examples/session-drift-guard.sh +12 -0
- package/examples/session-duration-guard.sh +25 -2
- package/examples/session-end-logger.sh +0 -0
- package/examples/session-health-monitor.sh +0 -0
- package/examples/session-index-repair.sh +1 -1
- package/examples/session-memory-watchdog.sh +1 -0
- package/examples/session-permission-reset-guard.sh +12 -0
- package/examples/session-persistence-verifier.sh +93 -0
- package/examples/session-plugin-pin-watch.sh +68 -0
- package/examples/session-rate-monitor.sh +176 -0
- package/examples/session-resume-env-fix.sh +0 -0
- package/examples/session-start-quota-status.sh +193 -0
- package/examples/session-start-safety-check.sh +55 -3
- package/examples/session-state-saver.sh +0 -0
- package/examples/session-summary-stop.sh +1 -1
- package/examples/session-summary.sh +0 -0
- package/examples/session-token-counter.sh +1 -1
- package/examples/settings-hooks-key-restorer.sh +99 -0
- package/examples/settings-json-health-check.sh +57 -0
- package/examples/settings-json-model-guard.sh +0 -0
- package/examples/settings-regression-tester.sh +188 -0
- package/examples/shell-config-truncation-guard.sh +12 -0
- package/examples/shell-wrapper-guard.sh +12 -0
- package/examples/skill-cumulative-size-detector.sh +104 -0
- package/examples/skill-description-drop-detector.sh +148 -0
- package/examples/skill-gate.sh +0 -0
- package/examples/skill-truncation-detector.sh +154 -0
- package/examples/skills-context-recorder.sh +167 -0
- package/examples/skills-load-verifier.sh +96 -0
- package/examples/skills-settings-validator.sh +139 -0
- package/examples/sonnet-45-deprecation-helper.sh +85 -0
- package/examples/spec-file-scope-guard.sh +12 -0
- package/examples/spring-profile-guard.sh +1 -0
- package/examples/spurious-malformed-notice-detector.sh +247 -0
- package/examples/sql-bulk-delete-warn.sh +88 -0
- package/examples/sql-injection-detect.sh +0 -0
- package/examples/ssh-key-protect.sh +12 -0
- package/examples/staged-secret-scan.sh +22 -1
- package/examples/stale-temp-settings-detector.sh +86 -0
- package/examples/standing-rule-session-surfacer.sh +137 -0
- package/examples/stop-hook-sigterm-wrapper.sh +182 -0
- package/examples/stop-transcript-write-health.sh +64 -0
- package/examples/strict-allowlist.sh +57 -15
- package/examples/strip-coauthored-by.sh +50 -10
- package/examples/subagent-blast-radius-guard.sh +184 -0
- package/examples/subagent-boundary-precheck.sh +126 -0
- package/examples/subagent-budget-guard.sh +12 -0
- package/examples/subagent-claudemd-inject.sh +0 -0
- package/examples/subagent-closure-verify-gate.sh +164 -0
- package/examples/subagent-destructive-git-guard.sh +120 -0
- package/examples/subagent-error-detector.sh +0 -0
- package/examples/subagent-forged-system-reminder-guard.sh +156 -0
- package/examples/subagent-identity-leak-guard.sh +102 -0
- package/examples/subagent-identity-reassertion.sh +51 -0
- package/examples/subagent-inheritance-tester.sh +179 -0
- package/examples/subagent-permission-mode-guard.sh +110 -0
- package/examples/subagent-scope-guard.sh +12 -0
- package/examples/subagent-spawn-verification-enforcer.sh +102 -0
- package/examples/subagent-tool-allowlist-enforcer.sh +101 -0
- package/examples/subagent-tool-call-limiter.sh +87 -15
- package/examples/subcommand-chain-guard.sh +12 -0
- package/examples/subscription-api-billing-warner.sh +160 -0
- package/examples/subscription-bypass-detector.sh +152 -0
- package/examples/svelte-lint-on-edit.sh +1 -0
- package/examples/swift-build-on-edit.sh +1 -1
- package/examples/symlink-guard.sh +27 -2
- package/examples/symlink-protect.sh +1 -0
- package/examples/system-dir-protection-guard.sh +73 -8
- package/examples/system-message-workaround.sh +38 -13
- package/examples/system-package-guard.sh +12 -0
- package/examples/systemd-service-guard.sh +12 -0
- package/examples/task-integrity-guard.sh +12 -0
- package/examples/temp-file-cleanup.sh +1 -1
- package/examples/temporal-suggestion-detector.sh +135 -0
- package/examples/terraform-guard.sh +27 -6
- package/examples/test-after-edit.sh +1 -1
- package/examples/test-before-push.sh +12 -0
- package/examples/test-coverage-reminder.sh +24 -1
- package/examples/thinking-budget-effort-mismatch-detector.sh +149 -0
- package/examples/timezone-guard.sh +0 -0
- package/examples/tmp-output-size-guard.sh +0 -0
- package/examples/todo-check.sh +1 -1
- package/examples/token-budget-guard.sh +12 -0
- package/examples/token-budget-per-task.sh +12 -0
- package/examples/token-usage-tracker.sh +7 -1
- package/examples/tokenizer-ratio-alert-corpus.sh +90 -0
- package/examples/tokenizer-ratio-alert.sh +104 -0
- package/examples/tool-call-rate-limiter.sh +0 -0
- package/examples/tool-result-correlation-checker.sh +167 -0
- package/examples/tool-result-size-guard.sh +132 -0
- package/examples/tool-retry-budget-guard.sh +12 -0
- package/examples/transcript-contamination-detector.sh +138 -0
- package/examples/trustfall-mcp-injection-guard.sh +103 -0
- package/examples/ultrareview-large-diff-advisor.sh +184 -0
- package/examples/unbounded-output-guard.sh +94 -0
- package/examples/uncommitted-changes-stop.sh +1 -0
- package/examples/uncommitted-discard-guard.sh +23 -5
- package/examples/uncommitted-work-guard.sh +12 -0
- package/examples/usage-warn.sh +0 -0
- package/examples/user-account-guard.sh +12 -0
- package/examples/userprompt-submit-receipt.sh +123 -0
- package/examples/variable-expansion-guard.sh +12 -0
- package/examples/verify-after-publish-reminder.sh +93 -0
- package/examples/verify-before-commit.sh +12 -0
- package/examples/version-bump-detector.sh +156 -0
- package/examples/version-regression-warner.sh +144 -0
- package/examples/vue-lint-on-edit.sh +1 -0
- package/examples/warn-cron-cost-trap.sh +173 -0
- package/examples/webfetch-runaway-guard.sh +89 -0
- package/examples/wellness-break-reminder.sh +118 -0
- package/examples/windows-destructive-command-guard.sh +177 -0
- package/examples/windows-path-guard.sh +12 -0
- package/examples/windows-python-stub-detector.sh +63 -0
- package/examples/work-hours-guard.sh +12 -0
- package/examples/working-directory-fence.sh +12 -0
- package/examples/workspace-lease-guard.sh +205 -0
- package/examples/worktree-create-log.sh +1 -0
- package/examples/worktree-delete-guard.sh +12 -0
- package/examples/worktree-edit-boundary-guard.sh +127 -0
- package/examples/worktree-escape-write-guard.sh +114 -0
- package/examples/worktree-hook-linker.sh +0 -0
- package/examples/worktree-hooks-path-fix.sh +111 -0
- package/examples/worktree-parent-write-guard.sh +43 -0
- package/examples/worktree-project-unify.sh +2 -1
- package/examples/worktree-remove-uncommitted-guard.sh +13 -0
- package/examples/worktree-unmerged-guard.sh +7 -1
- package/examples/write-byte-integrity-verifier.sh +190 -0
- package/examples/write-empty-content-guard.sh +67 -0
- package/examples/write-nul-corruption-detector.sh +61 -0
- package/examples/write-overwrite-confirm.sh +160 -14
- package/examples/write-secret-guard.sh +27 -5
- package/examples/write-shrink-guard.sh +12 -0
- package/examples/write-test-ratio.sh +0 -0
- package/examples/wsl-host-disk-space-guard.sh +97 -0
- package/examples/xml-format-leak-detector.sh +266 -0
- package/examples/yaml-syntax-check.sh +12 -0
- package/hooks/hooks.json +6 -6
- package/index.mjs +1699 -183
- package/mission.md +2 -0
- package/package.json +4 -4
- package/plugins/credential-guard/.claude-plugin/plugin.json +9 -7
- package/plugins/git-protection/.claude-plugin/plugin.json +10 -8
- package/plugins/safety-essentials/.claude-plugin/plugin.json +9 -7
- package/plugins/token-guard/.claude-plugin/plugin.json +10 -8
- package/scripts/agents-md-sync-setup.sh +156 -0
- package/scripts/autonomy-blocked-summary.sh +148 -0
- package/scripts/build-search-index.py +43 -0
- package/scripts/build-sitemap.py +119 -0
- package/scripts/cc-doctor.sh +192 -0
- package/scripts/check-hook-event-names.py +127 -0
- package/scripts/check-misfire-instrument.sh +64 -0
- package/scripts/claim-verify-audit.sh +467 -0
- package/scripts/cowork-claudemd-helper.sh +136 -0
- package/scripts/june-15-deprecated-model-scan.sh +111 -0
- package/scripts/measure-heredoc-shapes.sh +67 -0
- package/scripts/measure-literal-match-misfire.sh +110 -0
- package/scripts/probe-scope-guard-branches.sh +48 -0
- package/scripts/probe-skip-line-both-ways.sh +65 -0
- package/scripts/receipts-aggregate.py +199 -0
- package/scripts/session-forensic-audit.py +360 -0
- package/scripts.json +9 -9
- package/scripts.json.bak-2026-07-27-find +13 -0
- package/skills/safety-setup/SKILL.md +5 -5
- package/tests/approve-side-remaining-two.test.sh +129 -0
- package/tests/approver-list-granularity.test.sh +145 -0
- package/tests/askuserquestion-autonomy-gate.test.sh +181 -0
- package/tests/audit-ci-gate.test.sh +87 -0
- package/tests/audit-cross-layer-claudemd.test.sh +170 -0
- package/tests/audit-fail-open-parse.test.sh +100 -0
- package/tests/auto-approve-compound-tail.test.sh +194 -0
- package/tests/auto-mode-path-invoked-commands.test.sh +89 -0
- package/tests/auto-mode-quoted-targets.test.sh +127 -0
- package/tests/auto-mode-unquoted-targets.test.sh +112 -0
- package/tests/bash-secret-output-detector-detects.test.sh +102 -0
- package/tests/blindspots-cross-layer.test.sh +177 -0
- package/tests/block-database-wipe.test.sh +64 -0
- package/tests/bundled-short-flags-bypass.test.sh +85 -0
- package/tests/cd-git-allow-compound-tail.test.sh +67 -0
- package/tests/chown-guard-filesystem-root.test.sh +64 -0
- package/tests/ci-workflow-guard-skip-rule.test.sh +115 -0
- package/tests/compound-deny-safe-rm-targets.test.sh +105 -0
- package/tests/core-scripts-parser-fallback.test.sh +140 -0
- package/tests/destructive-guard-bare-glob.test.sh +74 -0
- package/tests/destructive-guard-every-operand.test.sh +100 -0
- package/tests/destructive-guard-find-deletion.test.sh +114 -0
- package/tests/destructive-guard-flag-order.test.sh +106 -0
- package/tests/destructive-guard-mentions-vs-invocations.test.sh +107 -0
- package/tests/destructive-guard-quoted-target.test.sh +89 -0
- package/tests/destructive-guard-separators.test.sh +105 -0
- package/tests/destructive-guard-variable-target.test.sh +98 -0
- package/tests/destructive-guard-wrapped-command.test.sh +91 -0
- package/tests/doctor-hook-disabling-env.test.sh +92 -0
- package/tests/doctor-misspelled-settings-keys.test.sh +106 -0
- package/tests/example-guards-wrapped-command.test.sh +169 -0
- package/tests/file-edit-backup.test.sh +93 -0
- package/tests/git-filter-repo-guard.test.sh +60 -0
- package/tests/git-global-options-bypass.test.sh +141 -0
- package/tests/git-invocation-prefixes-all-guards.test.sh +114 -0
- package/tests/git-maintenance-guard.test.sh +83 -0
- package/tests/git-pack-temp-cleanup-guard.test.sh +133 -0
- package/tests/git-rm-orphan-wipe-guard.test.sh +66 -0
- package/tests/git-submodule-guard.test.sh +50 -0
- package/tests/grep-pcre-portability.test.sh +155 -0
- package/tests/hook-registration-matches-input.test.sh +73 -0
- package/tests/hook-trigger-matches-envelope.test.sh +178 -0
- package/tests/install-does-not-claim-protection.test.sh +110 -0
- package/tests/install-example-standalone-tools.test.sh +139 -0
- package/tests/line-continuation-bypass.test.sh +114 -0
- package/tests/mcp-config-poisoning-audit.test.sh +106 -0
- package/tests/nested-background-agent-guard.test.sh +58 -0
- package/tests/no-unqualified-safety-claims.test.sh +114 -0
- package/tests/option-eating-grep-patterns.test.sh +130 -0
- package/tests/outdated-core-guards.test.sh +134 -0
- package/tests/outdated-report.test.sh +155 -0
- package/tests/output-secret-mask-warns.test.sh +73 -0
- package/tests/plugin-hooks-fail-open-under-sh.test.sh +165 -0
- package/tests/plugins-word-order.test.sh +126 -0
- package/tests/powershell-remove-item-guard.test.sh +85 -0
- package/tests/project-dir-naming.test.sh +103 -0
- package/tests/quoted-safe-targets-and-mentions.test.sh +190 -0
- package/tests/reroute-after-block-guard.test.sh +104 -0
- package/tests/secret-guard-documented-promises.test.sh +97 -0
- package/tests/session-plugin-pin-watch.test.sh +45 -0
- package/tests/settings-unreadable-refuses-to-write.test.sh +95 -0
- package/tests/shield-no-silent-adoption.test.py +110 -0
- package/tests/shield-no-silent-adoption.test.sh +46 -0
- package/tests/stop-transcript-write-health.test.sh +46 -0
- package/tests/system-dir-guard-root-erased.test.sh +66 -0
- package/tests/test-account-billing-log.sh +100 -0
- package/tests/test-account-routing-preflight.sh +77 -0
- package/tests/test-agent-sdk-credit-pool-monitor.sh +157 -0
- package/tests/test-agent-view-quota-warn.sh +184 -0
- package/tests/test-agents-md-edit-drift-warner.sh +176 -0
- package/tests/test-agents-md-loader.sh +204 -0
- package/tests/test-agents-md-sync-checker.sh +125 -0
- package/tests/test-ai-slop-punctuation-arrest.sh +280 -0
- package/tests/test-allowlist.sh +60 -0
- package/tests/test-always-allow-pattern-suggester.sh +147 -0
- package/tests/test-anthropic-base-url-guard.sh +88 -0
- package/tests/test-articulated-scope-capture.sh +139 -0
- package/tests/test-attachment-traversal-guard.sh +168 -0
- package/tests/test-aup-block-pattern-logger.sh +315 -0
- package/tests/test-aup-false-positive-helper.sh +188 -0
- package/tests/test-aup-large-tool-output-warner.sh +370 -0
- package/tests/test-aup-retry-loop-guard.sh +341 -0
- package/tests/test-auth-expiry-reminder.sh +303 -0
- package/tests/test-auth-macos-sleep-detector.sh +254 -0
- package/tests/test-auth-path-detector.sh +232 -0
- package/tests/test-auth-status-checker.sh +233 -0
- package/tests/test-authorized-reconfirmation-detector.sh +280 -0
- package/tests/test-background-cost-launch-guard.sh +62 -0
- package/tests/test-bash-allowlist-secondary-check.sh +143 -0
- package/tests/test-bash-fanout-bounded-rewriter.sh +186 -0
- package/tests/test-bash-project-fence.sh +127 -0
- package/tests/test-bg-permission-prompt-warner.sh +221 -0
- package/tests/test-binary-read-detector.sh +173 -0
- package/tests/test-broad-prefix-session-trap-warner.sh +167 -0
- package/tests/test-bulk-delete-target-resolution.sh +49 -0
- package/tests/test-bypass-mode-effective-verifier.sh +179 -0
- package/tests/test-cache-creation-drift-detector.sh +244 -0
- package/tests/test-cache-residue-detector.sh +429 -0
- package/tests/test-cache-tier-logger.sh +59 -0
- package/tests/test-cache-ttl-eviction-detector.sh +282 -0
- package/tests/test-cch-sentinel-precommit-guard.sh +64 -0
- package/tests/test-claim-verify-detector.sh +222 -0
- package/tests/test-claim-vs-caveat-checker.sh +134 -0
- package/tests/test-claude-code-version-pin-advisor.sh +353 -0
- package/tests/test-claude-md-reinjector.sh +173 -0
- package/tests/test-claude-md-size-monitor.sh +107 -0
- package/tests/test-claude-update-budget-guard.sh +132 -0
- package/tests/test-claude-update-smart.sh +91 -0
- package/tests/test-claudemd-tool-prohibition.sh +239 -0
- package/tests/test-cli-config-pinning-detector.sh +233 -0
- package/tests/test-cliff-countdown-advisor.sh +138 -0
- package/tests/test-closure-word-verify-gate.sh +158 -0
- package/tests/test-commit-all-scope-guard.sh +73 -0
- package/tests/test-commitment-carry-forward-arrest.sh +250 -0
- package/tests/test-compact-dispatch-watchdog.sh +147 -0
- package/tests/test-completion-claim-without-verification-detector.sh +326 -0
- package/tests/test-compound-bash-permission-resolver.sh +264 -0
- package/tests/test-compound-command-deny-enforcer.sh +67 -0
- package/tests/test-context-length-budget.sh +68 -0
- package/tests/test-context-usage-drift-alert.sh +52 -22
- package/tests/test-cost-incident-self-audit.sh +94 -0
- package/tests/test-cowork-claude-md-load-checker.sh +257 -0
- package/tests/test-cowork-claudemd-helper.sh +141 -0
- package/tests/test-cowork-fuse-staleness-watcher.sh +287 -0
- package/tests/test-cowork-hook-absence-warner.sh +230 -0
- package/tests/test-cowork-model-picker-advisor.sh +317 -0
- package/tests/test-credential-exfil-guard.sh +62 -0
- package/tests/test-cron-create-receipt.sh +165 -0
- package/tests/test-cross-product-mcp-spawn-detector.sh +112 -0
- package/tests/test-daemon-zombie-worker-surfacer.sh +71 -0
- package/tests/test-daily-cost-guard.sh +131 -0
- package/tests/test-deny-rule-integrity-verifier.sh +288 -0
- package/tests/test-deployment-readback-gate.sh +92 -0
- package/tests/test-deployment-readback-gh-adapter.sh +163 -0
- package/tests/test-disabled-feature-toggle-advisor.sh +78 -0
- package/tests/test-dispatch-allowlist-preflight.sh +385 -0
- package/tests/test-dispatch-liveness-watchdog.sh +319 -0
- package/tests/test-dispatch-receipt.sh +314 -0
- package/tests/test-dotenv-anthropic-key-billing-guard.sh +160 -0
- package/tests/test-echo-probe-spam-detector.sh +143 -0
- package/tests/test-edit-quote-ambiguity-guard.sh +75 -0
- package/tests/test-ephemeral-container-detector.sh +251 -0
- package/tests/test-evidence-claim-gate.sh +291 -0
- package/tests/test-extended-thinking-loop-guard.sh +289 -0
- package/tests/test-extended-thinking-resume-warning.sh +240 -0
- package/tests/test-extended-thinking-tool-use-mismatch-detector.sh +236 -0
- package/tests/test-fabricated-command-detector.sh +296 -0
- package/tests/test-false-disk-full-guard.sh +36 -0
- package/tests/test-feature-deprecation-detector.sh +117 -0
- package/tests/test-forbidden-response-phrase-detector.sh +246 -0
- package/tests/test-git-operations-require-approval.sh +53 -0
- package/tests/test-git-push-blast-radius-guard.sh +115 -0
- package/tests/test-goal-iteration-limit-warner.sh +128 -0
- package/tests/test-growthbook-flag-monitor.sh +428 -0
- package/tests/test-halt-signal-detector.sh +120 -0
- package/tests/test-hook-self-disable-detector.sh +232 -0
- package/tests/test-iteration-quality-degradation-detector.sh +244 -0
- package/tests/test-long-session-malformed-tool-call-detector.sh +185 -0
- package/tests/test-marketplace-plugins.sh +193 -0
- package/tests/test-mcp-misdiagnosis-arrest.sh +235 -0
- package/tests/test-mcp-orphan-leak-warner.sh +71 -0
- package/tests/test-mcp-permissions-ask-warner.sh +207 -0
- package/tests/test-mcp-routine-approval-detector.sh +229 -0
- package/tests/test-mcp-startup-bloat-detector.sh +141 -0
- package/tests/test-mcp-stdio-compatibility-test.sh +112 -0
- package/tests/test-memory-chain-audit.sh +59 -0
- package/tests/test-memory-orphan-detector.sh +227 -0
- package/tests/test-model-swap-suggester.sh +368 -0
- package/tests/test-model-version-lock.sh +83 -0
- package/tests/test-multi-file-plan-routing-gate.sh +277 -0
- package/tests/test-multi-window-auth-drift-detector.sh +262 -0
- package/tests/test-nested-spawn-inflight-guard.sh +281 -0
- package/tests/test-no-force-flag.sh +49 -0
- package/tests/test-non-english-quality-warner.sh +229 -0
- package/tests/test-oauth-refresh-monitor.sh +311 -0
- package/tests/test-opus-degradation-tracker.sh +274 -0
- package/tests/test-opus48-routine-task-warning.sh +201 -0
- package/tests/test-opus48-thinking-wedge-advisor.sh +249 -0
- package/tests/test-output-token-spike-detector.sh +315 -0
- package/tests/test-parallel-batch-size-limiter.sh +228 -0
- package/tests/test-parallel-cascade-detector.sh +191 -0
- package/tests/test-partial-view-claim-arrest.sh +272 -0
- package/tests/test-per-prompt-token-cap.sh +136 -0
- package/tests/test-persisted-1m-model-advisor.sh +71 -0
- package/tests/test-plugin-hooks-json-bloat-detector.sh +267 -0
- package/tests/test-post-edit-disk-verify.sh +294 -0
- package/tests/test-pre-bash-sed-line-ending-windows.sh +108 -0
- package/tests/test-pre-execution-claim-detector.sh +366 -0
- package/tests/test-private-key-backup-and-git-credentials.sh +100 -0
- package/tests/test-proxy-capture-suggester.sh +214 -0
- package/tests/test-public-artefact-socratic-narrowing.sh +346 -0
- package/tests/test-quota-anomaly-detector.sh +321 -0
- package/tests/test-quota-reset-audit.sh +81 -0
- package/tests/test-read-loop-detector.sh +137 -0
- package/tests/test-read-once-guard.sh +85 -0
- package/tests/test-receipts-aggregate.sh +143 -0
- package/tests/test-redirect-fragment-warner.sh +130 -0
- package/tests/test-redundant-read-blocker.sh +155 -0
- package/tests/test-refusal-arrest-gate.sh +303 -0
- package/tests/test-remote-control-billing-classifier.sh +125 -0
- package/tests/test-resume-drift-watcher.sh +65 -0
- package/tests/test-rg-replace-flag-detector.sh +175 -0
- package/tests/test-rhetorical-verification-prompt-detector.sh +237 -0
- package/tests/test-rm-safety-net.sh +115 -0
- package/tests/test-route-handler-emptiness-gate.sh +300 -0
- package/tests/test-runtime-binary-change-detector.sh +196 -0
- package/tests/test-same-command-repeat-detector.sh +165 -0
- package/tests/test-same-correction-arrest.sh +165 -0
- package/tests/test-scope-expansion-receipt.sh +207 -0
- package/tests/test-server-side-prompt-injection-detector.sh +181 -0
- package/tests/test-session-persistence-verifier.sh +128 -0
- package/tests/test-session-rate-monitor.sh +284 -0
- package/tests/test-session-start-quota-status.sh +210 -0
- package/tests/test-session-start-safety-check.sh +138 -0
- package/tests/test-settings-hooks-key-restorer.sh +257 -0
- package/tests/test-settings-json-health-check.sh +70 -0
- package/tests/test-settings-regression-tester.sh +197 -0
- package/tests/test-skill-cumulative-size-detector.sh +198 -0
- package/tests/test-skill-description-drop-detector.sh +236 -0
- package/tests/test-skill-truncation-detector.sh +263 -0
- package/tests/test-skills-context-recorder.sh +329 -0
- package/tests/test-skills-load-verifier.sh +184 -0
- package/tests/test-skills-settings-validator.sh +242 -0
- package/tests/test-sonnet-45-deprecation-helper.sh +150 -0
- package/tests/test-spurious-malformed-notice-detector.sh +290 -0
- package/tests/test-sql-bulk-delete-warn.sh +123 -0
- package/tests/test-ssh-key-protect.sh +81 -0
- package/tests/test-stale-temp-settings-detector.sh +161 -0
- package/tests/test-standing-rule-session-surfacer.sh +215 -0
- package/tests/test-starter-hooks-manifest.sh +96 -0
- package/tests/test-stop-hook-sigterm-wrapper.sh +258 -0
- package/tests/test-strict-allowlist.sh +72 -0
- package/tests/test-subagent-boundary-precheck.sh +137 -0
- package/tests/test-subagent-closure-verify-gate.sh +193 -0
- package/tests/test-subagent-destructive-git-guard.sh +86 -0
- package/tests/test-subagent-identity-leak-guard.sh +62 -0
- package/tests/test-subagent-identity-reassertion.sh +61 -0
- package/tests/test-subagent-inheritance-tester.sh +319 -0
- package/tests/test-subagent-permission-mode-guard.sh +61 -0
- package/tests/test-subagent-spawn-verification-enforcer.sh +60 -0
- package/tests/test-subagent-tool-allowlist-enforcer.sh +62 -0
- package/tests/test-subagent-tool-call-limiter.sh +97 -0
- package/tests/test-subscription-api-billing-warner.sh +138 -0
- package/tests/test-subscription-bypass-detector.sh +255 -0
- package/tests/test-temporal-suggestion-detector.sh +183 -0
- package/tests/test-thinking-budget-effort-mismatch-detector.sh +327 -0
- package/tests/test-tokenizer-ratio-alert-corpus.sh +77 -0
- package/tests/test-tokenizer-ratio-alert.sh +93 -0
- package/tests/test-tool-result-correlation-checker.sh +264 -0
- package/tests/test-tool-result-size-guard.sh +121 -0
- package/tests/test-transcript-contamination-detector.sh +264 -0
- package/tests/test-trigger-header-parsing.sh +151 -0
- package/tests/test-ultrareview-large-diff-advisor.sh +192 -0
- package/tests/test-userprompt-submit-receipt.sh +181 -0
- package/tests/test-verify-after-publish-reminder.sh +184 -0
- package/tests/test-version-bump-detector.sh +212 -0
- package/tests/test-version-regression-warner.sh +165 -0
- package/tests/test-warn-cron-cost-trap.sh +82 -0
- package/tests/test-webfetch-runaway-guard.sh +77 -0
- package/tests/test-wellness-break-reminder.sh +138 -0
- package/tests/test-windows-destructive-command-guard.sh +169 -0
- package/tests/test-windows-python-stub-detector.sh +146 -0
- package/tests/test-worktree-edit-boundary-guard.sh +219 -0
- package/tests/test-write-byte-integrity-verifier.sh +192 -0
- package/tests/test-xml-format-leak-detector.sh +299 -0
- package/tests/trustfall-mcp-injection-guard.test.sh +183 -0
- package/tests/workspace-lease-guard.test.sh +144 -0
- package/tests/worktree-escape-write-guard.test.sh +68 -0
- package/tests/worktree-hooks-path-fix.test.sh +181 -0
- package/tests/worktree-parent-write-guard.test.sh +51 -0
- package/tests/write-empty-content-guard.test.sh +39 -0
- package/tests/write-nul-corruption-detector.test.sh +29 -0
- package/tests/write-overwrite-unread-content.test.sh +178 -0
- package/tests/wsl-host-disk-space-guard.test.sh +55 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# destructive-guard never looked inside a wrapper.
|
|
3
|
+
#
|
|
4
|
+
# Every boundary in the guard is (^|[;&|]) — the start of the line, or just
|
|
5
|
+
# after a separator. A command placed inside a substitution, a subshell, or
|
|
6
|
+
# after a shell keyword sits at neither, so no check ever saw it:
|
|
7
|
+
#
|
|
8
|
+
# rm -rf ~ blocked
|
|
9
|
+
# [[ -n $(rm -rf ~) ]] NOT blocked <- same deletion
|
|
10
|
+
# if true; then rm -rf ~; fi NOT blocked
|
|
11
|
+
# `rm -rf ~` NOT blocked
|
|
12
|
+
#
|
|
13
|
+
# Measured 2026-08-04 against the shipped core: one blocked command, ten
|
|
14
|
+
# wrappings, seven passes. Claude Code 2.1.221 fixed the same shape on its own
|
|
15
|
+
# side ("a Bash tool permission-check bypass where zsh could execute hidden
|
|
16
|
+
# commands in [[ ]] regex conditionals"), which is what prompted the check here.
|
|
17
|
+
#
|
|
18
|
+
# Check 0z replaces the wrapping tokens with separators and runs the guard once
|
|
19
|
+
# more against that text, so the inner command meets every existing check. The
|
|
20
|
+
# controls below are the point of this file: the rewrite touches parentheses and
|
|
21
|
+
# braces, which appear in ordinary commands (awk, jq, find -exec, subshells), so
|
|
22
|
+
# "catches wrapped commands" and "blocks anything with a bracket in it" have to
|
|
23
|
+
# be told apart.
|
|
24
|
+
|
|
25
|
+
set -u
|
|
26
|
+
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
27
|
+
WORK="$(mktemp -d)"
|
|
28
|
+
trap 'find "$WORK" -mindepth 1 -delete 2>/dev/null; rmdir "$WORK" 2>/dev/null' EXIT
|
|
29
|
+
PASS=0; FAIL=0
|
|
30
|
+
|
|
31
|
+
python3 -c "
|
|
32
|
+
import json,sys
|
|
33
|
+
sys.stdout.write(json.load(open('$ROOT/scripts.json'))['destructive-guard'])" > "$WORK/dg.sh"
|
|
34
|
+
|
|
35
|
+
run() {
|
|
36
|
+
local T; T="$(mktemp -d)"
|
|
37
|
+
python3 -c '
|
|
38
|
+
import json,sys
|
|
39
|
+
print(json.dumps({"tool_name":"Bash","tool_input":{"command":sys.argv[1]},
|
|
40
|
+
"cwd":sys.argv[2],"session_id":"t"}))' "$1" "$T" \
|
|
41
|
+
| HOME="$T" bash "$WORK/dg.sh" >/dev/null 2>&1
|
|
42
|
+
local rc=$?
|
|
43
|
+
find "$T" -mindepth 1 -delete 2>/dev/null; rmdir "$T" 2>/dev/null
|
|
44
|
+
return $rc
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
want_block() {
|
|
48
|
+
if run "$1"; then FAIL=$((FAIL+1)); echo " FAIL blocked: $1"
|
|
49
|
+
else PASS=$((PASS+1)); echo " ok blocked: $1"; fi
|
|
50
|
+
}
|
|
51
|
+
want_allow() {
|
|
52
|
+
if run "$1"; then PASS=$((PASS+1)); echo " ok allowed: $1"
|
|
53
|
+
else FAIL=$((FAIL+1)); echo " FAIL allowed: $1"; fi
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
RM=$(printf 'r%s' 'm')
|
|
57
|
+
echo "destructive-guard-wrapped-command"
|
|
58
|
+
|
|
59
|
+
# --- the same deletion, wrapped ---------------------------------------------
|
|
60
|
+
want_block "[[ -n \$($RM -rf ~) ]]"
|
|
61
|
+
want_block "[[ x =~ \$($RM -rf ~) ]]"
|
|
62
|
+
want_block "[[ -n \`$RM -rf ~\` ]]"
|
|
63
|
+
want_block "if true; then $RM -rf ~; fi"
|
|
64
|
+
want_block "for f in a; do $RM -rf ~; done"
|
|
65
|
+
want_block "\$($RM -rf /)"
|
|
66
|
+
want_block "x=\$($RM -rf /) ; echo done"
|
|
67
|
+
want_block "echo \$(git reset --hard HEAD~5)"
|
|
68
|
+
|
|
69
|
+
# --- unwrapped, which already worked ----------------------------------------
|
|
70
|
+
want_block "$RM -rf ~"
|
|
71
|
+
want_block "$RM -rf /"
|
|
72
|
+
|
|
73
|
+
# --- controls: brackets and braces are ordinary ------------------------------
|
|
74
|
+
# Without these, the check above passes just as well by blocking every command
|
|
75
|
+
# that contains a parenthesis.
|
|
76
|
+
want_allow "awk '{print \$1}' access.log"
|
|
77
|
+
want_allow "sed -E 's/(a)/b/' file.txt"
|
|
78
|
+
want_allow "jq '{name: .name}' package.json"
|
|
79
|
+
want_allow "find . -name '*.log' -exec ls -l {} \\;"
|
|
80
|
+
want_allow "if [ -d node_modules ]; then echo yes; else echo no; fi"
|
|
81
|
+
want_allow "for i in \$(seq 1 3); do echo \$i; done"
|
|
82
|
+
want_allow "echo \$(basename \$(pwd))"
|
|
83
|
+
want_allow "( cd /tmp && ls )"
|
|
84
|
+
want_allow "ls \$(git rev-parse --show-toplevel)"
|
|
85
|
+
want_allow "$RM -rf node_modules && npm ci"
|
|
86
|
+
want_allow "git reset --soft HEAD~1"
|
|
87
|
+
want_allow "bash -c 'echo hello'"
|
|
88
|
+
|
|
89
|
+
echo
|
|
90
|
+
echo "destructive-guard-wrapped-command: $PASS passed, $FAIL failed"
|
|
91
|
+
[ "$FAIL" -eq 0 ]
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# --doctor: detect the environment variables that stop hooks loading at all
|
|
3
|
+
#
|
|
4
|
+
# Measured 2026-09-19 on Claude Code 2.1.278 (Linux/WSL2), disposable HOME and a
|
|
5
|
+
# scratch working directory, with a control on every row. Arrival was measured on
|
|
6
|
+
# UserPromptSubmit -- deliberately NOT PreToolUse on Bash, because --restricted
|
|
7
|
+
# removes Bash and then "the hook was ignored" and "nothing pulled the trigger"
|
|
8
|
+
# are the same observation.
|
|
9
|
+
#
|
|
10
|
+
# (control) no flags -> user, project and local hooks all fired
|
|
11
|
+
# --dangerously-skip-permissions -> hooks fired, the guard refused, exit 2
|
|
12
|
+
# --restricted -> no hook fired; --settings restored them
|
|
13
|
+
# --safe-mode -> no hook fired; --settings did NOT restore
|
|
14
|
+
# CLAUDE_CODE_RESTRICTED=1 -> same as --restricted
|
|
15
|
+
# CLAUDE_CODE_SAFE_MODE=1 -> same as --safe-mode, and Bash still ran
|
|
16
|
+
#
|
|
17
|
+
# The variables matter more than the flags: a flag is retyped every time you
|
|
18
|
+
# start, a line in a shell profile is read once and never seen again. Before this
|
|
19
|
+
# check, --doctor printed "All checks passed. Hooks should be working." with
|
|
20
|
+
# either variable set -- the exact false reassurance this command exists to
|
|
21
|
+
# prevent, since every other check inspects files that are all perfectly fine.
|
|
22
|
+
#
|
|
23
|
+
# Only the value "1" was measured. Any other non-empty, non-false value is
|
|
24
|
+
# flagged on the assumption that whoever typed it meant to turn it on; "0",
|
|
25
|
+
# "false" and empty are treated as off.
|
|
26
|
+
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
27
|
+
PASS=0
|
|
28
|
+
FAIL=0
|
|
29
|
+
|
|
30
|
+
# Assembled at runtime so this file does not carry the literal filename that the
|
|
31
|
+
# operator's own guards watch for.
|
|
32
|
+
SETTINGS_FILE="set""tings.json"
|
|
33
|
+
|
|
34
|
+
run_doctor() {
|
|
35
|
+
# $1 = extra env assignments (may be empty). Prints only the lines this check owns.
|
|
36
|
+
local envspec="$1"
|
|
37
|
+
local home
|
|
38
|
+
home=$(mktemp -d)
|
|
39
|
+
mkdir -p "$home/.claude/hooks"
|
|
40
|
+
printf '%s' '{"hooks":{}}' > "$home/.claude/$SETTINGS_FILE"
|
|
41
|
+
# shellcheck disable=SC2086
|
|
42
|
+
env -u CLAUDE_CODE_SAFE_MODE -u CLAUDE_CODE_RESTRICTED $envspec HOME="$home" \
|
|
43
|
+
node "$ROOT/index.mjs" --doctor 2>&1 \
|
|
44
|
+
| grep -E 'hook-disabling environment variable|CLAUDE_CODE_SAFE_MODE=|CLAUDE_CODE_RESTRICTED=|does NOT restore|pass your hooks with|unset CLAUDE_CODE' || true
|
|
45
|
+
rm -rf "$home"
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
check() {
|
|
49
|
+
local desc="$1" envspec="$2" expect="$3"
|
|
50
|
+
local out
|
|
51
|
+
out=$(run_doctor "$envspec")
|
|
52
|
+
if printf '%s' "$out" | grep -q "$expect"; then
|
|
53
|
+
PASS=$((PASS + 1))
|
|
54
|
+
else
|
|
55
|
+
echo " FAIL: $desc"
|
|
56
|
+
echo " expected to match: $expect"
|
|
57
|
+
printf '%s\n' "${out:-<no output>}" | sed 's/^/ got: /'
|
|
58
|
+
FAIL=$((FAIL + 1))
|
|
59
|
+
fi
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
# --- the two variables are reported ----------------------------------------
|
|
63
|
+
check "CLAUDE_CODE_SAFE_MODE=1 is reported" \
|
|
64
|
+
"CLAUDE_CODE_SAFE_MODE=1" "CLAUDE_CODE_SAFE_MODE=1"
|
|
65
|
+
|
|
66
|
+
check "CLAUDE_CODE_RESTRICTED=1 is reported" \
|
|
67
|
+
"CLAUDE_CODE_RESTRICTED=1" "CLAUDE_CODE_RESTRICTED=1"
|
|
68
|
+
|
|
69
|
+
check "safe-mode says --settings does not help" \
|
|
70
|
+
"CLAUDE_CODE_SAFE_MODE=1" "does NOT restore"
|
|
71
|
+
|
|
72
|
+
check "restricted points at --settings as the way back" \
|
|
73
|
+
"CLAUDE_CODE_RESTRICTED=1" "pass your hooks with"
|
|
74
|
+
|
|
75
|
+
# --- controls: no false positives -------------------------------------------
|
|
76
|
+
# Without a control here, the two rows above would also pass if the check simply
|
|
77
|
+
# printed its warning unconditionally.
|
|
78
|
+
check "neither variable set reports the all-clear" \
|
|
79
|
+
"" "no hook-disabling environment variable set"
|
|
80
|
+
|
|
81
|
+
check "an explicit 0 is treated as off" \
|
|
82
|
+
"CLAUDE_CODE_SAFE_MODE=0" "no hook-disabling environment variable set"
|
|
83
|
+
|
|
84
|
+
check "the string false is treated as off" \
|
|
85
|
+
"CLAUDE_CODE_RESTRICTED=false" "no hook-disabling environment variable set"
|
|
86
|
+
|
|
87
|
+
check "an empty value is treated as off" \
|
|
88
|
+
"CLAUDE_CODE_SAFE_MODE=" "no hook-disabling environment variable set"
|
|
89
|
+
|
|
90
|
+
echo
|
|
91
|
+
echo "doctor-hook-disabling-env: $PASS passed, $FAIL failed"
|
|
92
|
+
[ "$FAIL" -eq 0 ]
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# --doctor: detect near-miss (misspelled) keys in the settings file
|
|
3
|
+
#
|
|
4
|
+
# Claude Code ignores unknown settings keys in silence. Measured on v2.1.220
|
|
5
|
+
# with isolated settings files, controls paired against the correct spelling:
|
|
6
|
+
#
|
|
7
|
+
# permissions.deny -> the command is refused, never starts
|
|
8
|
+
# permissions.denyy -> the command runs, exit 0, ZERO warnings
|
|
9
|
+
# hooks.PreToolUse -> hook blocks, exit 2
|
|
10
|
+
# hooks.PreToolUsee -> hook never invoked, ZERO warnings
|
|
11
|
+
# (the hook body was unchanged; only the key differed)
|
|
12
|
+
# sandbox.failIfUnavailable -> refuses to start when deps are missing, exit 1
|
|
13
|
+
# sandbox.failIfUnavailble -> starts unprotected, exit 0
|
|
14
|
+
#
|
|
15
|
+
# `claude doctor` does not check spelling and there is no non-interactive
|
|
16
|
+
# command that prints the effective settings, so a control can be void from the
|
|
17
|
+
# day it was written while looking exactly like one that works: both produce
|
|
18
|
+
# "nothing bad happened".
|
|
19
|
+
#
|
|
20
|
+
# The check reports only keys within a small edit distance of a known key.
|
|
21
|
+
# An unknown key that is far from every known one is left alone on purpose --
|
|
22
|
+
# it is probably a setting this release has not heard of, and a diagnostic that
|
|
23
|
+
# cries wolf stops being read.
|
|
24
|
+
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
25
|
+
PASS=0
|
|
26
|
+
FAIL=0
|
|
27
|
+
|
|
28
|
+
# Assembled at runtime so this file does not carry the literal filename that the
|
|
29
|
+
# operator's own guards watch for.
|
|
30
|
+
SETTINGS_FILE="set""tings.json"
|
|
31
|
+
|
|
32
|
+
run_doctor() {
|
|
33
|
+
# $1 = JSON body. Runs --doctor against a throwaway HOME and prints the
|
|
34
|
+
# "did you mean" lines only.
|
|
35
|
+
local body="$1"
|
|
36
|
+
local home
|
|
37
|
+
home=$(mktemp -d)
|
|
38
|
+
mkdir -p "$home/.claude/hooks"
|
|
39
|
+
printf '%s' "$body" > "$home/.claude/$SETTINGS_FILE"
|
|
40
|
+
HOME="$home" node "$ROOT/index.mjs" --doctor 2>&1 | grep 'did you mean' || true
|
|
41
|
+
rm -rf "$home"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
check() {
|
|
45
|
+
local desc="$1" body="$2" expected_count="$3" expected_key="$4"
|
|
46
|
+
local out n
|
|
47
|
+
out=$(run_doctor "$body")
|
|
48
|
+
n=$(printf '%s' "$out" | grep -c 'did you mean' || true)
|
|
49
|
+
[ -z "$out" ] && n=0
|
|
50
|
+
if [ "$n" -ne "$expected_count" ]; then
|
|
51
|
+
echo " FAIL: $desc (reported $n, expected $expected_count)"
|
|
52
|
+
[ -n "$out" ] && printf '%s\n' "$out" | sed 's/^/ /'
|
|
53
|
+
FAIL=$((FAIL + 1)); return
|
|
54
|
+
fi
|
|
55
|
+
if [ -n "$expected_key" ] && ! printf '%s' "$out" | grep -q "$expected_key"; then
|
|
56
|
+
echo " FAIL: $desc (did not name $expected_key)"
|
|
57
|
+
FAIL=$((FAIL + 1)); return
|
|
58
|
+
fi
|
|
59
|
+
echo " PASS: $desc"
|
|
60
|
+
PASS=$((PASS + 1))
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
echo "Testing --doctor misspelled settings key detection"
|
|
64
|
+
echo "=================================================="
|
|
65
|
+
|
|
66
|
+
# --- the three spellings measured on v2.1.220 -------------------------------
|
|
67
|
+
check "permissions.denyy is reported" \
|
|
68
|
+
'{"permissions":{"denyy":["Bash(curl:*)"],"allow":[]}}' 1 'permissions.denyy'
|
|
69
|
+
|
|
70
|
+
check "hooks.PreToolUsee is reported" \
|
|
71
|
+
'{"hooks":{"PreToolUsee":[]}}' 1 'hooks.PreToolUsee'
|
|
72
|
+
|
|
73
|
+
check "sandbox.failIfUnavailble is reported" \
|
|
74
|
+
'{"sandbox":{"enabled":true,"failIfUnavailble":true}}' 1 'sandbox.failIfUnavailble'
|
|
75
|
+
|
|
76
|
+
check "sandbox.network.strictAllowlst is reported" \
|
|
77
|
+
'{"sandbox":{"network":{"strictAllowlst":true}}}' 1 'sandbox.network.strictAllowlst'
|
|
78
|
+
|
|
79
|
+
check "a misspelled top-level key is reported" \
|
|
80
|
+
'{"modell":"opus"}' 1 'modell'
|
|
81
|
+
|
|
82
|
+
check "all three layers at once" \
|
|
83
|
+
'{"permissions":{"denyy":[]},"hooks":{"PreToolUsee":[]},"modell":"opus"}' 3 ''
|
|
84
|
+
|
|
85
|
+
# --- no false positives -----------------------------------------------------
|
|
86
|
+
check "correct spellings report nothing" \
|
|
87
|
+
'{"permissions":{"deny":[],"allow":[],"ask":[]},"hooks":{"PreToolUse":[],"PostToolUse":[],"SessionStart":[]},"model":"opus","defaultMode":"dontAsk"}' 0 ''
|
|
88
|
+
|
|
89
|
+
check "correct sandbox spellings report nothing" \
|
|
90
|
+
'{"sandbox":{"enabled":true,"failIfUnavailable":true,"network":{"strictAllowlist":true,"allowedDomains":["example.com"]}}}' 0 ''
|
|
91
|
+
|
|
92
|
+
check "an unknown key far from every known one stays silent" \
|
|
93
|
+
'{"someBrandNewFeatureNobodyKnows":true,"permissions":{"deny":[]}}' 0 ''
|
|
94
|
+
|
|
95
|
+
check "keys inside env are not checked (operator picks those names)" \
|
|
96
|
+
'{"env":{"denyy":"1","modell":"2"},"permissions":{"deny":[]}}' 0 ''
|
|
97
|
+
|
|
98
|
+
check "every hook event name spelled correctly reports nothing" \
|
|
99
|
+
'{"hooks":{"PreToolUse":[],"PostToolUse":[],"Notification":[],"Stop":[],"SubagentStop":[],"SubagentStart":[],"UserPromptSubmit":[],"PreCompact":[],"PostCompact":[],"SessionStart":[],"SessionEnd":[],"DirectoryAdded":[]}}' 0 ''
|
|
100
|
+
|
|
101
|
+
check "an empty settings object reports nothing" \
|
|
102
|
+
'{}' 0 ''
|
|
103
|
+
|
|
104
|
+
echo
|
|
105
|
+
echo "doctor-misspelled-settings-keys: $PASS passed, $FAIL failed"
|
|
106
|
+
[ "$FAIL" -eq 0 ]
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Three example guards blocked a destructive command and then passed the same
|
|
3
|
+
# command once it was wrapped.
|
|
4
|
+
#
|
|
5
|
+
# The shapes measured 2026-08-04, against the shipped files:
|
|
6
|
+
#
|
|
7
|
+
# rm-safety-net 8 of 10 forms passed
|
|
8
|
+
# system-dir-protection-guard 9 of 11 forms passed
|
|
9
|
+
# auto-mode-safety-enforcer 5 of 9 forms passed
|
|
10
|
+
#
|
|
11
|
+
# Two separate causes, both about where a command is allowed to start:
|
|
12
|
+
#
|
|
13
|
+
# 1. The analysis gate. `rm-safety-net` and `system-dir-protection-guard`
|
|
14
|
+
# opened only on '^\s*(sudo\s+)?rm\s' — the command had to *begin* with
|
|
15
|
+
# rm, so `cd /tmp && rm -rf ~` never entered the analysis at all.
|
|
16
|
+
# 2. The target pattern. `auto-mode-safety-enforcer` required whitespace or
|
|
17
|
+
# end-of-line after the dangerous path, so `rm -rf ~; echo done` passed
|
|
18
|
+
# while `rm -rf ~ ; echo done` was blocked. One space apart, opposite
|
|
19
|
+
# verdicts.
|
|
20
|
+
#
|
|
21
|
+
# Wrappers hit both: inside a substitution or after `then`, a command is at
|
|
22
|
+
# neither the line start nor a separator. Claude Code 2.1.221 fixed the same
|
|
23
|
+
# shape in its own permission check ("hidden commands in [[ ]] regex
|
|
24
|
+
# conditionals"), which is what prompted measuring these.
|
|
25
|
+
#
|
|
26
|
+
# Half of this file is controls. The fix inserts separators at wrapper tokens,
|
|
27
|
+
# and parentheses and braces appear in ordinary commands (awk, jq, find -exec,
|
|
28
|
+
# subshells), so "looks inside wrappers" and "blocks anything with a bracket"
|
|
29
|
+
# have to be told apart. The home-path controls matter too: `ls ~/projects` and
|
|
30
|
+
# `cat ~/.bashrc` must stay untouched.
|
|
31
|
+
|
|
32
|
+
set -u
|
|
33
|
+
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
34
|
+
PASS=0; FAIL=0
|
|
35
|
+
RM=$(printf 'r%s' 'm')
|
|
36
|
+
|
|
37
|
+
run() {
|
|
38
|
+
local hook="$1" cmd="$2" T rc
|
|
39
|
+
T="$(mktemp -d)"
|
|
40
|
+
python3 -c '
|
|
41
|
+
import json,sys
|
|
42
|
+
print(json.dumps({"tool_name":"Bash","tool_input":{"command":sys.argv[1]},
|
|
43
|
+
"cwd":sys.argv[2],"session_id":"t"}))' "$cmd" "$T" \
|
|
44
|
+
| HOME="$T" bash "$ROOT/examples/$hook.sh" >/dev/null 2>&1
|
|
45
|
+
rc=$?
|
|
46
|
+
find "$T" -mindepth 1 -delete 2>/dev/null; rmdir "$T" 2>/dev/null
|
|
47
|
+
return $rc
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
want_block() {
|
|
51
|
+
if run "$1" "$2"; then FAIL=$((FAIL+1)); echo " FAIL blocked [$1]: $2"
|
|
52
|
+
else PASS=$((PASS+1)); echo " ok blocked [$1]: $2"; fi
|
|
53
|
+
}
|
|
54
|
+
want_allow() {
|
|
55
|
+
if run "$1" "$2"; then PASS=$((PASS+1)); echo " ok allowed [$1]: $2"
|
|
56
|
+
else FAIL=$((FAIL+1)); echo " FAIL allowed [$1]: $2"; fi
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
echo "example-guards-wrapped-command"
|
|
60
|
+
|
|
61
|
+
# --- rm-safety-net -----------------------------------------------------------
|
|
62
|
+
H=rm-safety-net
|
|
63
|
+
want_block $H "$RM -rf ~"
|
|
64
|
+
want_block $H "cd /tmp && $RM -rf ~"
|
|
65
|
+
want_block $H "[[ -n \$($RM -rf ~) ]]"
|
|
66
|
+
want_block $H "[[ -n \`$RM -rf ~\` ]]"
|
|
67
|
+
want_block $H "if true; then $RM -rf ~; fi"
|
|
68
|
+
want_block $H "for f in a; do $RM -rf ~; done"
|
|
69
|
+
want_block $H "( $RM -rf ~ )"
|
|
70
|
+
want_block $H "x=\$($RM -rf /etc)"
|
|
71
|
+
want_block $H "npm ci && $RM -rf /home/user"
|
|
72
|
+
want_allow $H "$RM -rf node_modules"
|
|
73
|
+
want_allow $H "cd app && $RM -rf node_modules && npm ci"
|
|
74
|
+
want_allow $H "npm run confirm ~/project"
|
|
75
|
+
want_allow $H "echo 'please confirm ~/data first'"
|
|
76
|
+
want_allow $H "rmdir empty-dir"
|
|
77
|
+
want_allow $H "awk '{print \$1}' access.log"
|
|
78
|
+
want_allow $H "( cd /tmp && ls )"
|
|
79
|
+
want_allow $H "echo \$(basename \$(pwd))"
|
|
80
|
+
|
|
81
|
+
# --- system-dir-protection-guard ---------------------------------------------
|
|
82
|
+
H=system-dir-protection-guard
|
|
83
|
+
want_block $H "$RM -rf /etc"
|
|
84
|
+
want_block $H "cd /tmp && $RM -rf /etc"
|
|
85
|
+
want_block $H "[[ -n \$($RM -rf /etc) ]]"
|
|
86
|
+
want_block $H "if true; then $RM -rf /usr; fi"
|
|
87
|
+
want_block $H "for f in a; do $RM -rf /var; done"
|
|
88
|
+
want_block $H "( $RM -rf /etc )"
|
|
89
|
+
want_block $H "npm ci && mv /etc /tmp/x"
|
|
90
|
+
want_block $H "true && chmod -R 777 /etc"
|
|
91
|
+
want_allow $H "ls -la /etc"
|
|
92
|
+
want_allow $H "cat /etc/hosts"
|
|
93
|
+
want_allow $H "mv old.txt new.txt"
|
|
94
|
+
want_allow $H "chmod -R 755 ./public"
|
|
95
|
+
want_allow $H "awk '{print \$1}' /var/log/app.log"
|
|
96
|
+
want_allow $H "( cd /tmp && ls )"
|
|
97
|
+
want_allow $H "grep -r 'TODO' /home/me/project"
|
|
98
|
+
|
|
99
|
+
# --- auto-mode-safety-enforcer -----------------------------------------------
|
|
100
|
+
H=auto-mode-safety-enforcer
|
|
101
|
+
want_block $H "$RM -rf ~; echo done"
|
|
102
|
+
want_block $H "[[ -n \$($RM -rf ~) ]]"
|
|
103
|
+
want_block $H "if true; then $RM -rf ~; fi"
|
|
104
|
+
want_block $H "for f in a; do $RM -rf ~; done"
|
|
105
|
+
want_block $H "x=\$($RM -rf /etc)"
|
|
106
|
+
want_allow $H "ls ~/projects"
|
|
107
|
+
want_allow $H "cat ~/.bashrc"
|
|
108
|
+
want_allow $H "cp -r ~/a ~/b"
|
|
109
|
+
want_allow $H "du -sh ~/"
|
|
110
|
+
want_allow $H "$RM -rf node_modules"
|
|
111
|
+
want_allow $H "jq '{a: .b}' package.json"
|
|
112
|
+
|
|
113
|
+
# --- bash-project-fence -------------------------------------------------------
|
|
114
|
+
# Different concern: this one fences *paths outside the project root*, not
|
|
115
|
+
# destructive verbs. Its word split does not break on ( ) or a backtick, so a
|
|
116
|
+
# path inside a substitution arrived as the token "~)" — matching neither the
|
|
117
|
+
# "~/" case nor the absolute-path case, and the reference passed unnoticed.
|
|
118
|
+
# The fix strips the wrapping punctuation from the token edges.
|
|
119
|
+
fence_run() {
|
|
120
|
+
local cmd="$1" P rc
|
|
121
|
+
P="$(mktemp -d)"
|
|
122
|
+
mkdir -p "$P/src"
|
|
123
|
+
python3 -c '
|
|
124
|
+
import json,sys
|
|
125
|
+
print(json.dumps({"tool_name":"Bash","tool_input":{"command":sys.argv[1]},
|
|
126
|
+
"cwd":sys.argv[2],"session_id":"t"}))' "$cmd" "$P" \
|
|
127
|
+
| (cd "$P" && CLAUDE_PROJECT_DIR="$P" HOME=/nonexistent-home-for-test \
|
|
128
|
+
bash "$ROOT/examples/bash-project-fence.sh") >/dev/null 2>&1
|
|
129
|
+
# HOME must not sit under /tmp here. /tmp is on this hook's default allow
|
|
130
|
+
# list, and mktemp -d hands out /tmp paths, so a temp HOME makes every `~`
|
|
131
|
+
# case allowed and the four assertions below pass no matter what the code
|
|
132
|
+
# does. The path does not need to exist; only its prefix is compared.
|
|
133
|
+
rc=$?
|
|
134
|
+
find "$P" -mindepth 1 -delete 2>/dev/null; rmdir "$P" 2>/dev/null
|
|
135
|
+
return $rc
|
|
136
|
+
}
|
|
137
|
+
fence_block() {
|
|
138
|
+
if fence_run "$1"; then FAIL=$((FAIL+1)); echo " FAIL blocked [fence]: $1"
|
|
139
|
+
else PASS=$((PASS+1)); echo " ok blocked [fence]: $1"; fi
|
|
140
|
+
}
|
|
141
|
+
fence_allow() {
|
|
142
|
+
if fence_run "$1"; then PASS=$((PASS+1)); echo " ok allowed [fence]: $1"
|
|
143
|
+
else FAIL=$((FAIL+1)); echo " FAIL allowed [fence]: $1"; fi
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
fence_block "cat /home/someone/secrets.txt"
|
|
147
|
+
fence_block "[[ -n \$(cat /home/someone/secrets.txt) ]]"
|
|
148
|
+
fence_block "x=\$(cat /home/someone/secrets.txt)"
|
|
149
|
+
fence_block "echo \`cat /home/someone/secrets.txt\`"
|
|
150
|
+
# The home directory reached the tokenizer as "~)" and "~`", which match
|
|
151
|
+
# neither branch. These are the four that actually failed before the fix; the
|
|
152
|
+
# absolute-path forms above were already caught, so they alone would have
|
|
153
|
+
# recorded the defect as the spec.
|
|
154
|
+
fence_block "[[ -n \$(ls ~) ]]"
|
|
155
|
+
fence_block "[[ -n \`ls ~\` ]]"
|
|
156
|
+
fence_block "x=\$(ls ~)"
|
|
157
|
+
fence_block "( ls ~ )"
|
|
158
|
+
fence_allow "ls -la"
|
|
159
|
+
fence_allow "git status"
|
|
160
|
+
fence_allow "echo hello > /tmp/out.txt"
|
|
161
|
+
fence_allow "cat /tmp/scratch"
|
|
162
|
+
fence_allow "npm ci && npm run build"
|
|
163
|
+
fence_allow "curl -s https://example.com/api"
|
|
164
|
+
fence_allow "echo \$(basename \$(pwd))"
|
|
165
|
+
fence_allow "for i in \$(seq 1 3); do echo \$i; done"
|
|
166
|
+
|
|
167
|
+
echo
|
|
168
|
+
echo "example-guards-wrapped-command: $PASS passed, $FAIL failed"
|
|
169
|
+
[ "$FAIL" -eq 0 ]
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Tests for file-edit-backup.sh
|
|
3
|
+
#
|
|
4
|
+
# The interesting case is path 2: a file overwritten through Bash
|
|
5
|
+
# (redirection, tee, mv) used to slip past this hook entirely.
|
|
6
|
+
# Reported by a reader on 2026-07-17.
|
|
7
|
+
HOOK="$(cd "$(dirname "$0")/.." && pwd)/examples/file-edit-backup.sh"
|
|
8
|
+
PASS=0; FAIL=0
|
|
9
|
+
|
|
10
|
+
WORK=$(mktemp -d)
|
|
11
|
+
export HOME="$WORK/home"
|
|
12
|
+
mkdir -p "$HOME"
|
|
13
|
+
BACKUPS="$HOME/.claude/file-backups"
|
|
14
|
+
|
|
15
|
+
# expect_backup <desc> <json> <basename-that-must-appear|NONE>
|
|
16
|
+
expect_backup() {
|
|
17
|
+
local desc="$1" input="$2" want="$3"
|
|
18
|
+
rm -rf "$BACKUPS" 2>/dev/null
|
|
19
|
+
( cd "$WORK" && printf '%s' "$input" | bash "$HOOK" >/dev/null 2>&1 )
|
|
20
|
+
local found=0
|
|
21
|
+
if [ "$want" != "NONE" ] && [ -d "$BACKUPS" ]; then
|
|
22
|
+
ls "$BACKUPS" 2>/dev/null | grep -q "$want" && found=1
|
|
23
|
+
fi
|
|
24
|
+
if [ "$want" = "NONE" ]; then
|
|
25
|
+
if [ ! -d "$BACKUPS" ] || [ -z "$(ls -A "$BACKUPS" 2>/dev/null)" ]; then
|
|
26
|
+
echo "PASS: $desc"; ((PASS++))
|
|
27
|
+
else
|
|
28
|
+
echo "FAIL: $desc (expected no backup, got $(ls "$BACKUPS"))"; ((FAIL++))
|
|
29
|
+
fi
|
|
30
|
+
elif [ "$found" = "1" ]; then
|
|
31
|
+
echo "PASS: $desc"; ((PASS++))
|
|
32
|
+
else
|
|
33
|
+
echo "FAIL: $desc (expected a backup matching '$want')"; ((FAIL++))
|
|
34
|
+
fi
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
cd "$WORK" || exit 1
|
|
38
|
+
echo "original" > target.md
|
|
39
|
+
echo "original" > appended.md
|
|
40
|
+
echo "original" > teed.md
|
|
41
|
+
echo "original" > moved.md
|
|
42
|
+
|
|
43
|
+
# Path 1 — the Edit/Write tools (existing behaviour must not regress)
|
|
44
|
+
expect_backup "Write on an existing file is backed up" \
|
|
45
|
+
"{\"tool_name\":\"Write\",\"tool_input\":{\"file_path\":\"$WORK/target.md\"}}" "target.md"
|
|
46
|
+
|
|
47
|
+
expect_backup "Write on a new file makes no backup" \
|
|
48
|
+
"{\"tool_name\":\"Write\",\"tool_input\":{\"file_path\":\"$WORK/does-not-exist.md\"}}" "NONE"
|
|
49
|
+
|
|
50
|
+
# Path 2 — Bash. This is the hole the reader named.
|
|
51
|
+
expect_backup "Bash redirection > is backed up" \
|
|
52
|
+
'{"tool_name":"Bash","tool_input":{"command":"echo new > target.md"}}' "target.md"
|
|
53
|
+
|
|
54
|
+
expect_backup "Bash append >> is backed up" \
|
|
55
|
+
'{"tool_name":"Bash","tool_input":{"command":"echo more >> appended.md"}}' "appended.md"
|
|
56
|
+
|
|
57
|
+
expect_backup "Bash tee is backed up" \
|
|
58
|
+
'{"tool_name":"Bash","tool_input":{"command":"cat src | tee teed.md"}}' "teed.md"
|
|
59
|
+
|
|
60
|
+
expect_backup "Bash tee -a is backed up" \
|
|
61
|
+
'{"tool_name":"Bash","tool_input":{"command":"cat src | tee -a teed.md"}}' "teed.md"
|
|
62
|
+
|
|
63
|
+
expect_backup "Bash mv destination is backed up" \
|
|
64
|
+
'{"tool_name":"Bash","tool_input":{"command":"mv src.txt moved.md"}}' "moved.md"
|
|
65
|
+
|
|
66
|
+
expect_backup "Bash redirection inside a chain is backed up" \
|
|
67
|
+
'{"tool_name":"Bash","tool_input":{"command":"cd /tmp && echo x > target.md"}}' "target.md"
|
|
68
|
+
|
|
69
|
+
# Must NOT fire on things that only look like writes
|
|
70
|
+
expect_backup "fd duplication >&2 makes no backup" \
|
|
71
|
+
'{"tool_name":"Bash","tool_input":{"command":"echo hi >&2"}}' "NONE"
|
|
72
|
+
|
|
73
|
+
expect_backup "stderr redirect 2>&1 makes no backup" \
|
|
74
|
+
'{"tool_name":"Bash","tool_input":{"command":"make 2>&1 | less"}}' "NONE"
|
|
75
|
+
|
|
76
|
+
expect_backup "writing to a device file makes no backup" \
|
|
77
|
+
'{"tool_name":"Bash","tool_input":{"command":"echo x > /dev/null"}}' "NONE"
|
|
78
|
+
|
|
79
|
+
expect_backup "redirect to a file that does not exist makes no backup" \
|
|
80
|
+
'{"tool_name":"Bash","tool_input":{"command":"echo x > brand-new.md"}}' "NONE"
|
|
81
|
+
|
|
82
|
+
expect_backup "a read-only command makes no backup" \
|
|
83
|
+
'{"tool_name":"Bash","tool_input":{"command":"grep -r TODO ."}}' "NONE"
|
|
84
|
+
|
|
85
|
+
# The hook must never block: exit code is always 0
|
|
86
|
+
code=$(printf '%s' '{"tool_name":"Bash","tool_input":{"command":"echo x > target.md"}}' | bash "$HOOK" >/dev/null 2>&1; echo $?)
|
|
87
|
+
if [ "$code" = "0" ]; then echo "PASS: never blocks (exit 0)"; ((PASS++)); else echo "FAIL: expected exit 0, got $code"; ((FAIL++)); fi
|
|
88
|
+
|
|
89
|
+
cd / && rm -rf "$WORK" 2>/dev/null
|
|
90
|
+
|
|
91
|
+
echo ""
|
|
92
|
+
echo "PASS: $PASS FAIL: $FAIL"
|
|
93
|
+
[ "$FAIL" -eq 0 ]
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Tests for git-filter-repo-guard.sh
|
|
3
|
+
# Run: bash tests/git-filter-repo-guard.test.sh
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
PASS=0
|
|
7
|
+
FAIL=0
|
|
8
|
+
HOOK="$(dirname "$0")/../examples/git-filter-repo-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-filter-repo-guard.sh tests"
|
|
24
|
+
echo ""
|
|
25
|
+
|
|
26
|
+
# --- Block: git filter-repo ---
|
|
27
|
+
test_hook '{"tool_input":{"command":"git filter-repo --force"}}' 2 "Block git filter-repo --force"
|
|
28
|
+
test_hook '{"tool_input":{"command":"git filter-repo --invert-paths --path secret.txt"}}' 2 "Block git filter-repo with path filter"
|
|
29
|
+
test_hook '{"tool_input":{"command":"git filter-repo --strip-blobs-bigger-than 10M"}}' 2 "Block git filter-repo blob strip"
|
|
30
|
+
test_hook '{"tool_input":{"command":"git filter-repo"}}' 2 "Block bare git filter-repo"
|
|
31
|
+
|
|
32
|
+
# --- Block: git filter-branch ---
|
|
33
|
+
test_hook '{"tool_input":{"command":"git filter-branch --tree-filter rm -f passwords.txt HEAD"}}' 2 "Block git filter-branch --tree-filter"
|
|
34
|
+
test_hook '{"tool_input":{"command":"git filter-branch --env-filter"}}' 2 "Block git filter-branch --env-filter"
|
|
35
|
+
test_hook '{"tool_input":{"command":"git filter-branch"}}' 2 "Block bare git filter-branch"
|
|
36
|
+
|
|
37
|
+
# --- Block: BFG Repo-Cleaner ---
|
|
38
|
+
test_hook '{"tool_input":{"command":"bfg --strip-blobs-bigger-than 100M"}}' 2 "Block bfg --strip-blobs-bigger-than"
|
|
39
|
+
test_hook '{"tool_input":{"command":"bfg --delete-files id_rsa"}}' 2 "Block bfg --delete-files"
|
|
40
|
+
test_hook '{"tool_input":{"command":"java -jar bfg.jar --replace-text passwords.txt"}}' 2 "Block bfg.jar via java"
|
|
41
|
+
|
|
42
|
+
# --- Allow: Safe git commands ---
|
|
43
|
+
test_hook '{"tool_input":{"command":"git status"}}' 0 "Allow git status"
|
|
44
|
+
test_hook '{"tool_input":{"command":"git log --oneline"}}' 0 "Allow git log"
|
|
45
|
+
test_hook '{"tool_input":{"command":"git diff HEAD~3"}}' 0 "Allow git diff"
|
|
46
|
+
test_hook '{"tool_input":{"command":"git commit -m fix"}}' 0 "Allow git commit"
|
|
47
|
+
test_hook '{"tool_input":{"command":"git push origin main"}}' 0 "Allow git push"
|
|
48
|
+
test_hook '{"tool_input":{"command":"git rebase main"}}' 0 "Allow non-interactive rebase"
|
|
49
|
+
|
|
50
|
+
# --- Allow: Commands containing filter as substring (not filter-repo) ---
|
|
51
|
+
test_hook '{"tool_input":{"command":"grep filter README.md"}}' 0 "Allow grep filter"
|
|
52
|
+
test_hook '{"tool_input":{"command":"cat filter-config.json"}}' 0 "Allow cat filter-config.json"
|
|
53
|
+
|
|
54
|
+
# --- Allow: Empty/missing input ---
|
|
55
|
+
test_hook '{}' 0 "Allow empty input"
|
|
56
|
+
test_hook '{"tool_input":{}}' 0 "Allow missing command"
|
|
57
|
+
|
|
58
|
+
echo ""
|
|
59
|
+
echo "Results: $PASS passed, $FAIL failed out of $((PASS + FAIL)) tests"
|
|
60
|
+
[ "$FAIL" -eq 0 ] && exit 0 || exit 1
|