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
|
@@ -11,38 +11,110 @@
|
|
|
11
11
|
# TRIGGER: PreToolUse
|
|
12
12
|
# MATCHER: ""
|
|
13
13
|
|
|
14
|
+
__CC_HOOK_INPUT=$(cat 2>/dev/null)
|
|
15
|
+
# --- セッション識別子でファイルを分ける (2026-08-09 修正) ---
|
|
16
|
+
# 旧実装は "$$" を使っていたが、$$ はこのスクリプト自身のPIDで呼び出しごとに変わるため、
|
|
17
|
+
# 状態が一度も持続しなかった(実測: 200回呼ぶとファイルが200個でき、中身は全部 1)。
|
|
18
|
+
__CC_SID=""
|
|
19
|
+
if [ -n "${__CC_HOOK_INPUT:-}" ]; then
|
|
20
|
+
if command -v jq >/dev/null 2>&1; then
|
|
21
|
+
__CC_SID=$(printf '%s' "$__CC_HOOK_INPUT" | jq -r '.session_id // .sessionId // empty' 2>/dev/null)
|
|
22
|
+
fi
|
|
23
|
+
if [ -z "$__CC_SID" ] && command -v python3 >/dev/null 2>&1; then
|
|
24
|
+
__CC_SID=$(printf '%s' "$__CC_HOOK_INPUT" | python3 -c 'import sys,json
|
|
25
|
+
try:
|
|
26
|
+
d=json.load(sys.stdin); print(d.get("session_id") or d.get("sessionId") or "")
|
|
27
|
+
except Exception: print("")' 2>/dev/null)
|
|
28
|
+
fi
|
|
29
|
+
fi
|
|
30
|
+
if [ -n "$__CC_SID" ]; then
|
|
31
|
+
__CC_KEY=$(printf '%s' "$__CC_SID" | tr -c 'A-Za-z0-9_-' '_' | cut -c1-64)
|
|
32
|
+
else
|
|
33
|
+
__CC_KEY="nosession-$(date +%Y%m%d)"
|
|
34
|
+
fi
|
|
35
|
+
# --- ここまで ---
|
|
36
|
+
|
|
14
37
|
set -euo pipefail
|
|
15
38
|
|
|
16
39
|
MAX_CALLS="${CC_MAX_TOOL_CALLS:-200}"
|
|
17
|
-
COUNTER_FILE="/tmp/claude-tool-call-counter
|
|
40
|
+
COUNTER_FILE="/tmp/claude-tool-call-counter-$__CC_KEY"
|
|
18
41
|
|
|
19
|
-
#
|
|
20
|
-
|
|
21
|
-
|
|
42
|
+
# 2026-08-09: $PPID を使う旧機構を削除した。
|
|
43
|
+
# 旧実装は $PPID(親プロセスのID)をセッションの識別子として使っていた。
|
|
44
|
+
# ★訂正: 最初これを「一度も遮断しない」と書いたが、それは計測の誤りだった。
|
|
45
|
+
# 親が安定していれば $PPID は持続するので、上限10で15回叩くと11回目から5回遮断する(実測)。
|
|
46
|
+
# 「200回で遮断0回」という最初の測定は、こちらが $( ) の中で呼んだせいで
|
|
47
|
+
# 呼び出しごとに別のサブシェルが親になっていたためで、仕掛けが作った結果だった。
|
|
48
|
+
# 実際に残る欠陥はこちら: $PPID はセッションの識別子ではない。
|
|
49
|
+
# 同じ親から複数のセッションが走ると**カウンターが混ざる**。
|
|
50
|
+
# 実測: 同じ親から session_id が sX と sY の呼び出しを6回ずつ交互に流すと、
|
|
51
|
+
# カウンターファイルは1個だけで値は12(=6+6)。片方のセッションが上限を使い切ると、
|
|
52
|
+
# もう片方が何もしていなくても遮断される。
|
|
53
|
+
# 修正: hookの入力JSONの session_id で分ける。
|
|
54
|
+
#
|
|
55
|
+
# ★2026-08-27 追記: session_id だけでは、このhookの名前が約束していることができない。
|
|
56
|
+
# PreToolUse の入力を丸ごと落として実測したところ、サブエージェントの呼び出しでも
|
|
57
|
+
# session_id は親と1バイトも違わない(transcript_path も同じ)。増えるのは agent_id と
|
|
58
|
+
# agent_type の2つだけ。つまり session_id を鍵にしたカウンターは、親と全サブエージェントの
|
|
59
|
+
# 合算になる。#36727 の「1体のサブエージェントが1.5時間で234回」を止めたくても、
|
|
60
|
+
# 合算のカウンターでは「誰が使ったか」が分からず、暴走した子が枠を焼き切ると
|
|
61
|
+
# 何もしていない親まで止まる。
|
|
62
|
+
# → セッション全体の上限はそのままに、agent_id ごとの上限を足した。
|
|
63
|
+
# どちらか先に達したほうで止める。合計の上限は変えていないので、緩くはならない。
|
|
22
64
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
65
|
+
MAX_SUBAGENT_CALLS="${CC_MAX_SUBAGENT_TOOL_CALLS:-100}"
|
|
66
|
+
|
|
67
|
+
# agent_id はサブエージェントの呼び出しにだけ入る(実測)。上位エージェントでは空。
|
|
68
|
+
__CC_AID=""
|
|
69
|
+
if [ -n "${__CC_HOOK_INPUT:-}" ] && command -v jq >/dev/null 2>&1; then
|
|
70
|
+
__CC_AID=$(printf '%s' "$__CC_HOOK_INPUT" | jq -r '.agent_id // empty' 2>/dev/null)
|
|
29
71
|
fi
|
|
72
|
+
__CC_AKEY=$(printf '%s' "$__CC_AID" | tr -c 'A-Za-z0-9_-' '_' | cut -c1-64)
|
|
73
|
+
AGENT_COUNTER_FILE="/tmp/claude-tool-call-counter-$__CC_KEY-agent-$__CC_AKEY"
|
|
30
74
|
|
|
31
|
-
|
|
32
|
-
|
|
75
|
+
read_counter() {
|
|
76
|
+
_v=0
|
|
77
|
+
if [ -f "$1" ]; then
|
|
78
|
+
_v=$(cat "$1" 2>/dev/null || echo 0)
|
|
79
|
+
case "$_v" in ''|*[!0-9]*) _v=0 ;; esac
|
|
80
|
+
fi
|
|
81
|
+
printf '%s' "$_v"
|
|
82
|
+
}
|
|
33
83
|
|
|
34
|
-
|
|
84
|
+
COUNT=$(( $(read_counter "$COUNTER_FILE") + 1 ))
|
|
85
|
+
AGENT_COUNT=0
|
|
86
|
+
[ -n "$__CC_AID" ] && AGENT_COUNT=$(( $(read_counter "$AGENT_COUNTER_FILE") + 1 ))
|
|
87
|
+
|
|
88
|
+
# ★記録は遮断を決めた後に置く。先に書くと、案内を読んで再試行するたびに
|
|
89
|
+
# カウンターが増えて待ち時間が自分の再試行で延びる(2026-07-28 に別のhookで実害)。
|
|
35
90
|
if [ "$COUNT" -gt "$MAX_CALLS" ]; then
|
|
36
|
-
echo "BLOCKED: Tool call limit reached ($COUNT/$MAX_CALLS)." >&2
|
|
37
|
-
echo "This session
|
|
91
|
+
echo "BLOCKED: Tool call limit reached ($COUNT/$MAX_CALLS) for this session." >&2
|
|
92
|
+
echo "This session (parent and all subagents share one session_id) has made" >&2
|
|
93
|
+
echo "$COUNT tool calls (limit: $MAX_CALLS)." >&2
|
|
38
94
|
echo "Consider starting a new session or increasing CC_MAX_TOOL_CALLS." >&2
|
|
39
95
|
exit 2
|
|
40
96
|
fi
|
|
41
97
|
|
|
98
|
+
if [ -n "$__CC_AID" ] && [ "$AGENT_COUNT" -gt "$MAX_SUBAGENT_CALLS" ]; then
|
|
99
|
+
echo "BLOCKED: Subagent tool call limit reached ($AGENT_COUNT/$MAX_SUBAGENT_CALLS)." >&2
|
|
100
|
+
echo "Subagent agent_id=$__CC_AID has made $AGENT_COUNT tool calls on its own." >&2
|
|
101
|
+
echo "The session as a whole is still at $COUNT/$MAX_CALLS, so the parent is not blocked." >&2
|
|
102
|
+
echo "Raise CC_MAX_SUBAGENT_TOOL_CALLS if this subagent legitimately needs more." >&2
|
|
103
|
+
exit 2
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
echo "$COUNT" > "$COUNTER_FILE"
|
|
107
|
+
[ -n "$__CC_AID" ] && echo "$AGENT_COUNT" > "$AGENT_COUNTER_FILE"
|
|
108
|
+
|
|
42
109
|
# Warn at 80%
|
|
43
110
|
WARN_AT=$((MAX_CALLS * 80 / 100))
|
|
44
111
|
if [ "$COUNT" -eq "$WARN_AT" ]; then
|
|
45
112
|
echo "WARNING: $COUNT/$MAX_CALLS tool calls used (80%). Consider wrapping up." >&2
|
|
46
113
|
fi
|
|
47
114
|
|
|
115
|
+
AGENT_WARN_AT=$((MAX_SUBAGENT_CALLS * 80 / 100))
|
|
116
|
+
if [ -n "$__CC_AID" ] && [ "$AGENT_COUNT" -eq "$AGENT_WARN_AT" ]; then
|
|
117
|
+
echo "WARNING: subagent $__CC_AID is at $AGENT_COUNT/$MAX_SUBAGENT_CALLS of its own budget." >&2
|
|
118
|
+
fi
|
|
119
|
+
|
|
48
120
|
exit 0
|
|
@@ -15,6 +15,18 @@
|
|
|
15
15
|
|
|
16
16
|
set -euo pipefail
|
|
17
17
|
|
|
18
|
+
# Without jq, the parse below silently yields empty and this hook stops
|
|
19
|
+
# guarding - with no error anywhere. Say so. We deliberately do not exit
|
|
20
|
+
# here: blocking would halt every tool call, and exiting 0 would change
|
|
21
|
+
# the behaviour of guards that do not depend on the parsed value.
|
|
22
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
23
|
+
_nojq_warned="/tmp/cc-nojq-warned-subcommand-chain-guard-$PPID"
|
|
24
|
+
[ -f "$_nojq_warned" ] || {
|
|
25
|
+
echo "WARNING [subcommand-chain-guard]: jq not found - this hook cannot inspect tool calls and is NOT protecting you. Install jq." >&2
|
|
26
|
+
: > "$_nojq_warned"
|
|
27
|
+
}
|
|
28
|
+
fi
|
|
29
|
+
|
|
18
30
|
THRESHOLD=${CC_SUBCOMMAND_LIMIT:-20}
|
|
19
31
|
|
|
20
32
|
INPUT=$(cat)
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ================================================================
|
|
3
|
+
# subscription-api-billing-warner.sh — Warn at session start when the
|
|
4
|
+
# active configuration will silently bill API / purchased credits
|
|
5
|
+
# *above* a Claude Pro/Max subscription, so a subscriber does not
|
|
6
|
+
# keep draining pay-per-token credits while subscription quota sits
|
|
7
|
+
# unused.
|
|
8
|
+
# ================================================================
|
|
9
|
+
# PURPOSE:
|
|
10
|
+
# Claude Code's credential precedence puts API-key auth ABOVE an
|
|
11
|
+
# OAuth subscription: if ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN
|
|
12
|
+
# is set, or settings.json defines an apiKeyHelper, every request
|
|
13
|
+
# authenticates as a pay-per-token API call and bills the
|
|
14
|
+
# Console/purchased credits — even when the user has a Pro/Max
|
|
15
|
+
# subscription with quota to spare. Because nothing in the UI
|
|
16
|
+
# surfaces this, subscribers lose real money without knowing an
|
|
17
|
+
# API key is in play.
|
|
18
|
+
#
|
|
19
|
+
# This is a recurring, money-losing pain on the issue tracker:
|
|
20
|
+
# #64613 API requests consuming personal token quota when
|
|
21
|
+
# subscription tokens available
|
|
22
|
+
# #53638 Desktop silently uses project API keys for billing
|
|
23
|
+
# instead of the subscription
|
|
24
|
+
# #53728 Silent ANTHROPIC_API_KEY precedence shadows Max
|
|
25
|
+
# subscription auth
|
|
26
|
+
# #54677 [FR] Distinguish API key vs OAuth subscription billing
|
|
27
|
+
#
|
|
28
|
+
# The hook inspects, at session start, the three locally-detectable
|
|
29
|
+
# precedence sources (the two env vars and an apiKeyHelper in
|
|
30
|
+
# user/project settings.json). If any is present it emits a one-line
|
|
31
|
+
# stderr advisory naming the precedence rule and the one-command
|
|
32
|
+
# confirmation (/status), then continues. Advisory only (exit 0);
|
|
33
|
+
# it never blocks the session and never reads the key value.
|
|
34
|
+
#
|
|
35
|
+
# It is deliberately conservative about noise: someone who runs on
|
|
36
|
+
# API billing on purpose (no subscription, or multiple deliberate
|
|
37
|
+
# API-key accounts) can silence it permanently, and it auto-skips
|
|
38
|
+
# when ANTHROPIC_ACCOUNT_LABEL is set — the same signal the
|
|
39
|
+
# account-routing-preflight.sh hook uses to mark intentional
|
|
40
|
+
# multi-account API use.
|
|
41
|
+
#
|
|
42
|
+
# TRIGGER: SessionStart
|
|
43
|
+
# MATCHER: ""
|
|
44
|
+
#
|
|
45
|
+
# SETUP:
|
|
46
|
+
# {
|
|
47
|
+
# "hooks": {
|
|
48
|
+
# "SessionStart": [
|
|
49
|
+
# {
|
|
50
|
+
# "matcher": "",
|
|
51
|
+
# "hooks": [
|
|
52
|
+
# { "type": "command",
|
|
53
|
+
# "command": "$HOME/.claude/hooks/subscription-api-billing-warner.sh" }
|
|
54
|
+
# ]
|
|
55
|
+
# }
|
|
56
|
+
# ]
|
|
57
|
+
# }
|
|
58
|
+
# }
|
|
59
|
+
#
|
|
60
|
+
# BEHAVIOR:
|
|
61
|
+
# - No API-key env var and no apiKeyHelper → silent pass.
|
|
62
|
+
# - Any precedence source present → stderr advisory + exit 0.
|
|
63
|
+
# - Never blocks; never prints the key/token value.
|
|
64
|
+
#
|
|
65
|
+
# CONFIGURATION (env vars):
|
|
66
|
+
# CC_SUB_BILLING_DISABLE Set to "1" to disable the hook entirely.
|
|
67
|
+
# ANTHROPIC_ACCOUNT_LABEL If set (non-empty), the hook stays silent:
|
|
68
|
+
# an explicit account label signals the
|
|
69
|
+
# operator is managing API billing on
|
|
70
|
+
# purpose (mirrors account-routing-preflight).
|
|
71
|
+
# CC_SUB_BILLING_SETTINGS_OVERRIDE
|
|
72
|
+
# Colon-separated list of settings.json paths
|
|
73
|
+
# to scan for apiKeyHelper instead of the real
|
|
74
|
+
# ~/.claude/settings.json and ./.claude/
|
|
75
|
+
# settings.json. Used by tests/CI.
|
|
76
|
+
# ================================================================
|
|
77
|
+
|
|
78
|
+
set -u
|
|
79
|
+
|
|
80
|
+
if [ "${CC_SUB_BILLING_DISABLE:-0}" = "1" ]; then
|
|
81
|
+
exit 0
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
# Read and discard the SessionStart JSON payload; the hook only
|
|
85
|
+
# inspects the environment and settings files, not the payload.
|
|
86
|
+
if [ ! -t 0 ]; then
|
|
87
|
+
cat >/dev/null 2>&1 || true
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
# An explicit account label means the operator is managing API-key
|
|
91
|
+
# billing deliberately — stay silent to avoid nagging that case.
|
|
92
|
+
if [ -n "${ANTHROPIC_ACCOUNT_LABEL:-}" ]; then
|
|
93
|
+
exit 0
|
|
94
|
+
fi
|
|
95
|
+
|
|
96
|
+
REASONS=""
|
|
97
|
+
|
|
98
|
+
if [ -n "${ANTHROPIC_API_KEY:-}" ]; then
|
|
99
|
+
REASONS="${REASONS} • ANTHROPIC_API_KEY is set in this environment
|
|
100
|
+
"
|
|
101
|
+
fi
|
|
102
|
+
|
|
103
|
+
if [ -n "${ANTHROPIC_AUTH_TOKEN:-}" ]; then
|
|
104
|
+
REASONS="${REASONS} • ANTHROPIC_AUTH_TOKEN is set in this environment
|
|
105
|
+
"
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
# Scan settings.json files for an apiKeyHelper, which injects a key on
|
|
109
|
+
# every request even when no env var is set.
|
|
110
|
+
if [ -n "${CC_SUB_BILLING_SETTINGS_OVERRIDE:-}" ]; then
|
|
111
|
+
SETTINGS_LIST="${CC_SUB_BILLING_SETTINGS_OVERRIDE//:/
|
|
112
|
+
}"
|
|
113
|
+
else
|
|
114
|
+
SETTINGS_LIST="${HOME:-/root}/.claude/settings.json
|
|
115
|
+
./.claude/settings.json"
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
while IFS= read -r SETTINGS_FILE; do
|
|
119
|
+
[ -z "$SETTINGS_FILE" ] && continue
|
|
120
|
+
[ -r "$SETTINGS_FILE" ] || continue
|
|
121
|
+
# Tolerant key-presence check (no jq dependency): a JSON object key
|
|
122
|
+
# "apiKeyHelper" with a non-empty string value.
|
|
123
|
+
if grep -Eq '"apiKeyHelper"[[:space:]]*:[[:space:]]*"[^"]+"' "$SETTINGS_FILE" 2>/dev/null; then
|
|
124
|
+
REASONS="${REASONS} • apiKeyHelper is configured in ${SETTINGS_FILE}
|
|
125
|
+
"
|
|
126
|
+
fi
|
|
127
|
+
done <<EOF
|
|
128
|
+
$SETTINGS_LIST
|
|
129
|
+
EOF
|
|
130
|
+
|
|
131
|
+
if [ -n "$REASONS" ]; then
|
|
132
|
+
cat >&2 <<EOF
|
|
133
|
+
|
|
134
|
+
⚠️ This session may bill API / purchased credits instead of your subscription.
|
|
135
|
+
|
|
136
|
+
Claude Code's credential precedence puts API-key auth ABOVE an
|
|
137
|
+
OAuth subscription. Detected:
|
|
138
|
+
${REASONS}
|
|
139
|
+
If you have a Claude Pro/Max subscription, requests are likely
|
|
140
|
+
spending pay-per-token (Console/purchased) credits even while your
|
|
141
|
+
subscription quota is unused — the cause of the "subscription at 0%
|
|
142
|
+
but my purchased balance is draining" reports (#64613, #53638, #53728).
|
|
143
|
+
|
|
144
|
+
Confirm and fix:
|
|
145
|
+
1. Run /status — it names the active auth method (subscription vs API).
|
|
146
|
+
2. To use the subscription: unset the variable(s) above, and check the
|
|
147
|
+
shell profile (.zshrc/.bashrc/.profile) and any .env / direnv .envrc
|
|
148
|
+
that re-export them; remove apiKeyHelper from settings.json; and in
|
|
149
|
+
/config turn off "Use custom API key".
|
|
150
|
+
3. If money was already charged while a subscription was active, a
|
|
151
|
+
billing review at https://support.claude.com can refund a misroute.
|
|
152
|
+
|
|
153
|
+
This is an advisory; the session continues.
|
|
154
|
+
Set CC_SUB_BILLING_DISABLE=1 (or ANTHROPIC_ACCOUNT_LABEL for deliberate
|
|
155
|
+
API-key accounts) to silence it.
|
|
156
|
+
|
|
157
|
+
EOF
|
|
158
|
+
fi
|
|
159
|
+
|
|
160
|
+
exit 0
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# subscription-bypass-detector.sh — SessionStart hook
|
|
3
|
+
# Trigger: SessionStart
|
|
4
|
+
# Matcher: ""
|
|
5
|
+
#
|
|
6
|
+
# Solves: anthropics/claude-code#60093 — operator with an active Max/Pro
|
|
7
|
+
# subscription accumulated approximately USD 1,050 in API charges
|
|
8
|
+
# across nine auto-refills over three days (2026-05-05 to 2026-05-07)
|
|
9
|
+
# because a dotenv-loaded ANTHROPIC_API_KEY silently overrode the
|
|
10
|
+
# subscription path. The UI displayed "Sonnet" throughout while
|
|
11
|
+
# billing routed through API-key Opus pricing. The operator had no
|
|
12
|
+
# in-session signal that the billing path had switched.
|
|
13
|
+
#
|
|
14
|
+
# The official support response was that this behavior is
|
|
15
|
+
# "intentional functionality" (the env var taking precedence over
|
|
16
|
+
# the subscription credential is documented somewhere but not on
|
|
17
|
+
# the path operators read), making operator-side detection the only
|
|
18
|
+
# reliable prevention.
|
|
19
|
+
#
|
|
20
|
+
# Earlier related case: a separate operator lost USD 187 in May
|
|
21
|
+
# 2026 from the same structural pattern (dotenv ANTHROPIC_API_KEY
|
|
22
|
+
# silently bypassing the subscription). The pattern is recurring.
|
|
23
|
+
#
|
|
24
|
+
# Class of failure: claim-vs-reality divergence at the billing-routing layer.
|
|
25
|
+
# The operator's mental model ("I subscribed, therefore I pay the
|
|
26
|
+
# subscription rate") and the runtime behavior ("API key in env
|
|
27
|
+
# takes precedence, so charges flow through metered API pricing")
|
|
28
|
+
# have diverged silently. The status surface (UI showing the
|
|
29
|
+
# expected model) reinforces the wrong mental model.
|
|
30
|
+
#
|
|
31
|
+
# HOW IT WORKS:
|
|
32
|
+
# At session start, inspect the resolved environment for ANTHROPIC_API_KEY
|
|
33
|
+
# (or related vendor-key env vars). If any are present, emit a SessionStart
|
|
34
|
+
# warning that explicitly states: (1) the env var was detected, (2) this
|
|
35
|
+
# bypasses subscription billing on the affected path, (3) the operator's
|
|
36
|
+
# choices are to unset the env var (subscription billing) or to
|
|
37
|
+
# acknowledge the API-key billing path explicitly.
|
|
38
|
+
#
|
|
39
|
+
# The hook reads the dotenv-style files Claude Code is known to load
|
|
40
|
+
# (~/.claude/.env, project .env, $HOME/.env) and surfaces which file is
|
|
41
|
+
# the source of the key, so the operator can disable the right line.
|
|
42
|
+
#
|
|
43
|
+
# WHY THIS MATTERS:
|
|
44
|
+
# Issue #60093 is one of the largest single-incident dollar losses in the
|
|
45
|
+
# May 2026 claim-vs-reality cluster (USD 1,050 across 9 auto-refills before
|
|
46
|
+
# the operator noticed). The cost is concentrated and irreversible at the
|
|
47
|
+
# billing layer (charges already accrued cannot be reversed by adjusting
|
|
48
|
+
# settings). Pre-session detection is the only reliable defense; in-session
|
|
49
|
+
# detection happens too late.
|
|
50
|
+
#
|
|
51
|
+
# TRIGGER: SessionStart
|
|
52
|
+
# MATCHER: ""
|
|
53
|
+
#
|
|
54
|
+
# CONFIGURATION:
|
|
55
|
+
# CC_SUBSCRIPTION_BYPASS_DISABLE=1 disable this hook entirely
|
|
56
|
+
# CC_SUBSCRIPTION_BYPASS_ACK=1 acknowledge API-key billing is intended
|
|
57
|
+
# (suppresses the warning but keeps the
|
|
58
|
+
# detection log entry)
|
|
59
|
+
# CC_SUBSCRIPTION_BYPASS_LOG=path append detection events to this file
|
|
60
|
+
# (default off)
|
|
61
|
+
#
|
|
62
|
+
# SAFETY:
|
|
63
|
+
# - Read-only on the environment and on .env files.
|
|
64
|
+
# - Does not modify, unset, or interact with the keys themselves.
|
|
65
|
+
# - Emits a warning to stderr; does not block the session.
|
|
66
|
+
# - Exit 0 always.
|
|
67
|
+
#
|
|
68
|
+
# REFERENCES:
|
|
69
|
+
# - anthropics/claude-code#60093 (2026-05-18, USD 1,050 case)
|
|
70
|
+
# - Earlier May 2026 case: USD 187 dotenv-bypass incident (community report,
|
|
71
|
+
# surfaced via a Qiita writeup published 2026-05-15)
|
|
72
|
+
set -euo pipefail
|
|
73
|
+
|
|
74
|
+
[ "${CC_SUBSCRIPTION_BYPASS_DISABLE:-}" = "1" ] && exit 0
|
|
75
|
+
|
|
76
|
+
# Collect signal sources. Each signal is reported only if the value is
|
|
77
|
+
# non-empty after the env var resolves.
|
|
78
|
+
declare -a SOURCES=()
|
|
79
|
+
|
|
80
|
+
# Direct env var check
|
|
81
|
+
if [ -n "${ANTHROPIC_API_KEY:-}" ]; then
|
|
82
|
+
SOURCES+=("process environment (ANTHROPIC_API_KEY is set in the shell that launched this session)")
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
# Look for the key in the standard dotenv search paths Claude Code is
|
|
86
|
+
# documented to consult.
|
|
87
|
+
check_dotenv_for_key() {
|
|
88
|
+
local path="$1"
|
|
89
|
+
local label="$2"
|
|
90
|
+
[ ! -f "$path" ] && return 0
|
|
91
|
+
if grep -qE '^[[:space:]]*ANTHROPIC_API_KEY[[:space:]]*=' "$path" 2>/dev/null; then
|
|
92
|
+
SOURCES+=("$label ($path)")
|
|
93
|
+
fi
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
check_dotenv_for_key "$HOME/.claude/.env" "Claude per-user dotenv"
|
|
97
|
+
check_dotenv_for_key "$HOME/.env" "user home dotenv"
|
|
98
|
+
check_dotenv_for_key "$PWD/.env" "project working-directory dotenv"
|
|
99
|
+
|
|
100
|
+
# Git-repo root dotenv (if we're inside a repo and it's different from cwd)
|
|
101
|
+
if command -v git >/dev/null 2>&1; then
|
|
102
|
+
GIT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || true)
|
|
103
|
+
if [ -n "$GIT_ROOT" ] && [ "$GIT_ROOT" != "$PWD" ]; then
|
|
104
|
+
check_dotenv_for_key "$GIT_ROOT/.env" "git repository root dotenv"
|
|
105
|
+
fi
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
# If no sources were found, exit silently.
|
|
109
|
+
if [ "${#SOURCES[@]}" -eq 0 ]; then
|
|
110
|
+
exit 0
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
# Build the warning body
|
|
114
|
+
SOURCE_LIST=""
|
|
115
|
+
for src in "${SOURCES[@]}"; do
|
|
116
|
+
SOURCE_LIST="${SOURCE_LIST}\n - ${src}"
|
|
117
|
+
done
|
|
118
|
+
|
|
119
|
+
WARNING_BODY="ANTHROPIC_API_KEY detected at session start. Source(s):${SOURCE_LIST}
|
|
120
|
+
|
|
121
|
+
Implication: if an ANTHROPIC_API_KEY is resolved in the environment, Claude Code routes billing through metered API-key pricing rather than your Max/Pro subscription, on the affected codepath. This is intentional per Anthropic support but it is not surfaced in-session — the UI continues to display the selected model normally while charges accumulate against the API key.
|
|
122
|
+
|
|
123
|
+
Issue #60093 (2026-05-18) documents a single operator accumulating ~USD 1,050 across 9 auto-refills over 3 days (2026-05-05 to 2026-05-07) before the divergence was noticed. An earlier May 2026 case surfaced a ~USD 187 loss from the same pattern. The cost is incurred at billing time and cannot be reversed by adjusting settings after the fact.
|
|
124
|
+
|
|
125
|
+
To use subscription billing on this session: unset ANTHROPIC_API_KEY in the shell (or remove the matching line from the source file above) and restart Claude Code.
|
|
126
|
+
|
|
127
|
+
To acknowledge that API-key billing is intended for this session, set CC_SUBSCRIPTION_BYPASS_ACK=1; the warning will be suppressed for future sessions but a log entry will still record the detection if CC_SUBSCRIPTION_BYPASS_LOG is set."
|
|
128
|
+
|
|
129
|
+
# Optional logging
|
|
130
|
+
if [ -n "${CC_SUBSCRIPTION_BYPASS_LOG:-}" ]; then
|
|
131
|
+
{
|
|
132
|
+
printf '[%s] subscription-bypass-detected: sources=%d sources_list=%s\n' \
|
|
133
|
+
"$(date -u +%Y-%m-%dT%H:%M:%SZ)" "${#SOURCES[@]}" "${SOURCES[*]}"
|
|
134
|
+
} >> "$CC_SUBSCRIPTION_BYPASS_LOG" 2>/dev/null || true
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
# Suppress visible warning if acknowledged
|
|
138
|
+
if [ "${CC_SUBSCRIPTION_BYPASS_ACK:-}" = "1" ]; then
|
|
139
|
+
exit 0
|
|
140
|
+
fi
|
|
141
|
+
|
|
142
|
+
# Emit warning via SessionStart hookSpecificOutput
|
|
143
|
+
jq -n \
|
|
144
|
+
--arg ctx "$(printf '%b' "$WARNING_BODY")" \
|
|
145
|
+
'{
|
|
146
|
+
hookSpecificOutput: {
|
|
147
|
+
hookEventName: "SessionStart",
|
|
148
|
+
additionalContext: $ctx
|
|
149
|
+
}
|
|
150
|
+
}'
|
|
151
|
+
|
|
152
|
+
exit 0
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
# }
|
|
19
19
|
# ================================================================
|
|
20
20
|
#
|
|
21
|
-
# TRIGGER:
|
|
21
|
+
# TRIGGER: PostToolUse MATCHER: "Edit|Write"
|
|
22
22
|
|
|
23
23
|
INPUT=$(cat)
|
|
24
24
|
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
|
|
@@ -20,6 +20,18 @@
|
|
|
20
20
|
# GitHub Issues: #36339 (93r), #764 (63r), #24964 (135r)
|
|
21
21
|
# ================================================================
|
|
22
22
|
|
|
23
|
+
# Without jq, the parse below silently yields empty and this hook stops
|
|
24
|
+
# guarding - with no error anywhere. Say so. We deliberately do not exit
|
|
25
|
+
# here: blocking would halt every tool call, and exiting 0 would change
|
|
26
|
+
# the behaviour of guards that do not depend on the parsed value.
|
|
27
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
28
|
+
_nojq_warned="/tmp/cc-nojq-warned-symlink-guard-$PPID"
|
|
29
|
+
[ -f "$_nojq_warned" ] || {
|
|
30
|
+
echo "WARNING [symlink-guard]: jq not found - this hook cannot inspect tool calls and is NOT protecting you. Install jq." >&2
|
|
31
|
+
: > "$_nojq_warned"
|
|
32
|
+
}
|
|
33
|
+
fi
|
|
34
|
+
|
|
23
35
|
INPUT=$(cat)
|
|
24
36
|
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
25
37
|
|
|
@@ -32,8 +44,21 @@ if ! echo "$COMMAND" | grep -qE '^\s*rm\s+.*-[rf]'; then
|
|
|
32
44
|
exit 0
|
|
33
45
|
fi
|
|
34
46
|
|
|
35
|
-
# Extract target path
|
|
36
|
-
|
|
47
|
+
# Extract target path.
|
|
48
|
+
# grep -P is GNU-only; macOS ships BSD grep, which rejects -P and returns nothing.
|
|
49
|
+
# TARGET would then be empty and the "[[ -z ]] && exit 0" below would let every
|
|
50
|
+
# rm through — the guard would stop guarding without saying anything. Measured:
|
|
51
|
+
# with -P the hook exits 2 on a symlink pointing outside the project, without it
|
|
52
|
+
# it exits 0 on the same input. \K has no POSIX equivalent, so the fallback
|
|
53
|
+
# matches from the command word and strips that prefix afterwards.
|
|
54
|
+
if echo x | grep -qP x 2>/dev/null; then
|
|
55
|
+
TARGET=$(echo "$COMMAND" | grep -oP 'rm\s+(-[rf]+\s+)*\K\S+' | tail -1)
|
|
56
|
+
else
|
|
57
|
+
TARGET=$(echo "$COMMAND" \
|
|
58
|
+
| grep -oE 'rm[[:space:]]+(-[rf]+[[:space:]]+)*[^[:space:]]+' \
|
|
59
|
+
| tail -1 \
|
|
60
|
+
| sed -E 's/^rm[[:space:]]+(-[rf]+[[:space:]]+)*//')
|
|
61
|
+
fi
|
|
37
62
|
|
|
38
63
|
if [[ -z "$TARGET" ]] || [[ ! -e "$TARGET" ]]; then
|
|
39
64
|
exit 0
|
|
@@ -16,15 +16,60 @@
|
|
|
16
16
|
|
|
17
17
|
set -euo pipefail
|
|
18
18
|
|
|
19
|
+
# Without jq, the parse below silently yields empty and this hook stops
|
|
20
|
+
# guarding - with no error anywhere. Say so. We deliberately do not exit
|
|
21
|
+
# here: blocking would halt every tool call, and exiting 0 would change
|
|
22
|
+
# the behaviour of guards that do not depend on the parsed value.
|
|
23
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
24
|
+
_nojq_warned="/tmp/cc-nojq-warned-system-dir-protection-guard-$PPID"
|
|
25
|
+
[ -f "$_nojq_warned" ] || {
|
|
26
|
+
echo "WARNING [system-dir-protection-guard]: jq not found - this hook cannot inspect tool calls and is NOT protecting you. Install jq." >&2
|
|
27
|
+
: > "$_nojq_warned"
|
|
28
|
+
}
|
|
29
|
+
fi
|
|
30
|
+
|
|
19
31
|
INPUT=$(cat)
|
|
20
32
|
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
33
|
+
|
|
34
|
+
# --- Look inside wrappers -----------------------------------------------------
|
|
35
|
+
# The three gates below open only when the command *starts* with rm / mv /
|
|
36
|
+
# chmod, so a command inside a substitution, a subshell, or after a shell
|
|
37
|
+
# keyword was never examined. Measured 2026-08-04: this hook blocked the bare
|
|
38
|
+
# deletion and passed all six wrappings of it. Claude Code 2.1.221 fixed the
|
|
39
|
+
# same shape in its own permission check.
|
|
40
|
+
#
|
|
41
|
+
# Replace the wrapping tokens with separators and run this same script once more
|
|
42
|
+
# against that text. Detection only: never executed, and the message keeps the
|
|
43
|
+
# command the user actually sent.
|
|
44
|
+
if [ -n "${CC_SYSDIR_UNWRAPPED:-}" ]; then
|
|
45
|
+
COMMAND="$CC_SYSDIR_UNWRAPPED"
|
|
46
|
+
elif [ -n "$COMMAND" ]; then
|
|
47
|
+
_sysdir_unwrapped=$(printf '%s' "$COMMAND" | sed -E \
|
|
48
|
+
-e 's/\$\(/ ; /g' -e 's/`/ ; /g' \
|
|
49
|
+
-e 's/(^|[[:space:]])\(/\1 ; /g' -e 's/\)([[:space:]]|$)/ ; \1/g' \
|
|
50
|
+
-e 's/(^|[[:space:]])(then|do|else|elif)([[:space:]])/\1 ; \3/g')
|
|
51
|
+
if [ "$_sysdir_unwrapped" != "$COMMAND" ]; then
|
|
52
|
+
CC_SYSDIR_UNWRAPPED="$_sysdir_unwrapped" bash "$0" </dev/null >/dev/null 2>&1
|
|
53
|
+
if [ "$?" = "2" ]; then
|
|
54
|
+
echo "BLOCKED: an operation on a system directory is hidden inside a wrapper." >&2
|
|
55
|
+
echo "Command: $COMMAND" >&2
|
|
56
|
+
echo "A substitution, a subshell or a shell keyword does not make it safe." >&2
|
|
57
|
+
exit 2
|
|
58
|
+
fi
|
|
59
|
+
fi
|
|
60
|
+
fi
|
|
21
61
|
[ -z "$COMMAND" ] && exit 0
|
|
22
62
|
|
|
23
63
|
# Check if a path is a protected system directory
|
|
24
64
|
is_system_dir() {
|
|
25
65
|
local path="$1"
|
|
26
|
-
# Remove trailing slash
|
|
27
|
-
|
|
66
|
+
# Remove a trailing slash -- but never on the root itself.
|
|
67
|
+
# `${path%/}` turns "/" into "", so the `/` branch in the case below (which
|
|
68
|
+
# the author did write) could never be reached, and `rm -rf /`, `mv / …`
|
|
69
|
+
# and `chmod -R 777 /` all walked through while `/etc` was blocked.
|
|
70
|
+
# Measured 2026-08-03: the root was the one directory this guard named and
|
|
71
|
+
# then erased one line before checking it.
|
|
72
|
+
[ "$path" != "/" ] && path="${path%/}"
|
|
28
73
|
|
|
29
74
|
# Expand ~ to $HOME
|
|
30
75
|
if [[ "$path" == "~"* ]]; then
|
|
@@ -55,10 +100,29 @@ is_system_dir() {
|
|
|
55
100
|
return 1
|
|
56
101
|
}
|
|
57
102
|
|
|
103
|
+
# The extractions below used grep -oP, which is GNU-only. BSD grep (macOS)
|
|
104
|
+
# rejects -P outright, so every target list came back empty, the loops below
|
|
105
|
+
# had nothing to iterate, and the guard exited 0 — `rm -rf /root` went through
|
|
106
|
+
# on macOS with no sign that the protection was off. The GNU path is unchanged;
|
|
107
|
+
# an equivalent is added for platforms without PCRE.
|
|
108
|
+
if echo x | grep -qP x 2>/dev/null; then HAS_PCRE=1; else HAS_PCRE=0; fi
|
|
109
|
+
|
|
110
|
+
# $1 = command alternation (e.g. "rm|unlink"), $2 = operand pattern.
|
|
111
|
+
# \K has no POSIX equivalent, so the fallback matches the whole thing and
|
|
112
|
+
# strips the command word and its flags afterwards.
|
|
113
|
+
extract_operand() {
|
|
114
|
+
if [ "$HAS_PCRE" = 1 ]; then
|
|
115
|
+
grep -oP "($1)\\s+(-[a-zA-Z]+\\s+)*\\K$2" 2>/dev/null || true
|
|
116
|
+
else
|
|
117
|
+
grep -oE "($1)[[:space:]]+(-[a-zA-Z]+[[:space:]]+)*$2" 2>/dev/null \
|
|
118
|
+
| sed -E "s/^($1)[[:space:]]+(-[a-zA-Z]+[[:space:]]+)*//" || true
|
|
119
|
+
fi
|
|
120
|
+
}
|
|
121
|
+
|
|
58
122
|
# --- rm / unlink on system directories ---
|
|
59
|
-
if echo "$COMMAND" | grep -qE '
|
|
123
|
+
if echo "$COMMAND" | grep -qE '(^|[;&|])[[:space:]]*(sudo[[:space:]]+)?(rm|unlink)[[:space:]]'; then
|
|
60
124
|
# Extract targets after rm and flags
|
|
61
|
-
TARGETS=$(echo "$COMMAND" |
|
|
125
|
+
TARGETS=$(echo "$COMMAND" | extract_operand 'rm|unlink' '[^;|&]+')
|
|
62
126
|
for target in $TARGETS; do
|
|
63
127
|
if is_system_dir "$target"; then
|
|
64
128
|
echo "BLOCKED: Destructive operation on system directory: $target" >&2
|
|
@@ -72,9 +136,9 @@ if echo "$COMMAND" | grep -qE '^\s*(sudo\s+)?(rm|unlink)\s'; then
|
|
|
72
136
|
fi
|
|
73
137
|
|
|
74
138
|
# --- mv (moving system directories) ---
|
|
75
|
-
if echo "$COMMAND" | grep -qE '
|
|
139
|
+
if echo "$COMMAND" | grep -qE '(^|[;&|])[[:space:]]*(sudo[[:space:]]+)?mv[[:space:]]'; then
|
|
76
140
|
# Get the source of the mv (first non-flag argument)
|
|
77
|
-
MV_SOURCE=$(echo "$COMMAND" |
|
|
141
|
+
MV_SOURCE=$(echo "$COMMAND" | extract_operand 'mv' '[^[:space:]]+')
|
|
78
142
|
if is_system_dir "$MV_SOURCE"; then
|
|
79
143
|
echo "BLOCKED: Moving system directory: $MV_SOURCE" >&2
|
|
80
144
|
echo "Command: $COMMAND" >&2
|
|
@@ -86,8 +150,9 @@ if echo "$COMMAND" | grep -qE '^\s*(sudo\s+)?mv\s'; then
|
|
|
86
150
|
fi
|
|
87
151
|
|
|
88
152
|
# --- chmod -R / chown -R on system directories ---
|
|
89
|
-
if echo "$COMMAND" | grep -qE '
|
|
90
|
-
|
|
153
|
+
if echo "$COMMAND" | grep -qE '(^|[;&|])[[:space:]]*(sudo[[:space:]]+)?(chmod|chown)[[:space:]]+.*-R'; then
|
|
154
|
+
# No \K here, so plain ERE is enough (POSIX classes instead of \s).
|
|
155
|
+
TARGETS=$(echo "$COMMAND" | grep -oE '(chmod|chown)[[:space:]]+[^;|&]+' 2>/dev/null | awk '{print $NF}' || true)
|
|
91
156
|
for target in $TARGETS; do
|
|
92
157
|
if is_system_dir "$target"; then
|
|
93
158
|
echo "BLOCKED: Recursive permission change on system directory: $target" >&2
|