cc-safe-setup 29.7.0 → 30.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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-circuit-breaker.sh +72 -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 +89 -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/thinking-stall-detector.sh +61 -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-circuit-breaker.sh +134 -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-exploration-budget-guard.sh +164 -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-thinking-stall-detector.sh +151 -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,27 +8,64 @@
|
|
|
8
8
|
# user approval. This hook enforces the restriction at process level.
|
|
9
9
|
#
|
|
10
10
|
# Blocks:
|
|
11
|
-
# git commit, git push (including --force
|
|
12
|
-
# git switch -c, git branch <name>
|
|
11
|
+
# git commit, git push (including --force and `git send-pack`),
|
|
12
|
+
# git checkout -b, git switch -c, git branch <name> - including when
|
|
13
|
+
# they arrive with git's own global options in front, such as
|
|
14
|
+
# `git -C <dir> push` or `git --git-dir=<path> commit` (#1117).
|
|
13
15
|
#
|
|
14
16
|
# Does NOT block:
|
|
15
17
|
# git status, git log, git diff, git show, git branch (list),
|
|
16
18
|
# git fetch, git stash, git add
|
|
17
19
|
#
|
|
18
|
-
# Handles compound commands (&&, ;,
|
|
20
|
+
# Handles compound commands (&&, ;, ||, |) by checking each segment.
|
|
19
21
|
#
|
|
20
22
|
# See: https://github.com/anthropics/claude-code/issues/40695
|
|
21
23
|
#
|
|
22
24
|
# TRIGGER: PreToolUse MATCHER: "Bash"
|
|
23
25
|
# ================================================================
|
|
24
26
|
|
|
27
|
+
# Without jq, the parse below silently yields empty and this hook stops
|
|
28
|
+
# guarding - with no error anywhere. Say so. We deliberately do not exit
|
|
29
|
+
# here: blocking would halt every tool call, and exiting 0 would change
|
|
30
|
+
# the behaviour of guards that do not depend on the parsed value.
|
|
31
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
32
|
+
_nojq_warned="/tmp/cc-nojq-warned-git-operations-require-approval-$PPID"
|
|
33
|
+
[ -f "$_nojq_warned" ] || {
|
|
34
|
+
echo "WARNING [git-operations-require-approval]: jq not found - this hook cannot inspect tool calls and is NOT protecting you. Install jq." >&2
|
|
35
|
+
: > "$_nojq_warned"
|
|
36
|
+
}
|
|
37
|
+
fi
|
|
38
|
+
|
|
25
39
|
INPUT=$(cat)
|
|
26
40
|
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
27
41
|
|
|
28
42
|
[ -z "$COMMAND" ] && exit 0
|
|
29
43
|
|
|
30
|
-
#
|
|
31
|
-
|
|
44
|
+
# git accepts global options between `git` and the subcommand, so matching the
|
|
45
|
+
# two as adjacent words misses every one of these real shapes (reported in #1117
|
|
46
|
+
# with four working examples):
|
|
47
|
+
# git -C /tmp/repo push --force origin main
|
|
48
|
+
# git --git-dir=/tmp/r/repo.d push origin main
|
|
49
|
+
# git -c user.name=x commit -m x
|
|
50
|
+
# git send-pack origin main
|
|
51
|
+
# GIT_OPT is the set of options git itself accepts before a subcommand.
|
|
52
|
+
GIT_OPT='(-[cC][[:space:]]+[^[:space:]]+|--(git-dir|work-tree|namespace|exec-path|config-env|attr-source|super-prefix)(=[^[:space:]]+|[[:space:]]+[^[:space:]]+)|-[pP]|--(paginate|no-pager|bare|no-replace-objects|literal-pathspecs|glob-pathspecs|noglob-pathspecs|icase-pathspecs|no-optional-locks|no-lazy-fetch|no-advice))'
|
|
53
|
+
|
|
54
|
+
# `git` at a command position, not the `git` inside a path. The old pattern
|
|
55
|
+
# matched `git --git-dir=/tmp/r/.git push` only because the *path* ended in
|
|
56
|
+
# `.git`, and let the same command through when the git-dir was named anything
|
|
57
|
+
# else (#1117). Quotes are allowed on purpose: `bash -c "git push ..."` runs.
|
|
58
|
+
GIT_HEAD='(^|[[:space:];&|(]|/|"|'"'"')git'
|
|
59
|
+
|
|
60
|
+
# Build "git [global options] <subcommand>" for one subcommand pattern.
|
|
61
|
+
git_re() {
|
|
62
|
+
printf '%s([[:space:]]+%s)*[[:space:]]+%s' "$GIT_HEAD" "$GIT_OPT" "$1"
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
# Read-only front halves that mention a command without running it.
|
|
66
|
+
# This list is a guess about how people write, not a rule - `sed -i` and any
|
|
67
|
+
# wrapper not named here will still be inspected.
|
|
68
|
+
READ_ONLY_RE='^[[:space:]]*(echo|printf|grep|rg|cat|less|head|tail)[[:space:]]'
|
|
32
69
|
|
|
33
70
|
# Check each segment of compound commands
|
|
34
71
|
check_segment() {
|
|
@@ -37,8 +74,12 @@ check_segment() {
|
|
|
37
74
|
seg=$(echo "$seg" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
38
75
|
[ -z "$seg" ] && return 0
|
|
39
76
|
|
|
77
|
+
# Exempt only THIS segment. Applying it to the whole command let
|
|
78
|
+
# `echo hi && git push origin main` through completely.
|
|
79
|
+
echo "$seg" | grep -qE "$READ_ONLY_RE" && return 0
|
|
80
|
+
|
|
40
81
|
# git commit
|
|
41
|
-
if echo "$seg" | grep -qE '\
|
|
82
|
+
if echo "$seg" | grep -qE "$(git_re 'commit\b')|${GIT_HEAD}-commit\b"; then
|
|
42
83
|
echo "BLOCKED: git commit requires explicit user approval." >&2
|
|
43
84
|
echo "Command: $seg" >&2
|
|
44
85
|
echo "" >&2
|
|
@@ -47,7 +88,7 @@ check_segment() {
|
|
|
47
88
|
fi
|
|
48
89
|
|
|
49
90
|
# git push (including force variants)
|
|
50
|
-
if echo "$seg" | grep -qE '\
|
|
91
|
+
if echo "$seg" | grep -qE "$(git_re '(push|send-pack)\b')|${GIT_HEAD}-(push|send-pack)\b"; then
|
|
51
92
|
echo "BLOCKED: git push requires explicit user approval." >&2
|
|
52
93
|
echo "Command: $seg" >&2
|
|
53
94
|
echo "" >&2
|
|
@@ -56,14 +97,14 @@ check_segment() {
|
|
|
56
97
|
fi
|
|
57
98
|
|
|
58
99
|
# git checkout -b (branch creation)
|
|
59
|
-
if echo "$seg" | grep -qE '
|
|
100
|
+
if echo "$seg" | grep -qE "$(git_re 'checkout[[:space:]]+(-b|--branch)\b')"; then
|
|
60
101
|
echo "BLOCKED: git branch creation requires explicit user approval." >&2
|
|
61
102
|
echo "Command: $seg" >&2
|
|
62
103
|
return 1
|
|
63
104
|
fi
|
|
64
105
|
|
|
65
106
|
# git switch -c / --create (branch creation)
|
|
66
|
-
if echo "$seg" | grep -qE '
|
|
107
|
+
if echo "$seg" | grep -qE "$(git_re 'switch[[:space:]]+(-c|--create)\b')"; then
|
|
67
108
|
echo "BLOCKED: git branch creation requires explicit user approval." >&2
|
|
68
109
|
echo "Command: $seg" >&2
|
|
69
110
|
return 1
|
|
@@ -71,14 +112,17 @@ check_segment() {
|
|
|
71
112
|
|
|
72
113
|
# git branch <name> (creation, not listing)
|
|
73
114
|
# git branch without flags or with only -a/-r/-l/--list is listing
|
|
74
|
-
if echo "$seg" | grep -qE '
|
|
115
|
+
if echo "$seg" | grep -qE "$(git_re 'branch[[:space:]]')"; then
|
|
75
116
|
# Allow listing flags
|
|
76
|
-
if echo "$seg" | grep -qE '
|
|
117
|
+
if echo "$seg" | grep -qE "$(git_re 'branch[[:space:]]+(-[arl]|--list|--merged|--no-merged|--contains|-v|--verbose|-d|--delete|-D)\b')"; then
|
|
77
118
|
return 0
|
|
78
119
|
fi
|
|
79
120
|
# If it has a name argument after "git branch", it's creation
|
|
80
121
|
local args
|
|
81
|
-
|
|
122
|
+
# The old sed required `git` and `branch` to be adjacent; with a
|
|
123
|
+
# global option in between it stripped nothing and every listing form
|
|
124
|
+
# looked like a creation. Strip up to the subcommand instead.
|
|
125
|
+
args=$(echo "$seg" | sed -E 's/.*[[:space:]]branch[[:space:]]+//')
|
|
82
126
|
if [ -n "$args" ] && ! echo "$args" | grep -qE '^\s*$'; then
|
|
83
127
|
echo "BLOCKED: git branch creation requires explicit user approval." >&2
|
|
84
128
|
echo "Command: $seg" >&2
|
|
@@ -89,11 +133,21 @@ check_segment() {
|
|
|
89
133
|
return 0
|
|
90
134
|
}
|
|
91
135
|
|
|
92
|
-
# Split on && ; || and check each part
|
|
136
|
+
# Split on && ; || | and check each part.
|
|
137
|
+
#
|
|
138
|
+
# A single `|` is a separator too. Without it, a read-only command piped into
|
|
139
|
+
# a git write arrives as ONE segment whose first word is the reader, and
|
|
140
|
+
# READ_ONLY_RE above exempts that whole segment - so the git write on the
|
|
141
|
+
# right-hand side of the pipe is never examined. Measured on the published
|
|
142
|
+
# file 2026-09-15: four piped shapes exited 0 while the same git operations
|
|
143
|
+
# exited 2 when written plainly.
|
|
144
|
+
#
|
|
145
|
+
# `||` is already turned into newlines by the rule before this one, so the
|
|
146
|
+
# single-pipe rule only ever sees real single pipes.
|
|
93
147
|
while IFS= read -r segment; do
|
|
94
148
|
if ! check_segment "$segment"; then
|
|
95
149
|
exit 2
|
|
96
150
|
fi
|
|
97
|
-
done < <(echo "$COMMAND" | sed 's/&&/\n/g; s/;/\n/g; s/||/\n/g')
|
|
151
|
+
done < <(echo "$COMMAND" | sed 's/&&/\n/g; s/;/\n/g; s/||/\n/g; s/|/\n/g')
|
|
98
152
|
|
|
99
153
|
exit 0
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ================================================================
|
|
3
|
+
# git-pack-temp-cleanup-guard.sh — Stop tmp_pack_* explosion
|
|
4
|
+
# ================================================================
|
|
5
|
+
# PURPOSE:
|
|
6
|
+
# Subagents and parallel git operations leave stale
|
|
7
|
+
# .git/objects/pack/tmp_pack_* files behind when git gc / repack
|
|
8
|
+
# fails or is interrupted. These accumulate without bound and can
|
|
9
|
+
# fill ext4.vhdx (observed: 1,350 files / 144 GB on a single repo).
|
|
10
|
+
#
|
|
11
|
+
# This hook runs before any git command. It removes tmp_pack_*
|
|
12
|
+
# files older than CC_GIT_PACK_AGE_MIN minutes, then checks the
|
|
13
|
+
# remaining total. Above thresholds, it warns or blocks.
|
|
14
|
+
#
|
|
15
|
+
# TRIGGER: PreToolUse MATCHER: "Bash"
|
|
16
|
+
#
|
|
17
|
+
# CONFIG:
|
|
18
|
+
# CC_GIT_PACK_AGE_MIN=60 minutes; older tmp_pack_* are removed
|
|
19
|
+
# CC_GIT_PACK_WARN_GB=1 warn at this many GiB remaining
|
|
20
|
+
# CC_GIT_PACK_STRONG_GB=5 strong warning at this many GiB
|
|
21
|
+
# CC_GIT_PACK_BLOCK_GB=10 block git command at this many GiB
|
|
22
|
+
# CC_GIT_PACK_DISABLE=1 set to disable this hook entirely
|
|
23
|
+
# ================================================================
|
|
24
|
+
|
|
25
|
+
# Without jq, the parse below silently yields empty and this hook stops
|
|
26
|
+
# guarding - with no error anywhere. Say so. We deliberately do not exit
|
|
27
|
+
# here: blocking would halt every tool call, and exiting 0 would change
|
|
28
|
+
# the behaviour of guards that do not depend on the parsed value.
|
|
29
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
30
|
+
_nojq_warned="/tmp/cc-nojq-warned-git-pack-temp-cleanup-guard-$PPID"
|
|
31
|
+
[ -f "$_nojq_warned" ] || {
|
|
32
|
+
echo "WARNING [git-pack-temp-cleanup-guard]: jq not found - this hook cannot inspect tool calls and is NOT protecting you. Install jq." >&2
|
|
33
|
+
: > "$_nojq_warned"
|
|
34
|
+
}
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
[ "${CC_GIT_PACK_DISABLE:-0}" = "1" ] && exit 0
|
|
38
|
+
|
|
39
|
+
INPUT=$(cat)
|
|
40
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
41
|
+
|
|
42
|
+
[ -z "$COMMAND" ] && exit 0
|
|
43
|
+
|
|
44
|
+
# Only act on git commands. \bgit\b matches git as a whole word, so
|
|
45
|
+
# pathnames like /usr/bin/gitlab won't trigger.
|
|
46
|
+
echo "$COMMAND" | grep -qE '(^|[^a-zA-Z0-9._-])git([^a-zA-Z0-9._-]|$)' || exit 0
|
|
47
|
+
|
|
48
|
+
AGE_MIN="${CC_GIT_PACK_AGE_MIN:-60}"
|
|
49
|
+
WARN_GB="${CC_GIT_PACK_WARN_GB:-1}"
|
|
50
|
+
STRONG_GB="${CC_GIT_PACK_STRONG_GB:-5}"
|
|
51
|
+
BLOCK_GB="${CC_GIT_PACK_BLOCK_GB:-10}"
|
|
52
|
+
|
|
53
|
+
# Resolve the .git directory of the current repo. If we are outside a
|
|
54
|
+
# git repo, exit silently — the user's git command will fail on its own.
|
|
55
|
+
GIT_DIR=$(git rev-parse --git-dir 2>/dev/null) || exit 0
|
|
56
|
+
PACK_DIR="$GIT_DIR/objects/pack"
|
|
57
|
+
|
|
58
|
+
[ -d "$PACK_DIR" ] || exit 0
|
|
59
|
+
|
|
60
|
+
# Step 1: remove tmp_pack_* older than AGE_MIN minutes.
|
|
61
|
+
# Use -maxdepth 1 so we never recurse. Only match the literal prefix.
|
|
62
|
+
removed_count=0
|
|
63
|
+
removed_bytes=0
|
|
64
|
+
while IFS= read -r -d '' f; do
|
|
65
|
+
sz=$(stat -c '%s' "$f" 2>/dev/null || echo 0)
|
|
66
|
+
if rm -f "$f" 2>/dev/null; then
|
|
67
|
+
removed_count=$((removed_count + 1))
|
|
68
|
+
removed_bytes=$((removed_bytes + sz))
|
|
69
|
+
fi
|
|
70
|
+
done < <(find "$PACK_DIR" -maxdepth 1 -type f -name 'tmp_pack_*' -mmin "+$AGE_MIN" -print0 2>/dev/null)
|
|
71
|
+
|
|
72
|
+
if [ "$removed_count" -gt 0 ]; then
|
|
73
|
+
removed_mib=$((removed_bytes / 1024 / 1024))
|
|
74
|
+
echo "INFO: git-pack-temp-cleanup-guard removed $removed_count stale tmp_pack_* files (${removed_mib} MiB) older than ${AGE_MIN} min in $PACK_DIR" >&2
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
# Step 2: compute remaining total size of tmp_pack_*.
|
|
78
|
+
remaining_bytes=0
|
|
79
|
+
while IFS= read -r -d '' f; do
|
|
80
|
+
sz=$(stat -c '%s' "$f" 2>/dev/null || echo 0)
|
|
81
|
+
remaining_bytes=$((remaining_bytes + sz))
|
|
82
|
+
done < <(find "$PACK_DIR" -maxdepth 1 -type f -name 'tmp_pack_*' -print0 2>/dev/null)
|
|
83
|
+
|
|
84
|
+
remaining_gb=$((remaining_bytes / 1024 / 1024 / 1024))
|
|
85
|
+
|
|
86
|
+
if [ "$remaining_gb" -ge "$BLOCK_GB" ]; then
|
|
87
|
+
echo "BLOCKED: tmp_pack_* total is ${remaining_gb} GiB in $PACK_DIR (threshold ${BLOCK_GB} GiB)." >&2
|
|
88
|
+
echo " Past incidents accumulated 144 GiB and filled ext4.vhdx." >&2
|
|
89
|
+
echo " Investigate before running more git commands. Inspect with:" >&2
|
|
90
|
+
echo " find \"$PACK_DIR\" -maxdepth 1 -name 'tmp_pack_*' -printf '%s %p\\n'" >&2
|
|
91
|
+
echo " Override (one shot) with: CC_GIT_PACK_DISABLE=1 git ..." >&2
|
|
92
|
+
exit 2
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
if [ "$remaining_gb" -ge "$STRONG_GB" ]; then
|
|
96
|
+
echo "STRONG WARNING: tmp_pack_* total is ${remaining_gb} GiB in $PACK_DIR (strong threshold ${STRONG_GB} GiB)." >&2
|
|
97
|
+
echo " Investigate parallel git activity before this grows further." >&2
|
|
98
|
+
elif [ "$remaining_gb" -ge "$WARN_GB" ]; then
|
|
99
|
+
echo "WARNING: tmp_pack_* total is ${remaining_gb} GiB in $PACK_DIR (threshold ${WARN_GB} GiB)." >&2
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
exit 0
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# git-push-blast-radius-guard.sh — Cap a runaway fan-out of git pushes that burns CI minutes
|
|
3
|
+
#
|
|
4
|
+
# Solves: When a bug touches many open PRs, the agent can "fix each branch
|
|
5
|
+
# individually" — cherry-pick the same change onto branch after branch and push
|
|
6
|
+
# each one. Every push to a distinct branch triggers the full CI suite, so the
|
|
7
|
+
# cost scales with the number of branches, not the size of the fix. In #65944 a
|
|
8
|
+
# 1-line lint fix was pushed to 13 PR branches across 3 rounds (~150+ workflow
|
|
9
|
+
# runs), exhausting the entire 2,000-minute monthly GitHub Actions allotment in
|
|
10
|
+
# under 3 hours and tripping an account-level Actions block. The operator's own
|
|
11
|
+
# root-cause note: the correct shape was "fix once on main, let PRs inherit it
|
|
12
|
+
# via rebase — 1 CI run instead of 150+."
|
|
13
|
+
#
|
|
14
|
+
# Existing hooks miss this exact shape:
|
|
15
|
+
# - git-operations-require-approval.sh blocks *every* git push (all-or-nothing,
|
|
16
|
+
# off by default); it never counts how many distinct branches a session has
|
|
17
|
+
# pushed, so it can't tell a normal 1-branch push from a 13-branch fan-out.
|
|
18
|
+
# - same-command-repeat-detector.sh keys on the *same* command repeating; a
|
|
19
|
+
# fan-out pushes a *different* branch each time, so it never matches (the
|
|
20
|
+
# same blind spot loop-detector.sh had before webfetch-runaway-guard.sh).
|
|
21
|
+
# - tool-call-rate-limiter.sh counts *all* tool calls in a short window; pushes
|
|
22
|
+
# spread across hours slip under it and it cannot see "distinct branches."
|
|
23
|
+
# - parallel-batch-size-limiter.sh measures *concurrent* batches, not sequential
|
|
24
|
+
# pushes to many branches across rounds.
|
|
25
|
+
#
|
|
26
|
+
# This counts the SET of distinct branches pushed in a rolling per-session window
|
|
27
|
+
# and, once that set crosses a threshold, surfaces the fix-on-main-then-rebase
|
|
28
|
+
# pattern and (by default) blocks the next branch push — the confirmation the
|
|
29
|
+
# operator wished for ("shouldn't push to more than 2-3 branches without
|
|
30
|
+
# confirmation"). The block is recoverable: raise the threshold, switch to warn,
|
|
31
|
+
# or set off. Re-pushing the *same* branch never inflates the count, so an
|
|
32
|
+
# ordinary edit-push-edit-push loop on one feature branch is untouched; only a
|
|
33
|
+
# genuine multi-branch fan-out trips it. `git push --all` / `--mirror` pushes
|
|
34
|
+
# every local branch at once (maximum blast radius) and is treated as a trip
|
|
35
|
+
# immediately.
|
|
36
|
+
#
|
|
37
|
+
# TRIGGER: PreToolUse MATCHER: "Bash"
|
|
38
|
+
# Config (all optional):
|
|
39
|
+
# CC_PUSH_BLAST_GUARD=block block | warn | off
|
|
40
|
+
# CC_PUSH_BLAST_BLOCK=6 block once this many distinct branches fall in the window
|
|
41
|
+
# CC_PUSH_BLAST_WARN=3 warn (stderr, non-blocking) at this many
|
|
42
|
+
# CC_PUSH_BLAST_WINDOW=3600 rolling window in seconds (default 1 hour)
|
|
43
|
+
# CC_PUSH_BLAST_DIR state dir (default: /tmp/cc-push-blast)
|
|
44
|
+
# Related: https://github.com/anthropics/claude-code/issues/65944
|
|
45
|
+
|
|
46
|
+
INPUT=$(cat)
|
|
47
|
+
|
|
48
|
+
MODE="${CC_PUSH_BLAST_GUARD:-block}"
|
|
49
|
+
[ "$MODE" = "off" ] && exit 0
|
|
50
|
+
|
|
51
|
+
# Only act on Bash. Fail open on anything unexpected.
|
|
52
|
+
TOOL=$(printf '%s' "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
53
|
+
[ "$TOOL" != "Bash" ] && exit 0
|
|
54
|
+
|
|
55
|
+
COMMAND=$(printf '%s' "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
56
|
+
[ -z "$COMMAND" ] && exit 0
|
|
57
|
+
|
|
58
|
+
# git accepts global options between `git` and the subcommand, so `git push`
|
|
59
|
+
# as two adjacent words never sees `git -C <dir> push` - which is exactly how
|
|
60
|
+
# an agent pushes to branch after branch without changing directory. Reported
|
|
61
|
+
# in #1117. `git send-pack` is the plumbing form of the same operation.
|
|
62
|
+
GIT_OPT='(-[cC][[:space:]]+[^[:space:]]+|--(git-dir|work-tree|namespace|exec-path|config-env|attr-source|super-prefix)(=[^[:space:]]+|[[:space:]]+[^[:space:]]+)|-[pP]|--(paginate|no-pager|bare|no-replace-objects|literal-pathspecs|glob-pathspecs|noglob-pathspecs|icase-pathspecs|no-optional-locks|no-lazy-fetch|no-advice))'
|
|
63
|
+
GIT_HEAD='(^|[[:space:];&|(]|/|"|'"'"')git'
|
|
64
|
+
GIT_PUSH_RE="${GIT_HEAD}([[:space:]]+${GIT_OPT})*[[:space:]]+(push|send-pack)\\b|${GIT_HEAD}-(push|send-pack)\\b"
|
|
65
|
+
|
|
66
|
+
# Quick reject: nothing to do unless the command runs `git push` somewhere.
|
|
67
|
+
printf '%s' "$COMMAND" | grep -qE "$GIT_PUSH_RE" || exit 0
|
|
68
|
+
|
|
69
|
+
BLOCK_AT="${CC_PUSH_BLAST_BLOCK:-6}"
|
|
70
|
+
WARN_AT="${CC_PUSH_BLAST_WARN:-3}"
|
|
71
|
+
WINDOW="${CC_PUSH_BLAST_WINDOW:-3600}"
|
|
72
|
+
case "$BLOCK_AT" in ''|*[!0-9]*) exit 0;; esac
|
|
73
|
+
case "$WARN_AT" in ''|*[!0-9]*) exit 0;; esac
|
|
74
|
+
case "$WINDOW" in ''|*[!0-9]*) exit 0;; esac
|
|
75
|
+
|
|
76
|
+
# Key state per session so one session's work never trips another's guard.
|
|
77
|
+
SID=$(printf '%s' "$INPUT" | jq -r '.session_id // "default"' 2>/dev/null)
|
|
78
|
+
case "$SID" in ''|*[!A-Za-z0-9_-]*) SID="default";; esac
|
|
79
|
+
|
|
80
|
+
# ---- Work out which branch(es) this command pushes to --------------------
|
|
81
|
+
# A command may chain several segments (&&, ;, newline, |). Inspect each git
|
|
82
|
+
# push segment and collect the branch name(s) it targets. Unknown/odd shapes
|
|
83
|
+
# fail open (they simply contribute nothing rather than guessing wrong).
|
|
84
|
+
FANOUT=0 # set when --all/--mirror pushes every branch at once
|
|
85
|
+
declare -a BRANCHES=()
|
|
86
|
+
|
|
87
|
+
# Split into segments on shell separators so each `git push ...` is seen alone.
|
|
88
|
+
SEGMENTS=$(printf '%s' "$COMMAND" | tr '\n;|' '\n\n\n' | sed 's/&&/\n/g')
|
|
89
|
+
|
|
90
|
+
while IFS= read -r seg; do
|
|
91
|
+
printf '%s' "$seg" | grep -qE "$GIT_PUSH_RE" || continue
|
|
92
|
+
|
|
93
|
+
# Tokens after the subcommand. The old `sed 's/.*\bgit[[:space:]]+push//'`
|
|
94
|
+
# stripped nothing when a global option sat between `git` and `push`, so the
|
|
95
|
+
# whole command ended up parsed as if it were the argument list (#1117).
|
|
96
|
+
# Walking the tokens has no such failure mode.
|
|
97
|
+
# shellcheck disable=SC2206
|
|
98
|
+
_all=( $seg )
|
|
99
|
+
after=""
|
|
100
|
+
_i=0
|
|
101
|
+
while [ "$_i" -lt "${#_all[@]}" ]; do
|
|
102
|
+
case "${_all[$_i]}" in
|
|
103
|
+
push|send-pack) after="${_all[*]:$((_i + 1))}"; break ;;
|
|
104
|
+
esac
|
|
105
|
+
_i=$((_i + 1))
|
|
106
|
+
done
|
|
107
|
+
|
|
108
|
+
# Whole-repo fan-out: one command, every branch, maximum CI blast radius.
|
|
109
|
+
if printf '%s' "$after" | grep -qE '(^|[[:space:]])(--all|--mirror)([[:space:]]|$)'; then
|
|
110
|
+
FANOUT=1
|
|
111
|
+
continue
|
|
112
|
+
fi
|
|
113
|
+
# shellcheck disable=SC2206
|
|
114
|
+
toks=( $after )
|
|
115
|
+
|
|
116
|
+
refspecs=()
|
|
117
|
+
seen_remote=0
|
|
118
|
+
i=0
|
|
119
|
+
while [ "$i" -lt "${#toks[@]}" ]; do
|
|
120
|
+
t="${toks[$i]}"
|
|
121
|
+
i=$((i + 1))
|
|
122
|
+
case "$t" in
|
|
123
|
+
# Flags that take a separate value -> skip the value too.
|
|
124
|
+
--repo|-o|--push-option|--receive-pack|--exec)
|
|
125
|
+
i=$((i + 1)); continue ;;
|
|
126
|
+
-*) continue ;; # any other flag
|
|
127
|
+
esac
|
|
128
|
+
# First bare token is the remote (origin / upstream / a URL). After that,
|
|
129
|
+
# bare tokens are refspecs (branches).
|
|
130
|
+
if [ "$seen_remote" -eq 0 ]; then
|
|
131
|
+
seen_remote=1
|
|
132
|
+
continue
|
|
133
|
+
fi
|
|
134
|
+
refspecs+=("$t")
|
|
135
|
+
done
|
|
136
|
+
|
|
137
|
+
if [ "${#refspecs[@]}" -eq 0 ]; then
|
|
138
|
+
# `git push` / `git push origin` with no refspec -> current branch.
|
|
139
|
+
cur=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
|
|
140
|
+
[ -n "$cur" ] && [ "$cur" != "HEAD" ] && BRANCHES+=("$cur")
|
|
141
|
+
continue
|
|
142
|
+
fi
|
|
143
|
+
|
|
144
|
+
for r in "${refspecs[@]}"; do
|
|
145
|
+
# Strip a leading '+' (force) and take the destination side of any colon.
|
|
146
|
+
r="${r#+}"
|
|
147
|
+
dst="${r##*:}"
|
|
148
|
+
# Ignore tag/ref deletions and empty sources.
|
|
149
|
+
[ -z "$dst" ] && continue
|
|
150
|
+
case "$dst" in
|
|
151
|
+
HEAD) cur=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
|
|
152
|
+
[ -n "$cur" ] && [ "$cur" != "HEAD" ] && BRANCHES+=("$cur"); continue ;;
|
|
153
|
+
esac
|
|
154
|
+
BRANCHES+=("$dst")
|
|
155
|
+
done
|
|
156
|
+
done <<EOF
|
|
157
|
+
$SEGMENTS
|
|
158
|
+
EOF
|
|
159
|
+
|
|
160
|
+
# Nothing identifiable and not a fan-out: fail open.
|
|
161
|
+
if [ "$FANOUT" -eq 0 ] && [ "${#BRANCHES[@]}" -eq 0 ]; then
|
|
162
|
+
exit 0
|
|
163
|
+
fi
|
|
164
|
+
|
|
165
|
+
# ---- Record into the rolling per-session window --------------------------
|
|
166
|
+
STATE_DIR="${CC_PUSH_BLAST_DIR:-/tmp/cc-push-blast}"
|
|
167
|
+
mkdir -p "$STATE_DIR" 2>/dev/null || exit 0
|
|
168
|
+
STATE="$STATE_DIR/$SID"
|
|
169
|
+
|
|
170
|
+
NOW=$(date +%s)
|
|
171
|
+
CUTOFF=$((NOW - WINDOW))
|
|
172
|
+
|
|
173
|
+
# Append "<ts>\t<branch>" for every branch this command pushes.
|
|
174
|
+
for b in "${BRANCHES[@]}"; do
|
|
175
|
+
# Sanitise tab/newline out of the branch name for the state file.
|
|
176
|
+
safe=$(printf '%s' "$b" | tr -d '\t\n')
|
|
177
|
+
[ -n "$safe" ] && printf '%s\t%s\n' "$NOW" "$safe" >> "$STATE" 2>/dev/null
|
|
178
|
+
done
|
|
179
|
+
|
|
180
|
+
# Prune entries older than the window.
|
|
181
|
+
if [ -f "$STATE" ]; then
|
|
182
|
+
awk -F'\t' -v c="$CUTOFF" '$1 ~ /^[0-9]+$/ && $1 >= c' "$STATE" > "$STATE.tmp" 2>/dev/null && mv "$STATE.tmp" "$STATE" 2>/dev/null
|
|
183
|
+
fi
|
|
184
|
+
|
|
185
|
+
# Count DISTINCT branches inside the window.
|
|
186
|
+
if [ -f "$STATE" ]; then
|
|
187
|
+
DISTINCT=$(awk -F'\t' '{print $2}' "$STATE" 2>/dev/null | sort -u | grep -c .)
|
|
188
|
+
else
|
|
189
|
+
DISTINCT=0
|
|
190
|
+
fi
|
|
191
|
+
case "$DISTINCT" in ''|*[!0-9]*) DISTINCT=0;; esac
|
|
192
|
+
|
|
193
|
+
# A whole-repo fan-out is an immediate trip regardless of the counter.
|
|
194
|
+
TRIP=0
|
|
195
|
+
[ "$FANOUT" -eq 1 ] && TRIP=1
|
|
196
|
+
[ "$DISTINCT" -ge "$BLOCK_AT" ] && TRIP=1
|
|
197
|
+
|
|
198
|
+
MINS=$((WINDOW / 60))
|
|
199
|
+
if [ "$FANOUT" -eq 1 ]; then
|
|
200
|
+
SUBJECT="This 'git push --all/--mirror' pushes every local branch at once"
|
|
201
|
+
else
|
|
202
|
+
SUBJECT="This makes ${DISTINCT} distinct branches pushed within ${MINS} min"
|
|
203
|
+
fi
|
|
204
|
+
MSG="${SUBJECT}. Each branch push triggers the full CI suite, so cost scales with branch count: in #65944 a 1-line fix pushed across 13 branches ran ~150+ workflow runs and burned the entire 2,000-minute monthly GitHub Actions allotment in under 3 hours. If you are applying one fix to many branches, stop and use the cheap shape instead: commit the fix once on main (or a single branch), merge it, and let the other branches inherit it via rebase/merge — 1 CI run instead of one per branch. To proceed anyway, raise CC_PUSH_BLAST_BLOCK, set CC_PUSH_BLAST_GUARD=warn to only warn, or =off to disable. State resets after ${WINDOW}s of no pushes: ${STATE}"
|
|
205
|
+
|
|
206
|
+
if [ "$TRIP" -eq 1 ]; then
|
|
207
|
+
if [ "$MODE" = "warn" ]; then
|
|
208
|
+
echo "git-push-blast-radius-guard: $MSG" >&2
|
|
209
|
+
exit 0
|
|
210
|
+
fi
|
|
211
|
+
# block (default): structured decision so the model sees the reason.
|
|
212
|
+
jq -n --arg r "$MSG" '{decision: "block", reason: $r}'
|
|
213
|
+
exit 0
|
|
214
|
+
elif [ "$DISTINCT" -ge "$WARN_AT" ]; then
|
|
215
|
+
echo "git-push-blast-radius-guard: ${DISTINCT} distinct branches pushed within ${MINS} min — each push triggers CI; consider fix-on-main + rebase if applying one change to many branches (#65944). Blocks at ${BLOCK_AT}." >&2
|
|
216
|
+
fi
|
|
217
|
+
|
|
218
|
+
exit 0
|
|
@@ -20,9 +20,30 @@ if echo "$COMMAND" | grep -qE '\bgit\s+remote\s+add\b'; then
|
|
|
20
20
|
echo "Verify this is a trusted repository." >&2
|
|
21
21
|
fi
|
|
22
22
|
|
|
23
|
-
# Check for push to
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
# Check for push to a remote that is not origin.
|
|
24
|
+
#
|
|
25
|
+
# The previous version used `(?!origin\b)`, a lookahead that ERE does not have:
|
|
26
|
+
# grep -E rejected the pattern, returned 2, and the `if` was never true. The
|
|
27
|
+
# warning below could not fire on any input. Deciding "is it origin?" in the
|
|
28
|
+
# shell instead of the pattern removes the need for a lookahead entirely.
|
|
29
|
+
#
|
|
30
|
+
# `git` also accepts global options before the subcommand, so `git -C <dir>
|
|
31
|
+
# push upstream main` has to be matched as well (#1117).
|
|
32
|
+
GIT_OPT='(-[cC][[:space:]]+[^[:space:]]+|--(git-dir|work-tree|namespace|exec-path|config-env|attr-source|super-prefix)(=[^[:space:]]+|[[:space:]]+[^[:space:]]+)|-[pP]|--(paginate|no-pager|bare|no-replace-objects|literal-pathspecs|glob-pathspecs|noglob-pathspecs|icase-pathspecs|no-optional-locks|no-lazy-fetch|no-advice))'
|
|
33
|
+
GIT_HEAD='(^|[[:space:];&|(]|/|"|'"'"')git'
|
|
34
|
+
GIT_PUSH_RE="${GIT_HEAD}([[:space:]]+${GIT_OPT})*[[:space:]]+push([[:space:]]|$)"
|
|
35
|
+
|
|
36
|
+
if echo "$COMMAND" | grep -qE "$GIT_PUSH_RE"; then
|
|
37
|
+
# First bare word after `push` is the remote (flags and their values are
|
|
38
|
+
# skipped). No remote at all means the default one, which is fine.
|
|
39
|
+
REMOTE=$(echo "$COMMAND" | awk '{
|
|
40
|
+
for (i = 1; i <= NF; i++) {
|
|
41
|
+
if ($i == "push") { seen = 1; continue }
|
|
42
|
+
if (!seen) continue
|
|
43
|
+
if ($i ~ /^-/) { continue }
|
|
44
|
+
print $i; exit
|
|
45
|
+
}
|
|
46
|
+
}')
|
|
26
47
|
if [ -n "$REMOTE" ] && [ "$REMOTE" != "origin" ]; then
|
|
27
48
|
echo "WARNING: Pushing to non-origin remote: $REMOTE" >&2
|
|
28
49
|
echo "Verify this remote is trusted." >&2
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# git-rm-orphan-wipe-guard.sh — Block whole-working-tree deletion via git
|
|
3
|
+
#
|
|
4
|
+
# Solves: Claude Code (or a careless one-liner it runs) wiping an entire
|
|
5
|
+
# working tree — and its recovery path — with git itself, slipping
|
|
6
|
+
# past the rm-based guards.
|
|
7
|
+
#
|
|
8
|
+
# The #70687 pattern (real loss of 3 years of work + .git history):
|
|
9
|
+
# git checkout --orphan wipe && git rm -rf . --quiet
|
|
10
|
+
# 1. "git checkout --orphan" starts a branch with NO parent commit, so
|
|
11
|
+
# there is no prior commit to restore the files from afterwards.
|
|
12
|
+
# 2. "git rm -rf ." then removes every tracked file from the index AND
|
|
13
|
+
# the working tree.
|
|
14
|
+
# Together the project is gone with no commit to recover from.
|
|
15
|
+
#
|
|
16
|
+
# Why existing guards miss it:
|
|
17
|
+
# - rm-safety-net.sh only fires on a command that STARTS with "rm"
|
|
18
|
+
# (^rm). "git rm ..." starts with "git", so it slips through.
|
|
19
|
+
# - git-checkout-safety-guard.sh Check 1 matches "checkout <letter>";
|
|
20
|
+
# "git checkout --orphan" starts with "-", so it is not matched.
|
|
21
|
+
# - bulk-file-delete-guard.sh only blocks "git rm -rf ." when the file
|
|
22
|
+
# count crosses its threshold, so a small-but-total wipe passes.
|
|
23
|
+
#
|
|
24
|
+
# Detects:
|
|
25
|
+
# git checkout --orphan <name> (detaches from all history)
|
|
26
|
+
# git switch --orphan <name> (same, newer syntax)
|
|
27
|
+
# git rm -r / -rf / --recursive (recursive removal of the tree)
|
|
28
|
+
# git rm . / git rm * (whole-tree target)
|
|
29
|
+
#
|
|
30
|
+
# Does NOT block (no on-disk data loss / normal use):
|
|
31
|
+
# git rm <file> (single tracked file)
|
|
32
|
+
# git rm --cached ... (untracks only; disk files survive)
|
|
33
|
+
# git checkout <branch> / -- file (covered by git-checkout-safety-guard)
|
|
34
|
+
#
|
|
35
|
+
# TRIGGER: PreToolUse MATCHER: "Bash"
|
|
36
|
+
|
|
37
|
+
# Without jq, the parse below silently yields empty and this hook stops
|
|
38
|
+
# guarding - with no error anywhere. Say so. We deliberately do not exit
|
|
39
|
+
# here: blocking would halt every tool call, and exiting 0 would change
|
|
40
|
+
# the behaviour of guards that do not depend on the parsed value.
|
|
41
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
42
|
+
_nojq_warned="/tmp/cc-nojq-warned-git-rm-orphan-wipe-guard-$PPID"
|
|
43
|
+
[ -f "$_nojq_warned" ] || {
|
|
44
|
+
echo "WARNING [git-rm-orphan-wipe-guard]: jq not found - this hook cannot inspect tool calls and is NOT protecting you. Install jq." >&2
|
|
45
|
+
: > "$_nojq_warned"
|
|
46
|
+
}
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
INPUT=$(cat)
|
|
50
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
51
|
+
|
|
52
|
+
[ -z "$COMMAND" ] && exit 0
|
|
53
|
+
|
|
54
|
+
# === Check 1: git checkout/switch --orphan (removes the recovery path) ===
|
|
55
|
+
if echo "$COMMAND" | grep -qE '\bgit\s+(checkout|switch)\b[^|;&]*--orphan\b'; then
|
|
56
|
+
echo "BLOCKED: git checkout --orphan starts a branch with no parent commit." >&2
|
|
57
|
+
echo " Followed by 'git rm -rf .' this leaves no commit to recover from (#70687)." >&2
|
|
58
|
+
echo " If you really need an orphan branch, run it yourself outside the agent." >&2
|
|
59
|
+
exit 2
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
# === git rm handling: only the destructive (whole-tree) forms ===
|
|
63
|
+
if echo "$COMMAND" | grep -qE '\bgit\s+rm\b'; then
|
|
64
|
+
# --cached removes from the index only; working-tree files survive. Safe.
|
|
65
|
+
if echo "$COMMAND" | grep -qE '\bgit\s+rm\b[^|;&]*--cached\b'; then
|
|
66
|
+
exit 0
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
# Check 2: recursive removal (-r / -R / -rf / --recursive)
|
|
70
|
+
if echo "$COMMAND" | grep -qE '\bgit\s+rm\s+(-[a-zA-Z]*[rR][a-zA-Z]*|--recursive)\b'; then
|
|
71
|
+
echo "BLOCKED: git rm -r removes every tracked file in the tree from disk." >&2
|
|
72
|
+
echo " rm-safety-net does not catch this (the command starts with 'git', not 'rm')." >&2
|
|
73
|
+
echo " Delete specific files by name, or use 'git rm --cached' to untrack only." >&2
|
|
74
|
+
exit 2
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
# Check 3: whole-tree target ( . or * ) even without -r
|
|
78
|
+
if echo "$COMMAND" | grep -qE '\bgit\s+rm\b[^|;&]*(\s\.(\s|$)|\s\./(\s|$)|\s\*(\s|$))'; then
|
|
79
|
+
echo "BLOCKED: git rm targeting '.' or '*' removes the whole working tree." >&2
|
|
80
|
+
echo " Delete specific files by name, or use 'git rm --cached' to untrack only." >&2
|
|
81
|
+
exit 2
|
|
82
|
+
fi
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
exit 0
|
|
@@ -28,6 +28,18 @@
|
|
|
28
28
|
# }
|
|
29
29
|
# ================================================================
|
|
30
30
|
|
|
31
|
+
# Without jq, the parse below silently yields empty and this hook stops
|
|
32
|
+
# guarding - with no error anywhere. Say so. We deliberately do not exit
|
|
33
|
+
# here: blocking would halt every tool call, and exiting 0 would change
|
|
34
|
+
# the behaviour of guards that do not depend on the parsed value.
|
|
35
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
36
|
+
_nojq_warned="/tmp/cc-nojq-warned-git-stash-before-checkout-$PPID"
|
|
37
|
+
[ -f "$_nojq_warned" ] || {
|
|
38
|
+
echo "WARNING [git-stash-before-checkout]: jq not found - this hook cannot inspect tool calls and is NOT protecting you. Install jq." >&2
|
|
39
|
+
: > "$_nojq_warned"
|
|
40
|
+
}
|
|
41
|
+
fi
|
|
42
|
+
|
|
31
43
|
INPUT=$(cat)
|
|
32
44
|
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
33
45
|
[ -z "$COMMAND" ] && exit 0
|
|
File without changes
|