cc-safe-setup 29.8.0 → 30.0.5
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 +421 -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 +1718 -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
|
@@ -8,46 +8,123 @@
|
|
|
8
8
|
# while letting destructive commands go through normal approval.
|
|
9
9
|
#
|
|
10
10
|
# TRIGGER: PreToolUse MATCHER: "Bash"
|
|
11
|
+
#
|
|
12
|
+
# Rewritten 2026-08-03. Two things were wrong, and they compounded.
|
|
13
|
+
#
|
|
14
|
+
# 1. The base command was taken from the FIRST word of the whole line, so
|
|
15
|
+
# `cat README.md && sudo rm -rf app` produced the base `cat` and the
|
|
16
|
+
# approval was handed to the entire line. Same defect as PR #937
|
|
17
|
+
# (allowlist.sh), #940 (cd-git-allow.sh) and #941/#942 (the other
|
|
18
|
+
# auto-approve hooks) — on the approving side, where the decision is an
|
|
19
|
+
# explicit approval rather than a missed block.
|
|
20
|
+
# 2. `find` sat in the read-only list with no look at its predicates, so
|
|
21
|
+
# `find . -name '*.log' -delete` was approved as a read-only command.
|
|
22
|
+
# Redirections had the same shape: `cat x > out` writes a file and was
|
|
23
|
+
# approved for reading one.
|
|
24
|
+
#
|
|
25
|
+
# Now every command position has to qualify on its own, and the forms that
|
|
26
|
+
# write are named. A line that does not qualify gets no decision, which leaves
|
|
27
|
+
# it to the normal permission flow — this hook only ever adds approval, it
|
|
28
|
+
# never blocks.
|
|
29
|
+
#
|
|
30
|
+
# Known limit: quotes are not parsed, so a separator inside a quoted string
|
|
31
|
+
# ends a segment early. That is the conservative direction here (fewer
|
|
32
|
+
# approvals, not more).
|
|
11
33
|
# ================================================================
|
|
12
34
|
|
|
13
35
|
COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
14
36
|
[ -z "$COMMAND" ] && exit 0
|
|
15
37
|
|
|
16
|
-
#
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
pwd|df|du|free|top|ps|pgrep|lsof|netstat|ss|\
|
|
25
|
-
git-log|git-diff|git-show|git-status|git-branch|git-remote|git-tag|\
|
|
26
|
-
jq|yq|python3-c|node-e|ruby-e|\
|
|
27
|
-
npm-ls|npm-list|npm-info|npm-view|npm-outdated|\
|
|
28
|
-
pip-list|pip-show|pip-freeze|\
|
|
29
|
-
cargo-tree|go-list|go-doc)
|
|
30
|
-
echo '{"decision":"approve","reason":"Read-only command"}'
|
|
31
|
-
exit 0
|
|
32
|
-
;;
|
|
38
|
+
# A substitution can carry a command that no string-level read sees.
|
|
39
|
+
case "$COMMAND" in
|
|
40
|
+
*'$('*|*'`'*) exit 0 ;;
|
|
41
|
+
esac
|
|
42
|
+
|
|
43
|
+
# Anything that writes to a file is not a read, whatever the command word says.
|
|
44
|
+
case "$COMMAND" in
|
|
45
|
+
*'>'*) exit 0 ;;
|
|
33
46
|
esac
|
|
34
47
|
|
|
35
|
-
# git
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
# Commands that read and nothing else. `git` is handled separately below,
|
|
49
|
+
# because only some of its subcommands read.
|
|
50
|
+
cc_ro_base_ok() {
|
|
51
|
+
case "$1" in
|
|
52
|
+
cat|head|tail|less|more|wc|grep|rg|ag|ack|locate|\
|
|
53
|
+
ls|ll|dir|tree|stat|file|which|whereis|type|realpath|\
|
|
54
|
+
date|uptime|uname|hostname|whoami|id|groups|env|printenv|\
|
|
55
|
+
pwd|df|du|free|top|ps|pgrep|lsof|netstat|ss|\
|
|
56
|
+
jq|yq)
|
|
57
|
+
return 0 ;;
|
|
58
|
+
esac
|
|
59
|
+
return 1
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
# Filters that only make sense downstream of something else. They stay out of
|
|
63
|
+
# the first position so that `sed …` on its own is not read as a read.
|
|
64
|
+
cc_filter_base_ok() {
|
|
65
|
+
case "$1" in
|
|
66
|
+
head|tail|grep|wc|sort|uniq|tr|cut|awk|sed|less|more|column|nl|rev)
|
|
67
|
+
return 0 ;;
|
|
50
68
|
esac
|
|
51
|
-
|
|
69
|
+
return 1
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
cc_segment_base() {
|
|
73
|
+
# strip leading blanks, leading VAR=value assignments, then take the command
|
|
74
|
+
# word without its directory
|
|
75
|
+
printf '%s' "$1" \
|
|
76
|
+
| sed -E 's/^[[:space:]]+//; s/^([A-Za-z_][A-Za-z0-9_]*=[^[:space:]]*[[:space:]]+)*//' \
|
|
77
|
+
| awk '{print $1}' \
|
|
78
|
+
| sed 's|.*/||'
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
cc_segment_reads() {
|
|
82
|
+
local seg="$1" idx="$2" base
|
|
83
|
+
base=$(cc_segment_base "$seg")
|
|
84
|
+
[ -z "$base" ] && return 1
|
|
85
|
+
|
|
86
|
+
# moving between directories reads nothing and writes nothing
|
|
87
|
+
[ "$base" = "cd" ] && return 0
|
|
88
|
+
|
|
89
|
+
# find reads only while it carries no predicate that acts on what it finds
|
|
90
|
+
if [ "$base" = "find" ]; then
|
|
91
|
+
printf '%s' "$seg" \
|
|
92
|
+
| grep -qE '(^|[[:space:]])-(delete|exec|execdir|ok|okdir|fprintf?|fls|fprint0)([[:space:]]|$)' \
|
|
93
|
+
&& return 1
|
|
94
|
+
return 0
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
# sed rewrites the file in place with -i
|
|
98
|
+
if [ "$base" = "sed" ]; then
|
|
99
|
+
printf '%s' "$seg" | grep -qE '(^|[[:space:]])-[a-zA-Z]*i' && return 1
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
cc_ro_base_ok "$base" && return 0
|
|
103
|
+
|
|
104
|
+
# git, only the subcommands that read
|
|
105
|
+
if printf '%s' "$seg" | grep -qE '^[[:space:]]*git[[:space:]]+(status|log|diff|show|branch|remote|tag[[:space:]]+-l|blame|shortlog|describe|rev-parse|ls-files|ls-tree)([[:space:]]|$)'; then
|
|
106
|
+
return 0
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
# downstream positions may also be a plain filter
|
|
110
|
+
[ "$idx" -gt 0 ] && cc_filter_base_ok "$base" && return 0
|
|
111
|
+
|
|
112
|
+
return 1
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
IDX=0
|
|
116
|
+
while IFS= read -r SEG; do
|
|
117
|
+
SEG="${SEG#"${SEG%%[![:space:]]*}"}"
|
|
118
|
+
SEG="${SEG%"${SEG##*[![:space:]]}"}"
|
|
119
|
+
[ -z "$SEG" ] && continue
|
|
120
|
+
cc_segment_reads "$SEG" "$IDX" || exit 0
|
|
121
|
+
IDX=$((IDX + 1))
|
|
122
|
+
done <<EOF
|
|
123
|
+
$(printf '%s' "$COMMAND" | tr ';&|' '\n\n\n')
|
|
124
|
+
EOF
|
|
125
|
+
|
|
126
|
+
# nothing to approve if the line held no command at all
|
|
127
|
+
[ "$IDX" -eq 0 ] && exit 0
|
|
52
128
|
|
|
129
|
+
echo '{"decision":"approve","reason":"Read-only command"}'
|
|
53
130
|
exit 0
|
|
@@ -21,7 +21,36 @@ fi
|
|
|
21
21
|
# Safe remote commands (customize this list)
|
|
22
22
|
SAFE_COMMANDS="uptime|w|whoami|hostname|uname|date|df|free|cat /etc/os-release"
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
# Approve only when EVERY command position qualifies.
|
|
25
|
+
#
|
|
26
|
+
# The pattern below was anchored at `^\s*` and matched against the whole command
|
|
27
|
+
# string, so only the first command position was examined and the approval was
|
|
28
|
+
# then handed to the entire line: `ssh host uptime && sudo rm -rf app` was
|
|
29
|
+
# approved on the strength of its first word. Measured 2026-08-03. Same defect
|
|
30
|
+
# as PR #937 / #940 / #941, on the approving side, where the decision is an
|
|
31
|
+
# explicit approval rather than a missed block.
|
|
32
|
+
#
|
|
33
|
+
# Splitting on the separator characters is approximate — quotes are not parsed —
|
|
34
|
+
# so a quoted remote payload that carries a separator (`ssh host "uptime; df"`)
|
|
35
|
+
# stops qualifying too. That is the conservative direction: it gets no decision
|
|
36
|
+
# and lands in the normal permission flow. Command substitution and backticks
|
|
37
|
+
# disqualify the line outright. This hook only ever adds approval; it never
|
|
38
|
+
# blocks.
|
|
39
|
+
cc_every_segment_matches() {
|
|
40
|
+
local pat="$1" cmd="$2" seg
|
|
41
|
+
case "$cmd" in *'$('*|*'`'*) return 1 ;; esac
|
|
42
|
+
while IFS= read -r seg; do
|
|
43
|
+
seg="${seg#"${seg%%[![:space:]]*}"}"
|
|
44
|
+
seg="${seg%"${seg##*[![:space:]]}"}"
|
|
45
|
+
[ -z "$seg" ] && continue
|
|
46
|
+
printf '%s' "$seg" | grep -qE "$pat" || return 1
|
|
47
|
+
done <<EOF
|
|
48
|
+
$(printf '%s' "$cmd" | tr ';&|' '\n\n\n')
|
|
49
|
+
EOF
|
|
50
|
+
return 0
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if cc_every_segment_matches "^\s*ssh\s+\S+\s+($SAFE_COMMANDS)(\s|\$)" "$COMMAND"; then
|
|
25
54
|
jq -n '{
|
|
26
55
|
hookSpecificOutput: {
|
|
27
56
|
hookEventName: "PreToolUse",
|
|
@@ -3,6 +3,50 @@
|
|
|
3
3
|
# TRIGGER: PreToolUse MATCHER: "Bash"
|
|
4
4
|
COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
5
5
|
[ -z "$COMMAND" ] && exit 0
|
|
6
|
+
|
|
7
|
+
# Approve only when EVERY command position qualifies.
|
|
8
|
+
#
|
|
9
|
+
# The patterns below were anchored at `^\s*` and matched against the whole
|
|
10
|
+
# command string, so only the first command position was examined and the
|
|
11
|
+
# approval was then handed to the entire line: `pytest && sudo rm -rf app` was
|
|
12
|
+
# approved on the strength of its first word. Measured 2026-08-03. Same defect
|
|
13
|
+
# as PR #937 / #940 / #941, on the approving side, where the decision is an
|
|
14
|
+
# explicit approval rather than a missed block.
|
|
15
|
+
#
|
|
16
|
+
# Splitting on the separator characters is approximate — quotes are not parsed —
|
|
17
|
+
# so anything that can hide a command from a string-level read (command
|
|
18
|
+
# substitution, backticks) disqualifies the line outright. A line that does not
|
|
19
|
+
# qualify gets no decision, which leaves it to the normal permission flow. This
|
|
20
|
+
# hook only ever adds approval; it never blocks.
|
|
21
|
+
#
|
|
22
|
+
# The gate uses the union of all eight patterns, so a chain of test runners
|
|
23
|
+
# (`pytest && go test ./...`) still passes; the if-chain below only picks which
|
|
24
|
+
# reason to report.
|
|
25
|
+
cc_every_segment_matches() {
|
|
26
|
+
local pat="$1" cmd="$2" seg
|
|
27
|
+
case "$cmd" in *'$('*|*'`'*) return 1 ;; esac
|
|
28
|
+
while IFS= read -r seg; do
|
|
29
|
+
seg="${seg#"${seg%%[![:space:]]*}"}"
|
|
30
|
+
seg="${seg%"${seg##*[![:space:]]}"}"
|
|
31
|
+
[ -z "$seg" ] && continue
|
|
32
|
+
printf '%s' "$seg" | grep -qE "$pat" || return 1
|
|
33
|
+
done <<EOF
|
|
34
|
+
$(printf '%s' "$cmd" | tr ';&|' '\n\n\n')
|
|
35
|
+
EOF
|
|
36
|
+
return 0
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
TEST_SAFE_RE='^\s*(npm\s+test|npm\s+run\s+test|npx\s+(jest|vitest|mocha|ava|tap|playwright\s+test|cypress\s+run)|yarn\s+test|pnpm\s+test|bun\s+test)\b'
|
|
40
|
+
TEST_SAFE_RE="$TEST_SAFE_RE"'|^\s*(pytest|python\s+-m\s+(pytest|unittest)|tox)\b'
|
|
41
|
+
TEST_SAFE_RE="$TEST_SAFE_RE"'|^\s*go\s+test\b'
|
|
42
|
+
TEST_SAFE_RE="$TEST_SAFE_RE"'|^\s*cargo\s+test\b'
|
|
43
|
+
TEST_SAFE_RE="$TEST_SAFE_RE"'|^\s*(phpunit|vendor/bin/phpunit|php\s+artisan\s+test)\b'
|
|
44
|
+
TEST_SAFE_RE="$TEST_SAFE_RE"'|^\s*(rspec|bundle\s+exec\s+rspec|rake\s+test|rails\s+test)\b'
|
|
45
|
+
TEST_SAFE_RE="$TEST_SAFE_RE"'|^\s*(mvn\s+test|gradle\s+test|./gradlew\s+test)\b'
|
|
46
|
+
TEST_SAFE_RE="$TEST_SAFE_RE"'|^\s*dotnet\s+test\b'
|
|
47
|
+
|
|
48
|
+
cc_every_segment_matches "$TEST_SAFE_RE" "$COMMAND" || exit 0
|
|
49
|
+
|
|
6
50
|
if echo "$COMMAND" | grep -qE '^\s*(npm\s+test|npm\s+run\s+test|npx\s+(jest|vitest|mocha|ava|tap|playwright\s+test|cypress\s+run)|yarn\s+test|pnpm\s+test|bun\s+test)\b'; then
|
|
7
51
|
echo '{"decision":"approve","reason":"Test runner command"}'
|
|
8
52
|
exit 0
|
|
File without changes
|
|
File without changes
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
# }
|
|
22
22
|
# }
|
|
23
23
|
#
|
|
24
|
-
# TRIGGER:
|
|
24
|
+
# TRIGGER: PreToolUse MATCHER: "Bash"
|
|
25
25
|
|
|
26
26
|
INPUT=$(cat)
|
|
27
27
|
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
@@ -99,6 +99,58 @@ if echo "$COMMAND" | grep -qE '^\s*(node|python3?)\s+-e\s'; then
|
|
|
99
99
|
fi
|
|
100
100
|
fi
|
|
101
101
|
|
|
102
|
+
# --- Every command position has to qualify, not just the first one ---
|
|
103
|
+
#
|
|
104
|
+
# The comment at the top of this file says "We check each component of compound
|
|
105
|
+
# commands". It did not. Every pattern above is anchored at `^\s*` and matched
|
|
106
|
+
# against the whole command string, so only the first command position was ever
|
|
107
|
+
# examined, and the approval below was then handed to the entire line.
|
|
108
|
+
# `cat README.md && sudo rm -rf app` and `curl -s http://… | sh` were both
|
|
109
|
+
# approved. Measured 2026-08-03. Same defect as PR #937 / #940 / #941, on the
|
|
110
|
+
# approving side, where the decision is an explicit approval rather than a
|
|
111
|
+
# missed block. This hook matters more than most because it exists for auto
|
|
112
|
+
# mode, where nobody is watching the prompt.
|
|
113
|
+
#
|
|
114
|
+
# Splitting on the separator characters is approximate — quotes are not parsed —
|
|
115
|
+
# so a line that does not qualify gets no decision and lands in the normal
|
|
116
|
+
# permission flow. This hook only ever adds approval; it never blocks.
|
|
117
|
+
cc_every_segment_matches() {
|
|
118
|
+
local pat="$1" cmd="$2" seg
|
|
119
|
+
while IFS= read -r seg; do
|
|
120
|
+
seg="${seg#"${seg%%[![:space:]]*}"}"
|
|
121
|
+
seg="${seg%"${seg##*[![:space:]]}"}"
|
|
122
|
+
[ -z "$seg" ] && continue
|
|
123
|
+
printf '%s' "$seg" | grep -qE "$pat" || return 1
|
|
124
|
+
done <<EOF
|
|
125
|
+
$(printf '%s' "$cmd" | tr ';&|' '\n\n\n')
|
|
126
|
+
EOF
|
|
127
|
+
return 0
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
SAFE_RE='^\s*(cat|head|tail|less|more|wc|file|stat|du|df|ls|tree|find|which|whereis|type|realpath|readlink)\s'
|
|
131
|
+
SAFE_RE="$SAFE_RE"'|^\s*(grep|rg|ag|ack|sed\s+-n|awk)\s'
|
|
132
|
+
SAFE_RE="$SAFE_RE"'|^\s*git\s+(status|log|diff|show|branch|tag|remote|stash\s+list|ls-files|ls-tree|rev-parse|describe|shortlog|blame|config\s+--get)'
|
|
133
|
+
SAFE_RE="$SAFE_RE"'|^\s*(npm\s+(ls|list|info|view|outdated|audit)|pip\s+(list|show|freeze)|yarn\s+(list|info|why)|pnpm\s+(ls|list))\s*'
|
|
134
|
+
SAFE_RE="$SAFE_RE"'|^\s*(echo|printf|date|env|printenv|uname|hostname|whoami|id|pwd|tput)\s*'
|
|
135
|
+
SAFE_RE="$SAFE_RE"'|^\s*(jq|yq|python3?\s+-c\s|python3?\s+-m\s+json)\s'
|
|
136
|
+
SAFE_RE="$SAFE_RE"'|^\s*curl\s+-s'
|
|
137
|
+
SAFE_RE="$SAFE_RE"'|^\s*(node|python3?)\s+-e\s'
|
|
138
|
+
SAFE_RE="$SAFE_RE"'|^\s*(mkdir|touch|cp|mv)\s'
|
|
139
|
+
|
|
140
|
+
if [ "$APPROVE" = true ]; then
|
|
141
|
+
# A substitution can carry a command that no string-level read sees. The
|
|
142
|
+
# `$(date …)` branch above is the one this hook deliberately supports, so
|
|
143
|
+
# strip those and refuse if any other substitution is left.
|
|
144
|
+
if printf '%s' "$COMMAND" | grep -q '`'; then
|
|
145
|
+
exit 0
|
|
146
|
+
fi
|
|
147
|
+
SUBST_REST=$(printf '%s' "$COMMAND" | sed -E 's/\$\(date[^)]*\)//g')
|
|
148
|
+
if printf '%s' "$SUBST_REST" | grep -q '\$('; then
|
|
149
|
+
exit 0
|
|
150
|
+
fi
|
|
151
|
+
cc_every_segment_matches "$SAFE_RE" "$COMMAND" || exit 0
|
|
152
|
+
fi
|
|
153
|
+
|
|
102
154
|
# --- Output the decision ---
|
|
103
155
|
if [ "$APPROVE" = true ]; then
|
|
104
156
|
jq -n --arg reason "$REASON" \
|
|
@@ -21,21 +21,168 @@
|
|
|
21
21
|
|
|
22
22
|
set -euo pipefail
|
|
23
23
|
|
|
24
|
+
# Without jq, the parse below silently yields empty and this hook stops
|
|
25
|
+
# guarding - with no error anywhere. Say so. We deliberately do not exit
|
|
26
|
+
# here: blocking would halt every tool call, and exiting 0 would change
|
|
27
|
+
# the behaviour of guards that do not depend on the parsed value.
|
|
28
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
29
|
+
_nojq_warned="/tmp/cc-nojq-warned-auto-mode-safety-enforcer-$PPID"
|
|
30
|
+
[ -f "$_nojq_warned" ] || {
|
|
31
|
+
echo "WARNING [auto-mode-safety-enforcer]: jq not found - this hook cannot inspect tool calls and is NOT protecting you. Install jq." >&2
|
|
32
|
+
: > "$_nojq_warned"
|
|
33
|
+
}
|
|
34
|
+
fi
|
|
35
|
+
|
|
24
36
|
INPUT=$(cat)
|
|
25
37
|
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
38
|
+
|
|
39
|
+
# --- Mention vs invocation ----------------------------------------------------
|
|
40
|
+
# Every pattern below scans the command text, so a dangerous string written
|
|
41
|
+
# inside quotes reaches them exactly as an executed command would. Until now
|
|
42
|
+
# this hook lived with that by leaving quotes out of the terminator sets: the
|
|
43
|
+
# five quoted targets the header claims passed, and the mention controls in
|
|
44
|
+
# tests/auto-mode-unquoted-targets.test.sh passed for the same reason -- the
|
|
45
|
+
# target was invisible, not recognised.
|
|
46
|
+
#
|
|
47
|
+
# This is the quote-aware extraction destructive-guard grew in PR #960, ported
|
|
48
|
+
# here as the comment further down said it would have to be. Blank out
|
|
49
|
+
# everything inside quotes, then ask whether a destructive verb still starts a
|
|
50
|
+
# command in what is left. If none does, nothing destructive is being invoked
|
|
51
|
+
# and there is nothing for the rest of this file to judge.
|
|
52
|
+
if [ -z "${CC_AUTOMODE_UNWRAPPED:-}" ] && [ -n "$COMMAND" ]; then
|
|
53
|
+
_am_outside=$(printf '%s' "$COMMAND" | awk -v SQ="'" -v DQ='"' '
|
|
54
|
+
{ line = $0; out = ""; q = 0
|
|
55
|
+
for (i = 1; i <= length(line); i++) {
|
|
56
|
+
c = substr(line, i, 1)
|
|
57
|
+
if (q == 0 && c == SQ) { q = 1; out = out " "; continue }
|
|
58
|
+
if (q == 1) { if (c == SQ) q = 0; out = out " "; continue }
|
|
59
|
+
if (q == 0 && c == DQ) { q = 2; out = out " "; continue }
|
|
60
|
+
if (q == 2) { if (c == DQ) q = 0; out = out " "; continue }
|
|
61
|
+
out = out c
|
|
62
|
+
}
|
|
63
|
+
print out }')
|
|
64
|
+
# The wrapper unwrapping further down only runs if this gate lets the call
|
|
65
|
+
# through, so the gate has to see a verb that sits inside a substitution or
|
|
66
|
+
# a subshell too. Replace the wrapping tokens with separators first -- the
|
|
67
|
+
# same substitution the unwrapping step uses. Measured 2026-08-11: without
|
|
68
|
+
# this, a deletion inside a substitution and one on the right of an
|
|
69
|
+
# assignment both exited 0 here and never reached the unwrapping.
|
|
70
|
+
_am_gate=$(printf '%s' "$_am_outside" | sed -E \
|
|
71
|
+
-e 's/\$\(/ ; /g' -e 's/`/ ; /g' \
|
|
72
|
+
-e 's/(^|[[:space:]])\(/\1 ; /g' -e 's/\)([[:space:]]|$)/ ; \1/g' \
|
|
73
|
+
-e 's/\)$/ ; /g' \
|
|
74
|
+
-e 's/(^|[[:space:]])(then|do|else|elif)([[:space:]])/\1 ; \3/g')
|
|
75
|
+
# A quoted string is only inert while nothing executes it. `sh -c` with a
|
|
76
|
+
# quoted argument, `eval`, and a pipe into a shell all run that text, so
|
|
77
|
+
# there the quoted string is a command and not a mention. Never let the
|
|
78
|
+
# gate pass those through.
|
|
79
|
+
if printf '%s' "$_am_outside" | grep -qE '(^|[;&|`(){}]|[[:space:]])((sh|bash|zsh|dash|ksh)[[:space:]]+(-[a-z]*[[:space:]]+)*-c([[:space:]]|$)|eval([[:space:]]|$))|\|[[:space:]]*(sudo[[:space:]]+)?(sh|bash|zsh|dash|ksh|python3?|perl|ruby|node)([[:space:]]|$)' 2>/dev/null; then
|
|
80
|
+
:
|
|
81
|
+
elif ! printf '%s' "$_am_gate" | grep -qE '(^|[;&|`(){}]|\$\(|[[:space:]](then|do|else|elif)[[:space:]])[[:space:]]*([A-Za-z_][A-Za-z0-9_]*=[^[:space:]]*[[:space:]]+|(env|nice|ionice|timeout|exec|command|builtin|stdbuf|nohup|sudo|time|xargs)([[:space:]]+-[^[:space:]]+)*([[:space:]]+[0-9]+)?([[:space:]]+-[^[:space:]]+)*[[:space:]]+)*([./]*[A-Za-z0-9_./-]*/)?(rm|rmdir|unlink|shred|dd|mkfs[.a-z0-9]*|fdisk|parted|chmod|kill|killall)([[:space:]]|$)' 2>/dev/null; then
|
|
82
|
+
exit 0
|
|
83
|
+
fi
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
# --- Look inside wrappers -----------------------------------------------------
|
|
87
|
+
# The target patterns below require whitespace or end-of-line after the
|
|
88
|
+
# dangerous path, so a deletion wrapped in a substitution or a subshell ends
|
|
89
|
+
# with `)` and never matches. Measured 2026-08-04: the bare deletion was
|
|
90
|
+
# blocked and five of six wrappings passed. Claude Code 2.1.221 fixed the same
|
|
91
|
+
# shape in its own permission check.
|
|
92
|
+
#
|
|
93
|
+
# Replace the wrapping tokens with separators and run this same script once more
|
|
94
|
+
# against that text. Detection only: never executed; the message keeps the
|
|
95
|
+
# command the user actually sent.
|
|
96
|
+
if [ -n "${CC_AUTOMODE_UNWRAPPED:-}" ]; then
|
|
97
|
+
COMMAND="$CC_AUTOMODE_UNWRAPPED"
|
|
98
|
+
elif [ -n "$COMMAND" ]; then
|
|
99
|
+
_am_unwrapped=$(printf '%s' "$COMMAND" | sed -E \
|
|
100
|
+
-e 's/\$\(/ ; /g' -e 's/`/ ; /g' \
|
|
101
|
+
-e 's/(^|[[:space:]])\(/\1 ; /g' -e 's/\)([[:space:]]|$)/ ; \1/g' \
|
|
102
|
+
-e 's/\)$/ ; /g' \
|
|
103
|
+
-e 's/(^|[[:space:]])(then|do|else|elif)([[:space:]])/\1 ; \3/g')
|
|
104
|
+
if [ "$_am_unwrapped" != "$COMMAND" ]; then
|
|
105
|
+
CC_AUTOMODE_UNWRAPPED="$_am_unwrapped" bash "$0" </dev/null >/dev/null 2>&1
|
|
106
|
+
if [ "$?" = "2" ]; then
|
|
107
|
+
echo "BLOCKED: a destructive command is hidden inside a wrapper." >&2
|
|
108
|
+
echo "Command: $COMMAND" >&2
|
|
109
|
+
echo "A substitution, a subshell or a shell keyword does not make it safe." >&2
|
|
110
|
+
exit 2
|
|
111
|
+
fi
|
|
112
|
+
fi
|
|
113
|
+
fi
|
|
26
114
|
[ -z "$COMMAND" ] && exit 0
|
|
27
115
|
|
|
116
|
+
# Quote characters are dropped for matching only. The gate above has already
|
|
117
|
+
# ruled out text that merely names a command, so a quote here is an ordinary
|
|
118
|
+
# way to write an argument and the target behind it has to be judged. Every
|
|
119
|
+
# message below still shows the command the user actually sent.
|
|
120
|
+
#
|
|
121
|
+
# This has to come after the unwrapping above, not before it: that step
|
|
122
|
+
# re-reads COMMAND from CC_AUTOMODE_UNWRAPPED in the recursive call, so a
|
|
123
|
+
# CMD_UNQ computed earlier would still hold the value from before the rewrite
|
|
124
|
+
# (empty, in the child) and every pattern below would match nothing.
|
|
125
|
+
CMD_UNQ=$(printf '%s' "$COMMAND" | tr -d "\"'")
|
|
126
|
+
|
|
127
|
+
# The gate at the top of this file counts a path, a sudo, an inline assignment
|
|
128
|
+
# and the usual wrappers as an invocation. The two checks below anchored on a
|
|
129
|
+
# separator or a bare space, so `/usr/bin/rm -rf /` reached them and matched
|
|
130
|
+
# nothing: measured 2026-09-14, nine invocation forms against six destructive
|
|
131
|
+
# commands, twelve of fifty-four cells passed and every one of the twelve was
|
|
132
|
+
# reached through a path.
|
|
133
|
+
#
|
|
134
|
+
# This is a superset of what was here before, not a replacement. The bare-space
|
|
135
|
+
# branch is load-bearing: the gate lets `echo "rm -rf /" | sh` through on the
|
|
136
|
+
# pipe-into-a-shell arm, and only the loose match below stops it. Narrowing this
|
|
137
|
+
# to command position alone reopened four cases in tests/auto-mode-quoted-
|
|
138
|
+
# targets.test.sh -- measured against the pre-change copy to be sure it was the
|
|
139
|
+
# change and not the instrument.
|
|
140
|
+
AM_CMDPOS='(^|[;&|`(){}]|\$\(|[[:space:]])[[:space:]]*([A-Za-z_][A-Za-z0-9_]*=[^[:space:]]*[[:space:]]+|(env|nice|ionice|timeout|exec|command|builtin|stdbuf|nohup|sudo|time|xargs)([[:space:]]+-[^[:space:]]+)*([[:space:]]+[0-9]+)?([[:space:]]+-[^[:space:]]+)*[[:space:]]+)*([./]*[A-Za-z0-9_./-]*/)?'
|
|
141
|
+
|
|
28
142
|
# --- Critical rm operations ---
|
|
29
|
-
if echo "$
|
|
143
|
+
if echo "$CMD_UNQ" | grep -qE "${AM_CMDPOS}"'rm[[:space:]]'; then
|
|
30
144
|
# Always block rm on root-level and home-level critical paths
|
|
31
|
-
|
|
145
|
+
# The home terminators used to be whitespace or end-of-line only, so
|
|
146
|
+
# `rm -rf ~; echo done` passed while `rm -rf ~ ; echo done` was blocked --
|
|
147
|
+
# one space apart, opposite verdicts. Separators end a command just as much
|
|
148
|
+
# as a space does. Measured 2026-08-04.
|
|
149
|
+
if echo "$CMD_UNQ" | grep -qE 'rm\s.*(/[[:space:];&|]|/$|~\/?[[:space:];&|]|~\/?$|~\/\.|/home\b|/etc\b|/usr\b|/var\b|/opt\b|/root\b)'; then
|
|
32
150
|
echo "BLOCKED: rm targeting critical system/home path" >&2
|
|
33
151
|
echo "This operation would cause irreversible data loss." >&2
|
|
34
152
|
echo "Command: $COMMAND" >&2
|
|
35
153
|
exit 2
|
|
36
154
|
fi
|
|
155
|
+
# The header above lists $HOME, a parent traversal and the git directory
|
|
156
|
+
# among the paths this hook blocks, but the pattern before this one has no
|
|
157
|
+
# case for any of them. Measured 2026-08-11 against the 19 targets the
|
|
158
|
+
# header claims: 11 passed, including
|
|
159
|
+
#
|
|
160
|
+
# rm -rf $HOME rm -rf ${HOME} rm -rf ..
|
|
161
|
+
# rm -rf ../.. rm -rf a/../.. rm -rf .git
|
|
162
|
+
#
|
|
163
|
+
# scripts.json grew the $HOME case in PR #962 and the quoted case in #961.
|
|
164
|
+
# Neither fix reached this example, so the same defect stayed here.
|
|
165
|
+
#
|
|
166
|
+
# Quoted forms ("/", "$HOME", ".git") are deliberately still not matched.
|
|
167
|
+
# This hook has no mention-vs-invocation pass, so putting a quote in a
|
|
168
|
+
# terminator set would also block `echo "rm -rf /"` and a commit message
|
|
169
|
+
# that names the command. Those pass today only because the target is
|
|
170
|
+
# invisible behind the quote. Telling a mention from a run needs the
|
|
171
|
+
# quote-aware extraction destructive-guard grew in PR #960; until that is
|
|
172
|
+
# ported here, rm-safety-net and compound-command-deny-enforcer are the
|
|
173
|
+
# layer that catches the quoted forms (measured: both deny them).
|
|
174
|
+
#
|
|
175
|
+
# Terminators are deliberate: `..` and `.git` must be a whole path
|
|
176
|
+
# component, so `my..cache`, `backup..`, `.gitignore` and `.github` are
|
|
177
|
+
# untouched, and `$HOMEBREW_PREFIX` does not look like `$HOME`.
|
|
178
|
+
if echo "$CMD_UNQ" | grep -qE 'rm\s.*(\$\{?HOME\}?([[:space:];&|/]|$)|(^|[[:space:]/])\.\.([[:space:];&|/]|$)|(^|[[:space:]/])\.git([[:space:];&|/]|$))'; then
|
|
179
|
+
echo "BLOCKED: rm targeting the home directory, a parent directory, or the git directory" >&2
|
|
180
|
+
echo "This operation would cause irreversible data loss." >&2
|
|
181
|
+
echo "Command: $COMMAND" >&2
|
|
182
|
+
exit 2
|
|
183
|
+
fi
|
|
37
184
|
# Block rm on dotfiles in home directory
|
|
38
|
-
if echo "$
|
|
185
|
+
if echo "$CMD_UNQ" | grep -qE "rm\s.*(${HOME}|\~)/\."; then
|
|
39
186
|
echo "BLOCKED: rm targeting home dotfile" >&2
|
|
40
187
|
echo "Command: $COMMAND" >&2
|
|
41
188
|
exit 2
|
|
@@ -43,7 +190,7 @@ if echo "$COMMAND" | grep -qE '(^|\s|;|&&|\|)(sudo\s+)?rm\s'; then
|
|
|
43
190
|
fi
|
|
44
191
|
|
|
45
192
|
# --- Disk-level operations ---
|
|
46
|
-
if echo "$
|
|
193
|
+
if echo "$CMD_UNQ" | grep -qE "${AM_CMDPOS}"'(dd[[:space:]]+.*of=/dev|mkfs\.|fdisk[[:space:]]|parted[[:space:]])'; then
|
|
47
194
|
echo "BLOCKED: Disk-level operation (dd/mkfs/fdisk/parted)" >&2
|
|
48
195
|
exit 2
|
|
49
196
|
fi
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -uo pipefail
|
|
3
|
+
git rev-parse --is-inside-work-tree >/dev/null 2>&1 || exit 0
|
|
4
|
+
branch=$(git symbolic-ref --quiet --short HEAD 2>/dev/null || echo "")
|
|
5
|
+
[ -z "$branch" ] && exit 0 # detached HEAD: do nothing
|
|
6
|
+
case "$branch" in
|
|
7
|
+
main|master|develop|release) exit 0 ;;
|
|
8
|
+
esac
|
|
9
|
+
push() { if "$@"; then return 0; else
|
|
10
|
+
echo "[auto-push] '$branch' not pushed (no remote / auth / rejected). Push manually." >&2
|
|
11
|
+
exit 0; fi; }
|
|
12
|
+
if git rev-parse --abbrev-ref --symbolic-full-name '@{u}' >/dev/null 2>&1; then
|
|
13
|
+
ahead=$(git rev-list --count '@{u}..HEAD' 2>/dev/null || echo 0)
|
|
14
|
+
[ "$ahead" -eq 0 ] && exit 0 # nothing unpushed
|
|
15
|
+
push git push --quiet
|
|
16
|
+
echo "[auto-push] pushed $ahead commit(s) on '$branch'." >&2
|
|
17
|
+
else
|
|
18
|
+
git rev-parse --verify --quiet HEAD >/dev/null 2>&1 || exit 0
|
|
19
|
+
push git push --quiet -u origin "$branch"
|
|
20
|
+
echo "[auto-push] set upstream and pushed '$branch' to origin." >&2
|
|
21
|
+
fi
|
|
22
|
+
exit 0
|
|
@@ -6,7 +6,19 @@
|
|
|
6
6
|
# aws rds delete-db-instance, aws cloudformation delete-stack
|
|
7
7
|
#
|
|
8
8
|
# TRIGGER: PreToolUse
|
|
9
|
-
# MATCHER: "Bash"
|
|
9
|
+
# MATCHER: "Bash|PowerShell"
|
|
10
|
+
|
|
11
|
+
# Without jq, the parse below silently yields empty and this hook stops
|
|
12
|
+
# guarding - with no error anywhere. Say so. We deliberately do not exit
|
|
13
|
+
# here: blocking would halt every tool call, and exiting 0 would change
|
|
14
|
+
# the behaviour of guards that do not depend on the parsed value.
|
|
15
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
16
|
+
_nojq_warned="/tmp/cc-nojq-warned-aws-production-guard-$PPID"
|
|
17
|
+
[ -f "$_nojq_warned" ] || {
|
|
18
|
+
echo "WARNING [aws-production-guard]: jq not found - this hook cannot inspect tool calls and is NOT protecting you. Install jq." >&2
|
|
19
|
+
: > "$_nojq_warned"
|
|
20
|
+
}
|
|
21
|
+
fi
|
|
10
22
|
|
|
11
23
|
INPUT=$(cat)
|
|
12
24
|
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
# aws-region-guard.sh — Warn when AWS commands target unexpected regions
|
|
3
|
-
# TRIGGER: PreToolUse MATCHER: "Bash"
|
|
3
|
+
# TRIGGER: PreToolUse MATCHER: "Bash|PowerShell"
|
|
4
4
|
COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
5
5
|
[ -z "$COMMAND" ] && exit 0
|
|
6
6
|
echo "$COMMAND" | grep -qE '^\s*aws\s' || exit 0
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# background-cost-launch-guard.sh — Block a *background* Bash launch that can
|
|
3
|
+
# incur ongoing API/compute cost and would
|
|
4
|
+
# keep running after the model says "Done".
|
|
5
|
+
#
|
|
6
|
+
# Solves (#68642): Claude Code launched a batch API script with
|
|
7
|
+
# run_in_background=true, announced completion ("Done."), and the user walked
|
|
8
|
+
# away — but three processes kept hammering the API for 6+ hours, racking up
|
|
9
|
+
# several hundred USD. The model signaled completion WITHOUT confirming the
|
|
10
|
+
# background processes had exited. A foreground run would have blocked (and been
|
|
11
|
+
# visible); a background run is fire-and-forget and silent.
|
|
12
|
+
#
|
|
13
|
+
# Why existing guards miss this exact shape:
|
|
14
|
+
# - timeout-guard.sh nudges you TO use run_in_background for forever-servers —
|
|
15
|
+
# the opposite direction; it never inspects the cost of a bg launch.
|
|
16
|
+
# - dangling-process-guard.sh is a Stop hook that lists leftover `&`/`nohup`
|
|
17
|
+
# processes; it does not read the Bash tool's `run_in_background` parameter
|
|
18
|
+
# and only fires at session end, after the money is already spent.
|
|
19
|
+
# - api-busyloop-guard.sh fires on no-op-body busy-poll loops; a batch API
|
|
20
|
+
# script is not a busy-poll, so it never trips.
|
|
21
|
+
#
|
|
22
|
+
# What this does: on PreToolUse for Bash, if run_in_background is true AND the
|
|
23
|
+
# command matches a cost-incurring signature (LLM API endpoints/SDKs, `claude -p`
|
|
24
|
+
# headless runs, or a loop that calls curl/python against an API), it BLOCKS
|
|
25
|
+
# (exit 2) and tells the model to (a) run it in the FOREGROUND so completion is
|
|
26
|
+
# actually observed, or (b) add an explicit budget/turn cap and a hard timeout.
|
|
27
|
+
# Background launches with no cost signature (dev servers, log tails) pass.
|
|
28
|
+
#
|
|
29
|
+
# TRIGGER: PreToolUse MATCHER: "Bash"
|
|
30
|
+
# settings.json:
|
|
31
|
+
# { "hooks": { "PreToolUse": [{ "matcher": "Bash",
|
|
32
|
+
# "hooks": [{ "type": "command",
|
|
33
|
+
# "command": "~/.claude/hooks/background-cost-launch-guard.sh" }] }] } }
|
|
34
|
+
|
|
35
|
+
# Without jq, the parse below silently yields empty and this hook stops
|
|
36
|
+
# guarding - with no error anywhere. Say so. We deliberately do not exit
|
|
37
|
+
# here: blocking would halt every tool call, and exiting 0 would change
|
|
38
|
+
# the behaviour of guards that do not depend on the parsed value.
|
|
39
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
40
|
+
_nojq_warned="/tmp/cc-nojq-warned-background-cost-launch-guard-$PPID"
|
|
41
|
+
[ -f "$_nojq_warned" ] || {
|
|
42
|
+
echo "WARNING [background-cost-launch-guard]: jq not found - this hook cannot inspect tool calls and is NOT protecting you. Install jq." >&2
|
|
43
|
+
: > "$_nojq_warned"
|
|
44
|
+
}
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
INPUT=$(cat)
|
|
48
|
+
|
|
49
|
+
# Only act on background launches. Claude Code exposes run_in_background as a
|
|
50
|
+
# top-level field of the Bash tool input.
|
|
51
|
+
BG=$(printf '%s' "$INPUT" | jq -r '.tool_input.run_in_background // false' 2>/dev/null)
|
|
52
|
+
[[ "$BG" != "true" ]] && exit 0
|
|
53
|
+
|
|
54
|
+
CMD=$(printf '%s' "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
55
|
+
[[ -z "$CMD" ]] && exit 0
|
|
56
|
+
|
|
57
|
+
# Cost-incurring signatures. Kept deliberately specific so ordinary background
|
|
58
|
+
# work (servers, builds, log tails) is never blocked.
|
|
59
|
+
COST_RE='api\.anthropic\.com|api\.openai\.com|generativelanguage\.googleapis\.com|/v1/(messages|complete|chat/completions|batches)|anthropic|openai|litellm|(^|[^a-z])claude[[:space:]]+-p([[:space:]]|$)|--print|messages\.batches|batch'
|
|
60
|
+
LOOP_RE='(while|until|for)[[:space:]].*(curl|wget|python|node|claude|gh[[:space:]]+api)'
|
|
61
|
+
|
|
62
|
+
if printf '%s' "$CMD" | grep -qiE "$COST_RE" || printf '%s' "$CMD" | grep -qiE "$LOOP_RE"; then
|
|
63
|
+
{
|
|
64
|
+
echo "BLOCKED: background launch that can incur ongoing API/compute cost."
|
|
65
|
+
echo ""
|
|
66
|
+
echo "This Bash call sets run_in_background=true on a command that looks"
|
|
67
|
+
echo "like it spends money or loops over an API. A background launch is"
|
|
68
|
+
echo "fire-and-forget: the session can report \"Done\" while it keeps"
|
|
69
|
+
echo "running for hours (see anthropics/claude-code#68642 — several"
|
|
70
|
+
echo "hundred USD burned this exact way)."
|
|
71
|
+
echo ""
|
|
72
|
+
echo "Do one of:"
|
|
73
|
+
echo " 1) Run it in the FOREGROUND (run_in_background=false) so its exit"
|
|
74
|
+
echo " is actually observed before any completion is claimed; or"
|
|
75
|
+
echo " 2) Bound it: add a hard timeout and an explicit budget/turn cap"
|
|
76
|
+
echo " (e.g. 'timeout 600 ...', a max-iteration counter), then verify"
|
|
77
|
+
echo " the process has exited before moving on."
|
|
78
|
+
} >&2
|
|
79
|
+
exit 2
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
exit 0
|