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
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# deny-rule-integrity-verifier.sh — Catch deny-rule bypasses through whitespace normalization
|
|
3
|
+
#
|
|
4
|
+
# Solves: Cluster 6 Axis 5 — deny rules don't enforce the safety constraints
|
|
5
|
+
# users configure. A deny pattern Bash(rm -rf:*) catches `rm -rf /`
|
|
6
|
+
# but is silently bypassed by:
|
|
7
|
+
# - extra whitespace (`rm -rf /`)
|
|
8
|
+
# - backslash-newline line continuations (`rm \<newline>-rf /`)
|
|
9
|
+
# - tab characters in place of spaces (`rm -rf /`)
|
|
10
|
+
# The matching engine compares the raw command string against the
|
|
11
|
+
# pattern body literally, so any whitespace anomaly slips past.
|
|
12
|
+
#
|
|
13
|
+
# Issues addressed: meta-issue #30519 Axis 5 (deny rules have same bugs as
|
|
14
|
+
# allow rules), #35954 (backslash-escaped whitespace warning
|
|
15
|
+
# framework), and the broader pattern that whitespace-sensitive
|
|
16
|
+
# literal matching can be defeated trivially.
|
|
17
|
+
#
|
|
18
|
+
# How it works: PreToolUse hook on Bash. For each Bash() deny pattern in
|
|
19
|
+
# permissions.deny across the four standard settings locations, the
|
|
20
|
+
# hook checks:
|
|
21
|
+
# 1. Does the RAW command match the pattern? (the matching engine
|
|
22
|
+
# catches this case; if yes, this hook does nothing)
|
|
23
|
+
# 2. Does the NORMALIZED command match the pattern? (whitespace
|
|
24
|
+
# collapsed, line continuations joined)
|
|
25
|
+
# If raw misses and normalized hits — that is the bypass. The hook
|
|
26
|
+
# blocks with exit code 2 by default; CC_DENY_INTEGRITY_WARN_ONLY=1
|
|
27
|
+
# converts the block to a stderr advisory while letting the command
|
|
28
|
+
# through.
|
|
29
|
+
#
|
|
30
|
+
# Pattern extraction supports the three Claude Code Bash() shapes:
|
|
31
|
+
# Bash(<exact>) — exact match
|
|
32
|
+
# Bash(<prefix>:*) — prefix match (the common form)
|
|
33
|
+
# Bash(*) — wildcard — skipped (already matches raw)
|
|
34
|
+
#
|
|
35
|
+
# Scope of normalization (v1):
|
|
36
|
+
# - Strip backslash-newline line continuations (`\<newline>` → ` `)
|
|
37
|
+
# - Collapse any whitespace run (spaces, tabs, newlines) to a single space
|
|
38
|
+
# - Trim leading and trailing whitespace
|
|
39
|
+
# Flag combination/reordering normalization (`-rf` vs `-r -f` vs `-fr`)
|
|
40
|
+
# is OUT OF SCOPE for v1 — those bypass shapes need command-aware logic
|
|
41
|
+
# beyond uniform string normalization and warrant a separate hook.
|
|
42
|
+
#
|
|
43
|
+
# Environment variables:
|
|
44
|
+
# CC_DENY_INTEGRITY_DISABLE=1 — disable verification entirely
|
|
45
|
+
# CC_DENY_INTEGRITY_WARN_ONLY=1 — log the bypass to stderr but do not block
|
|
46
|
+
#
|
|
47
|
+
# TRIGGER: PreToolUse
|
|
48
|
+
# MATCHER: "Bash"
|
|
49
|
+
|
|
50
|
+
set -uo pipefail
|
|
51
|
+
|
|
52
|
+
# Without jq, the parse below silently yields empty and this hook stops
|
|
53
|
+
# guarding - with no error anywhere. Say so. We deliberately do not exit
|
|
54
|
+
# here: blocking would halt every tool call, and exiting 0 would change
|
|
55
|
+
# the behaviour of guards that do not depend on the parsed value.
|
|
56
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
57
|
+
_nojq_warned="/tmp/cc-nojq-warned-deny-rule-integrity-verifier-$PPID"
|
|
58
|
+
[ -f "$_nojq_warned" ] || {
|
|
59
|
+
echo "WARNING [deny-rule-integrity-verifier]: jq not found - this hook cannot inspect tool calls and is NOT protecting you. Install jq." >&2
|
|
60
|
+
: > "$_nojq_warned"
|
|
61
|
+
}
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
INPUT=$(cat)
|
|
65
|
+
|
|
66
|
+
COMMAND=$(printf '%s' "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
67
|
+
[ -z "$COMMAND" ] && exit 0
|
|
68
|
+
[ "${CC_DENY_INTEGRITY_DISABLE:-0}" = "1" ] && exit 0
|
|
69
|
+
|
|
70
|
+
# Collect Bash() deny patterns from all four standard settings locations.
|
|
71
|
+
DENY_FILE=$(mktemp)
|
|
72
|
+
trap 'rm -f "$DENY_FILE"' EXIT
|
|
73
|
+
for CFG in \
|
|
74
|
+
"${HOME}/.claude/settings.json" \
|
|
75
|
+
"${HOME}/.claude/settings.local.json" \
|
|
76
|
+
"./.claude/settings.json" \
|
|
77
|
+
"./.claude/settings.local.json"; do
|
|
78
|
+
[ -f "$CFG" ] || continue
|
|
79
|
+
jq -r '.permissions.deny // [] | .[] | select(type == "string" and startswith("Bash("))' \
|
|
80
|
+
"$CFG" 2>/dev/null >> "$DENY_FILE" || true
|
|
81
|
+
done
|
|
82
|
+
|
|
83
|
+
[ ! -s "$DENY_FILE" ] && exit 0
|
|
84
|
+
|
|
85
|
+
# Normalize: strip backslash-newline (line continuation), collapse all
|
|
86
|
+
# whitespace to single space, trim.
|
|
87
|
+
NORMALIZED=$(printf '%s' "$COMMAND" \
|
|
88
|
+
| sed -E ':a;N;$!ba;s/\\\n/ /g' \
|
|
89
|
+
| tr '\t\n' ' ' \
|
|
90
|
+
| sed -E 's/[[:space:]]+/ /g; s/^[[:space:]]+//; s/[[:space:]]+$//')
|
|
91
|
+
|
|
92
|
+
# If normalization is a no-op, nothing to do — the standard matcher
|
|
93
|
+
# already handles this command.
|
|
94
|
+
if [ "$NORMALIZED" = "$COMMAND" ]; then
|
|
95
|
+
exit 0
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
# Check each deny pattern.
|
|
99
|
+
while IFS= read -r PATTERN; do
|
|
100
|
+
[ -z "$PATTERN" ] && continue
|
|
101
|
+
|
|
102
|
+
# Extract pattern body. Handles:
|
|
103
|
+
# Bash(<body>) → BODY=<body>, SUFFIX_GLOB=0
|
|
104
|
+
# Bash(<body>:*) → BODY=<body>, SUFFIX_GLOB=1
|
|
105
|
+
BODY=""
|
|
106
|
+
SUFFIX_GLOB=0
|
|
107
|
+
if [[ "$PATTERN" =~ ^Bash\((.+):\*\)$ ]]; then
|
|
108
|
+
BODY="${BASH_REMATCH[1]}"
|
|
109
|
+
SUFFIX_GLOB=1
|
|
110
|
+
elif [[ "$PATTERN" =~ ^Bash\((.+)\)$ ]]; then
|
|
111
|
+
BODY="${BASH_REMATCH[1]}"
|
|
112
|
+
SUFFIX_GLOB=0
|
|
113
|
+
fi
|
|
114
|
+
[ -z "$BODY" ] && continue
|
|
115
|
+
|
|
116
|
+
# Bash(*) — wildcard pattern — would match raw already.
|
|
117
|
+
[ "$BODY" = "*" ] && continue
|
|
118
|
+
|
|
119
|
+
# Match logic: a command matches the pattern if it equals BODY exactly
|
|
120
|
+
# or (when the pattern has the :* glob suffix) if it starts with BODY
|
|
121
|
+
# at a word boundary. The boundary rule:
|
|
122
|
+
# - If BODY ends in an alphanumeric char (rm, git, curl) the next
|
|
123
|
+
# character must be a space — otherwise `Bash(rm:*)` would block
|
|
124
|
+
# `rmdir`.
|
|
125
|
+
# - If BODY ends in a non-alphanumeric char (=, -, /, .) the next
|
|
126
|
+
# character can be attached — `Bash(dd if=:*)` should match
|
|
127
|
+
# `dd if=/dev/zero ...`.
|
|
128
|
+
raw_matches() {
|
|
129
|
+
local CMD="$1" BDY="$2" GLOB="$3"
|
|
130
|
+
if [ "$CMD" = "$BDY" ]; then
|
|
131
|
+
return 0
|
|
132
|
+
fi
|
|
133
|
+
if [ "$GLOB" = "1" ]; then
|
|
134
|
+
local LAST_CHAR="${BDY: -1}"
|
|
135
|
+
if [[ "$LAST_CHAR" =~ [a-zA-Z0-9] ]]; then
|
|
136
|
+
case "$CMD" in
|
|
137
|
+
"$BDY "*) return 0 ;;
|
|
138
|
+
esac
|
|
139
|
+
else
|
|
140
|
+
case "$CMD" in
|
|
141
|
+
"$BDY"*) return 0 ;;
|
|
142
|
+
esac
|
|
143
|
+
fi
|
|
144
|
+
fi
|
|
145
|
+
return 1
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if raw_matches "$COMMAND" "$BODY" "$SUFFIX_GLOB"; then
|
|
149
|
+
# Standard matcher already catches this — nothing for us to do.
|
|
150
|
+
continue
|
|
151
|
+
fi
|
|
152
|
+
|
|
153
|
+
if raw_matches "$NORMALIZED" "$BODY" "$SUFFIX_GLOB"; then
|
|
154
|
+
# BYPASS DETECTED.
|
|
155
|
+
CMD_SAFE_PRINT=$(printf '%s' "$COMMAND" | head -c 300 | tr -d '\000')
|
|
156
|
+
{
|
|
157
|
+
echo "[deny-rule-integrity-verifier] Deny-rule bypass detected."
|
|
158
|
+
echo ""
|
|
159
|
+
echo " Configured deny pattern : $PATTERN"
|
|
160
|
+
echo " Raw command (excerpt) : $CMD_SAFE_PRINT"
|
|
161
|
+
echo " Normalized command : $NORMALIZED"
|
|
162
|
+
echo ""
|
|
163
|
+
echo "The normalized form matches your configured deny pattern, but the raw"
|
|
164
|
+
echo "command bypasses literal matching through whitespace anomalies"
|
|
165
|
+
echo "(extra spaces, tab characters, or backslash-newline line continuations)."
|
|
166
|
+
echo "Cluster 6 Axis 5 — meta-issue #30519 (Claude Code permission matching)."
|
|
167
|
+
} >&2
|
|
168
|
+
if [ "${CC_DENY_INTEGRITY_WARN_ONLY:-0}" = "1" ]; then
|
|
169
|
+
echo "" >&2
|
|
170
|
+
echo "(CC_DENY_INTEGRITY_WARN_ONLY=1 is set — command will proceed)" >&2
|
|
171
|
+
exit 0
|
|
172
|
+
else
|
|
173
|
+
echo "" >&2
|
|
174
|
+
echo "Blocking. Set CC_DENY_INTEGRITY_WARN_ONLY=1 to convert this to a" >&2
|
|
175
|
+
echo "warning, or CC_DENY_INTEGRITY_DISABLE=1 to silence the hook." >&2
|
|
176
|
+
exit 2
|
|
177
|
+
fi
|
|
178
|
+
fi
|
|
179
|
+
done < "$DENY_FILE"
|
|
180
|
+
|
|
181
|
+
exit 0
|
|
@@ -19,6 +19,18 @@
|
|
|
19
19
|
#
|
|
20
20
|
# TRIGGER: PreToolUse MATCHER: "Bash"
|
|
21
21
|
|
|
22
|
+
# Without jq, the parse below silently yields empty and this hook stops
|
|
23
|
+
# guarding - with no error anywhere. Say so. We deliberately do not exit
|
|
24
|
+
# here: blocking would halt every tool call, and exiting 0 would change
|
|
25
|
+
# the behaviour of guards that do not depend on the parsed value.
|
|
26
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
27
|
+
_nojq_warned="/tmp/cc-nojq-warned-dependency-install-guard-$PPID"
|
|
28
|
+
[ -f "$_nojq_warned" ] || {
|
|
29
|
+
echo "WARNING [dependency-install-guard]: jq not found - this hook cannot inspect tool calls and is NOT protecting you. Install jq." >&2
|
|
30
|
+
: > "$_nojq_warned"
|
|
31
|
+
}
|
|
32
|
+
fi
|
|
33
|
+
|
|
22
34
|
INPUT=$(cat)
|
|
23
35
|
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
24
36
|
[ -z "$COMMAND" ] && exit 0
|
package/examples/deploy-guard.sh
CHANGED
|
@@ -12,13 +12,25 @@
|
|
|
12
12
|
# {
|
|
13
13
|
# "hooks": {
|
|
14
14
|
# "PreToolUse": [{
|
|
15
|
-
# "matcher": "Bash",
|
|
15
|
+
# "matcher": "Bash|PowerShell",
|
|
16
16
|
# "hooks": [{ "type": "command", "command": "~/.claude/hooks/deploy-guard.sh" }]
|
|
17
17
|
# }]
|
|
18
18
|
# }
|
|
19
19
|
# }
|
|
20
20
|
#
|
|
21
|
-
# TRIGGER: PreToolUse MATCHER: "Bash"
|
|
21
|
+
# TRIGGER: PreToolUse MATCHER: "Bash|PowerShell"
|
|
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-deploy-guard-$PPID"
|
|
29
|
+
[ -f "$_nojq_warned" ] || {
|
|
30
|
+
echo "WARNING [deploy-guard]: jq not found - this hook cannot inspect tool calls and is NOT protecting you. Install jq." >&2
|
|
31
|
+
: > "$_nojq_warned"
|
|
32
|
+
}
|
|
33
|
+
fi
|
|
22
34
|
|
|
23
35
|
INPUT=$(cat)
|
|
24
36
|
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
@@ -18,6 +18,18 @@
|
|
|
18
18
|
|
|
19
19
|
set -euo pipefail
|
|
20
20
|
|
|
21
|
+
# Without jq, the parse below silently yields empty and this hook stops
|
|
22
|
+
# guarding - with no error anywhere. Say so. We deliberately do not exit
|
|
23
|
+
# here: blocking would halt every tool call, and exiting 0 would change
|
|
24
|
+
# the behaviour of guards that do not depend on the parsed value.
|
|
25
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
26
|
+
_nojq_warned="/tmp/cc-nojq-warned-deploy-path-verify-guard-$PPID"
|
|
27
|
+
[ -f "$_nojq_warned" ] || {
|
|
28
|
+
echo "WARNING [deploy-path-verify-guard]: jq not found - this hook cannot inspect tool calls and is NOT protecting you. Install jq." >&2
|
|
29
|
+
: > "$_nojq_warned"
|
|
30
|
+
}
|
|
31
|
+
fi
|
|
32
|
+
|
|
21
33
|
INPUT=$(cat)
|
|
22
34
|
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
23
35
|
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ================================================================
|
|
3
|
+
# deployment-readback-gate.sh — Refuse a deployment-completion claim
|
|
4
|
+
# unless an authority confirms it, recently
|
|
5
|
+
# ================================================================
|
|
6
|
+
# PURPOSE:
|
|
7
|
+
# Turn a "deployment complete" claim into a refusable, auditable trust
|
|
8
|
+
# artifact. This is the GENERIC decision core: it consumes a *normalized
|
|
9
|
+
# readback receipt* (produced by a provider adapter — gh deployments,
|
|
10
|
+
# Vercel, k8s, ...) and decides allow / refuse-mismatch / refuse-query-
|
|
11
|
+
# failure / refuse-stale. It contains no provider knowledge.
|
|
12
|
+
#
|
|
13
|
+
# Spec: docs/deployment-readback-gate-spec.md (issue #313)
|
|
14
|
+
# Motivating incident: anthropics/claude-code#61699 (model claimed
|
|
15
|
+
# "deployment complete" while the real deployment state diverged).
|
|
16
|
+
#
|
|
17
|
+
# TRIGGER: Stop
|
|
18
|
+
#
|
|
19
|
+
# INPUT (stdin): a normalized readback receipt JSON. Required fields:
|
|
20
|
+
# claim_span, claim_time (ISO 8601), claimed_ref, authority,
|
|
21
|
+
# queried_ref, queried_state, readback_time (ISO 8601, from the
|
|
22
|
+
# authority's own response), stale_if_older_than_ms (integer).
|
|
23
|
+
# queried_state is the authority's state ("success" = confirmed) or
|
|
24
|
+
# the literal "query-failure" when the adapter could not reach the
|
|
25
|
+
# authority.
|
|
26
|
+
#
|
|
27
|
+
# DECISION (and exit code):
|
|
28
|
+
# refuse-query-failure (exit 2) — authority unreachable / readback errored.
|
|
29
|
+
# FAIL CLOSED. Never coerced to allow. Distinct incident class from a
|
|
30
|
+
# mismatch: truth was never established (the claim *might* be correct).
|
|
31
|
+
# refuse-mismatch (exit 2) — queried_ref != claimed_ref, or
|
|
32
|
+
# queried_state != "success". Truth established, claim was wrong.
|
|
33
|
+
# refuse-stale (exit 2) — claim_time - readback_time exceeds
|
|
34
|
+
# stale_if_older_than_ms. A cached/replayed "success" must not ratify
|
|
35
|
+
# a fresh claim. Staleness is anchored to the CLAIM, not wall-clock.
|
|
36
|
+
# allow (exit 0) — fresh AND matching AND success.
|
|
37
|
+
#
|
|
38
|
+
# Missing required fields or unparseable timestamps also FAIL CLOSED
|
|
39
|
+
# (refuse-query-failure): an unauditable receipt is not a confirmation.
|
|
40
|
+
#
|
|
41
|
+
# WHAT IT WRITES:
|
|
42
|
+
# $CC_READBACK_RECEIPT_DIR/<timestamp>-<pid>.json (decision filled in)
|
|
43
|
+
# default: ~/.claude/state/deployments/readback/
|
|
44
|
+
# The receipt is written OUTSIDE the transcript, so the audit unit
|
|
45
|
+
# survives a lost or rewound session.
|
|
46
|
+
#
|
|
47
|
+
# USAGE (settings.json):
|
|
48
|
+
# { "hooks": { "Stop": [{ "hooks": [{ "type": "command",
|
|
49
|
+
# "command": "<adapter> | ~/.claude/hooks/deployment-readback-gate.sh" }] }] } }
|
|
50
|
+
# The adapter emits the normalized receipt on stdout; this gate decides.
|
|
51
|
+
# ================================================================
|
|
52
|
+
|
|
53
|
+
set -uo pipefail
|
|
54
|
+
|
|
55
|
+
RECEIPT_DIR="${CC_READBACK_RECEIPT_DIR:-$HOME/.claude/state/deployments/readback}"
|
|
56
|
+
|
|
57
|
+
INPUT=$(cat)
|
|
58
|
+
|
|
59
|
+
# This gate only acts on a normalized readback receipt produced by an adapter.
|
|
60
|
+
# Empty stdin, "{}", or unrelated input means no deployment claim is being
|
|
61
|
+
# gated here — pass through. Hooks must not block on irrelevant input.
|
|
62
|
+
if ! printf '%s' "$INPUT" | grep -qE '"(claimed_ref|authority|queried_state|claim_span)"'; then
|
|
63
|
+
exit 0
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
# A receipt is present; jq is required to evaluate it.
|
|
67
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
68
|
+
echo "deployment-readback-gate: jq not found; cannot audit a present receipt — failing closed" >&2
|
|
69
|
+
exit 2
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
# Extract fields (empty if absent).
|
|
73
|
+
field() { printf '%s' "$INPUT" | jq -r "(.$1 // empty)" 2>/dev/null; }
|
|
74
|
+
|
|
75
|
+
CLAIM_SPAN=$(field claim_span)
|
|
76
|
+
CLAIM_TIME=$(field claim_time)
|
|
77
|
+
CLAIMED_REF=$(field claimed_ref)
|
|
78
|
+
AUTHORITY=$(field authority)
|
|
79
|
+
QUERIED_REF=$(field queried_ref)
|
|
80
|
+
QUERIED_STATE=$(field queried_state)
|
|
81
|
+
READBACK_TIME=$(field readback_time)
|
|
82
|
+
STALE_MS=$(field stale_if_older_than_ms)
|
|
83
|
+
|
|
84
|
+
decision=""
|
|
85
|
+
reason=""
|
|
86
|
+
|
|
87
|
+
# to_ms: ISO 8601 -> epoch milliseconds, or empty on parse failure.
|
|
88
|
+
to_ms() { date -d "$1" +%s%3N 2>/dev/null; }
|
|
89
|
+
|
|
90
|
+
# --- fail closed on an unauditable receipt -------------------------------
|
|
91
|
+
if [ -z "$AUTHORITY" ] || [ -z "$READBACK_TIME" ] || [ -z "$STALE_MS" ] \
|
|
92
|
+
|| [ -z "$CLAIM_TIME" ] || [ -z "$CLAIMED_REF" ] || [ -z "$QUERIED_STATE" ]; then
|
|
93
|
+
decision="refuse-query-failure"
|
|
94
|
+
reason="receipt missing required fields (authority/readback_time/stale_if_older_than_ms/claim_time/claimed_ref/queried_state) — cannot establish truth"
|
|
95
|
+
elif [ "$QUERIED_STATE" = "query-failure" ]; then
|
|
96
|
+
decision="refuse-query-failure"
|
|
97
|
+
reason="authority '$AUTHORITY' was not reached — deployment NOT confirmed (failing closed)"
|
|
98
|
+
else
|
|
99
|
+
claim_ms=$(to_ms "$CLAIM_TIME")
|
|
100
|
+
read_ms=$(to_ms "$READBACK_TIME")
|
|
101
|
+
if [ -z "$claim_ms" ] || [ -z "$read_ms" ]; then
|
|
102
|
+
decision="refuse-query-failure"
|
|
103
|
+
reason="unparseable claim_time/readback_time — cannot verify freshness (failing closed)"
|
|
104
|
+
elif [ "$QUERIED_REF" != "$CLAIMED_REF" ] || [ "$QUERIED_STATE" != "success" ]; then
|
|
105
|
+
decision="refuse-mismatch"
|
|
106
|
+
reason="claim ref/state ($CLAIMED_REF/success) != authority ($QUERIED_REF/$QUERIED_STATE)"
|
|
107
|
+
elif [ $(( claim_ms - read_ms )) -gt "$STALE_MS" ]; then
|
|
108
|
+
decision="refuse-stale"
|
|
109
|
+
reason="readback is $(( claim_ms - read_ms ))ms older than the claim (> ${STALE_MS}ms) — possible replay/cache, not ratifying"
|
|
110
|
+
else
|
|
111
|
+
decision="allow"
|
|
112
|
+
reason="confirmed by $AUTHORITY: $QUERIED_REF/$QUERIED_STATE, fresh within ${STALE_MS}ms"
|
|
113
|
+
fi
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
# --- write the receipt (audit artifact, outside the transcript) ----------
|
|
117
|
+
mkdir -p "$RECEIPT_DIR" 2>/dev/null
|
|
118
|
+
ts=$(date -u +%Y%m%dT%H%M%S%3NZ 2>/dev/null || date -u +%Y%m%dT%H%M%SZ)
|
|
119
|
+
receipt_path="$RECEIPT_DIR/${ts}-$$.json"
|
|
120
|
+
printf '%s' "$INPUT" \
|
|
121
|
+
| jq --arg d "$decision" --arg r "$reason" --arg w "$ts" \
|
|
122
|
+
'. + {decision: $d, decision_reason: $r, decided_at: $w}' \
|
|
123
|
+
> "$receipt_path" 2>/dev/null \
|
|
124
|
+
|| printf '{"decision":"%s","decision_reason":"%s","raw":true}\n' "$decision" "$reason" > "$receipt_path"
|
|
125
|
+
|
|
126
|
+
# --- emit decision -------------------------------------------------------
|
|
127
|
+
if [ "$decision" = "allow" ]; then
|
|
128
|
+
exit 0
|
|
129
|
+
fi
|
|
130
|
+
|
|
131
|
+
# Stop hook: exit 2 surfaces the reason and blocks the stop so the claim
|
|
132
|
+
# is corrected instead of standing.
|
|
133
|
+
echo "deployment-readback-gate: $decision — $reason" >&2
|
|
134
|
+
echo " receipt: $receipt_path" >&2
|
|
135
|
+
exit 2
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ================================================================
|
|
3
|
+
# deployment-readback-gh-adapter.sh — `gh` deployments provider adapter
|
|
4
|
+
# for deployment-readback-gate
|
|
5
|
+
# ================================================================
|
|
6
|
+
# PURPOSE:
|
|
7
|
+
# The PROVIDER ADAPTER half of the deployment-readback-gate (issue
|
|
8
|
+
# #313). It owns all GitHub-specific knowledge: given a Stop event,
|
|
9
|
+
# it detects a deployment-completion claim in the assistant's
|
|
10
|
+
# closeout, reads the GitHub Deployments API for the *authoritative*
|
|
11
|
+
# state of that deploy, and emits the NORMALIZED readback receipt on
|
|
12
|
+
# stdout. The generic gate (examples/deployment-readback-gate.sh)
|
|
13
|
+
# consumes that receipt and decides allow / refuse-*.
|
|
14
|
+
#
|
|
15
|
+
# Spec: docs/deployment-readback-gate-spec.md
|
|
16
|
+
# Gate: examples/deployment-readback-gate.sh (provider-agnostic core)
|
|
17
|
+
# Incident: anthropics/claude-code#61699 (model claimed "deployment
|
|
18
|
+
# complete" while the real deploy state diverged).
|
|
19
|
+
#
|
|
20
|
+
# The adapter knows the provider; the gate knows the decision. A new
|
|
21
|
+
# authority (Vercel, Cloud Run, k8s, ...) is added by writing another
|
|
22
|
+
# adapter to this same output contract — never by touching the gate.
|
|
23
|
+
#
|
|
24
|
+
# TRIGGER: Stop
|
|
25
|
+
#
|
|
26
|
+
# INSTALL (settings.json) — adapter piped into the gate:
|
|
27
|
+
# { "hooks": { "Stop": [{ "hooks": [{ "type": "command",
|
|
28
|
+
# "command": "~/.claude/hooks/deployment-readback-gh-adapter.sh | ~/.claude/hooks/deployment-readback-gate.sh",
|
|
29
|
+
# "env": { "DRG_REPO": "owner/repo", "DRG_ENVIRONMENT": "production" } }] }] } }
|
|
30
|
+
#
|
|
31
|
+
# INPUT (stdin): the Stop event JSON. The adapter reads the last
|
|
32
|
+
# assistant message from `transcript_path` to find the claim. For
|
|
33
|
+
# deterministic use/testing, DRG_CLAIM_TEXT overrides the transcript.
|
|
34
|
+
#
|
|
35
|
+
# OUTPUT (stdout): exactly one of —
|
|
36
|
+
# * `{}` — no deployment claim here (or an
|
|
37
|
+
# unresolvable target in advisory mode). The gate passes through.
|
|
38
|
+
# * a normalized readback receipt — fields per the spec:
|
|
39
|
+
# claim_span, claim_time, target, claimed_ref, authority,
|
|
40
|
+
# readback_query, queried_ref, queried_state, readback_time,
|
|
41
|
+
# stale_if_older_than_ms
|
|
42
|
+
# `queried_state` is the authority's deployment-status state, or the
|
|
43
|
+
# literal "query-failure" when the API could not be reached.
|
|
44
|
+
#
|
|
45
|
+
# CONFIG (env):
|
|
46
|
+
# DRG_REPO owner/repo. If unset, derived from `gh repo view`.
|
|
47
|
+
# DRG_ENVIRONMENT deployment environment to query (default: production)
|
|
48
|
+
# DRG_CLAIMED_REF the claimed commit/ref. If unset, parsed from the
|
|
49
|
+
# claim text (`@<sha>` or a bare 7–40 hex token).
|
|
50
|
+
# DRG_PHRASE_LIST comma-separated claim phrases (case-insensitive).
|
|
51
|
+
# default: deployed,deployment complete,shipped to
|
|
52
|
+
# production,rolled out,deploy succeeded,live in production
|
|
53
|
+
# DRG_STALE_MS staleness window in ms (default: 300000 = 5 min)
|
|
54
|
+
# DRG_STRICT "1" → a deployment claim with an unresolvable
|
|
55
|
+
# target emits a query-failure receipt (gate refuses)
|
|
56
|
+
# instead of passing through. default: advisory (0).
|
|
57
|
+
# DRG_GH_CMD gh binary to invoke (default: gh). Overridable so
|
|
58
|
+
# the API layer can be mocked under test.
|
|
59
|
+
#
|
|
60
|
+
# WHY A SEPARATE ADAPTER:
|
|
61
|
+
# readback_time must come from the AUTHORITY's own response (the
|
|
62
|
+
# deployment status `created_at`), not from when the hook ran — that
|
|
63
|
+
# is what closes the replay/cache gap the gate's staleness check
|
|
64
|
+
# depends on. Only the provider adapter can source it, so provider
|
|
65
|
+
# I/O is isolated here and the gate stays pure and deterministic.
|
|
66
|
+
# ================================================================
|
|
67
|
+
|
|
68
|
+
set -uo pipefail
|
|
69
|
+
|
|
70
|
+
EVENT_INPUT=$(cat)
|
|
71
|
+
|
|
72
|
+
# --- config --------------------------------------------------------------
|
|
73
|
+
GH="${DRG_GH_CMD:-gh}"
|
|
74
|
+
ENVIRONMENT="${DRG_ENVIRONMENT:-production}"
|
|
75
|
+
STALE_MS="${DRG_STALE_MS:-300000}"
|
|
76
|
+
STRICT="${DRG_STRICT:-0}"
|
|
77
|
+
PHRASE_LIST="${DRG_PHRASE_LIST:-deployed,deployment complete,shipped to production,rolled out,deploy succeeded,live in production}"
|
|
78
|
+
AUTHORITY="github_deployments_api"
|
|
79
|
+
|
|
80
|
+
now_iso() { date -u +%Y-%m-%dT%H:%M:%S.%3NZ 2>/dev/null || date -u +%Y-%m-%dT%H:%M:%SZ; }
|
|
81
|
+
|
|
82
|
+
# Emit a bare pass-through and leave: this Stop is not gating a deploy claim.
|
|
83
|
+
pass_through() { printf '{}\n'; exit 0; }
|
|
84
|
+
|
|
85
|
+
# A receipt is meaningless without jq to build it; without it we cannot
|
|
86
|
+
# safely emit the contract, so pass through rather than emit malformed JSON.
|
|
87
|
+
command -v jq >/dev/null 2>&1 || pass_through
|
|
88
|
+
|
|
89
|
+
# --- 1. obtain the closeout text -----------------------------------------
|
|
90
|
+
# DRG_CLAIM_TEXT wins (deterministic). Otherwise read the last assistant
|
|
91
|
+
# message from the transcript named in the Stop event.
|
|
92
|
+
CLAIM_TEXT="${DRG_CLAIM_TEXT:-}"
|
|
93
|
+
if [ -z "$CLAIM_TEXT" ]; then
|
|
94
|
+
TRANSCRIPT=$(printf '%s' "$EVENT_INPUT" | jq -r '.transcript_path // empty' 2>/dev/null)
|
|
95
|
+
if [ -n "$TRANSCRIPT" ] && [ -f "$TRANSCRIPT" ]; then
|
|
96
|
+
CLAIM_TEXT=$(python3 - "$TRANSCRIPT" <<'PY' 2>/dev/null || true
|
|
97
|
+
import sys, json
|
|
98
|
+
last = ""
|
|
99
|
+
try:
|
|
100
|
+
with open(sys.argv[1]) as f:
|
|
101
|
+
for line in f:
|
|
102
|
+
line = line.strip()
|
|
103
|
+
if not line:
|
|
104
|
+
continue
|
|
105
|
+
try:
|
|
106
|
+
d = json.loads(line)
|
|
107
|
+
except Exception:
|
|
108
|
+
continue
|
|
109
|
+
if d.get("type") != "assistant":
|
|
110
|
+
continue
|
|
111
|
+
msg = d.get("message", d)
|
|
112
|
+
content = msg.get("content", "")
|
|
113
|
+
if isinstance(content, str):
|
|
114
|
+
text = content
|
|
115
|
+
elif isinstance(content, list):
|
|
116
|
+
text = " ".join(
|
|
117
|
+
b.get("text", "") for b in content
|
|
118
|
+
if isinstance(b, dict) and b.get("type") == "text"
|
|
119
|
+
)
|
|
120
|
+
else:
|
|
121
|
+
text = ""
|
|
122
|
+
if text.strip():
|
|
123
|
+
last = text
|
|
124
|
+
except Exception:
|
|
125
|
+
pass
|
|
126
|
+
print(last)
|
|
127
|
+
PY
|
|
128
|
+
)
|
|
129
|
+
fi
|
|
130
|
+
fi
|
|
131
|
+
[ -n "$CLAIM_TEXT" ] || pass_through
|
|
132
|
+
|
|
133
|
+
# --- 2. detect a (non-benign) deployment-completion claim ----------------
|
|
134
|
+
# Returns the matched claim phrase/span, or empty. A phrase inside a benign
|
|
135
|
+
# context ("deployed locally", "shipped to my machine", "in dev only") is
|
|
136
|
+
# not a production-deployment claim and must not trip the gate.
|
|
137
|
+
CLAIM_SPAN=$(CLAIM_TEXT="$CLAIM_TEXT" PHRASE_LIST="$PHRASE_LIST" python3 <<'PY' 2>/dev/null || true
|
|
138
|
+
import os, re
|
|
139
|
+
text = os.environ["CLAIM_TEXT"]
|
|
140
|
+
low = text.lower()
|
|
141
|
+
phrases = [p.strip().lower() for p in os.environ["PHRASE_LIST"].split(",") if p.strip()]
|
|
142
|
+
benign = ("local", "localhost", "my machine", "in dev", "dev only",
|
|
143
|
+
"staging only", "dry run", "dry-run", "would deploy", "about to deploy",
|
|
144
|
+
"going to deploy", "preview deploy")
|
|
145
|
+
best = ""
|
|
146
|
+
for p in phrases:
|
|
147
|
+
for m in re.finditer(re.escape(p), low):
|
|
148
|
+
s, e = m.start(), m.end()
|
|
149
|
+
window = low[max(0, s - 50):min(len(low), e + 50)]
|
|
150
|
+
if any(b in window for b in benign):
|
|
151
|
+
continue
|
|
152
|
+
# span = the surrounding clause, trimmed, from the original-case text
|
|
153
|
+
cs = max(0, s - 30)
|
|
154
|
+
ce = min(len(text), e + 30)
|
|
155
|
+
span = text[cs:ce].strip()
|
|
156
|
+
span = re.sub(r"\s+", " ", span)
|
|
157
|
+
if span:
|
|
158
|
+
best = span[:120]
|
|
159
|
+
break
|
|
160
|
+
if best:
|
|
161
|
+
break
|
|
162
|
+
print(best)
|
|
163
|
+
PY
|
|
164
|
+
)
|
|
165
|
+
[ -n "$CLAIM_SPAN" ] || pass_through
|
|
166
|
+
|
|
167
|
+
# --- 3. resolve the target (repo + claimed ref) --------------------------
|
|
168
|
+
REPO="${DRG_REPO:-}"
|
|
169
|
+
if [ -z "$REPO" ]; then
|
|
170
|
+
REPO=$("$GH" repo view --json nameWithOwner -q .nameWithOwner 2>/dev/null || true)
|
|
171
|
+
fi
|
|
172
|
+
|
|
173
|
+
CLAIMED_REF="${DRG_CLAIMED_REF:-}"
|
|
174
|
+
if [ -z "$CLAIMED_REF" ]; then
|
|
175
|
+
# `@<sha>` first (e.g. "api@abc1234"), else a bare 7–40 hex token.
|
|
176
|
+
CLAIMED_REF=$(printf '%s' "$CLAIM_SPAN" | grep -oiE '@[0-9a-f]{7,40}' | head -1 | tr -d '@')
|
|
177
|
+
[ -n "$CLAIMED_REF" ] || CLAIMED_REF=$(printf '%s' "$CLAIM_SPAN" | grep -oiE '\b[0-9a-f]{7,40}\b' | head -1)
|
|
178
|
+
fi
|
|
179
|
+
|
|
180
|
+
CLAIM_TIME=$(now_iso)
|
|
181
|
+
READBACK_QUERY="repos/${REPO:-?}/deployments?environment=${ENVIRONMENT}&sha=${CLAIMED_REF:-?}&per_page=1 + /statuses"
|
|
182
|
+
|
|
183
|
+
# emit_receipt <queried_ref> <queried_state> <readback_time> <target>
|
|
184
|
+
emit_receipt() {
|
|
185
|
+
jq -n \
|
|
186
|
+
--arg cs "$CLAIM_SPAN" --arg ct "$CLAIM_TIME" --arg tg "$4" \
|
|
187
|
+
--arg cr "$CLAIMED_REF" --arg au "$AUTHORITY" --arg rq "$READBACK_QUERY" \
|
|
188
|
+
--arg qr "$1" --arg qs "$2" --arg rt "$3" \
|
|
189
|
+
--argjson sm "$STALE_MS" \
|
|
190
|
+
'{claim_span:$cs, claim_time:$ct, target:$tg, claimed_ref:$cr,
|
|
191
|
+
authority:$au, readback_query:$rq, queried_ref:$qr, queried_state:$qs,
|
|
192
|
+
readback_time:$rt, stale_if_older_than_ms:$sm}'
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
TARGET="${REPO:-?}/${ENVIRONMENT}"
|
|
196
|
+
|
|
197
|
+
# A deployment claim with no resolvable target: advisory passes through,
|
|
198
|
+
# strict fails closed (a named-but-unverifiable deploy is the spec's
|
|
199
|
+
# "strict mode refuses any closeout with a deployment phrase but no
|
|
200
|
+
# resolvable target").
|
|
201
|
+
if [ -z "$REPO" ] || [ -z "$CLAIMED_REF" ]; then
|
|
202
|
+
if [ "$STRICT" = "1" ]; then
|
|
203
|
+
emit_receipt "" "query-failure" "$(now_iso)" "$TARGET"
|
|
204
|
+
exit 0
|
|
205
|
+
fi
|
|
206
|
+
pass_through
|
|
207
|
+
fi
|
|
208
|
+
|
|
209
|
+
# --- 4. read the authority: GitHub Deployments API -----------------------
|
|
210
|
+
DEPLOYMENTS=$("$GH" api "repos/$REPO/deployments?environment=$ENVIRONMENT&sha=$CLAIMED_REF&per_page=1" 2>/dev/null)
|
|
211
|
+
if [ $? -ne 0 ] || [ -z "$DEPLOYMENTS" ]; then
|
|
212
|
+
# Authority unreachable / errored → truth never established. Fail closed.
|
|
213
|
+
emit_receipt "" "query-failure" "$(now_iso)" "$TARGET"
|
|
214
|
+
exit 0
|
|
215
|
+
fi
|
|
216
|
+
|
|
217
|
+
DEP_ID=$(printf '%s' "$DEPLOYMENTS" | jq -r 'if type=="array" then (.[0].id // empty) else (.id // empty) end' 2>/dev/null)
|
|
218
|
+
DEP_SHA=$(printf '%s' "$DEPLOYMENTS" | jq -r 'if type=="array" then (.[0].sha // empty) else (.sha // empty) end' 2>/dev/null)
|
|
219
|
+
|
|
220
|
+
if [ -z "$DEP_ID" ]; then
|
|
221
|
+
# Authority reached, but it reports no deployment for this ref/env.
|
|
222
|
+
# Truth established: the claimed deploy is not on record → mismatch.
|
|
223
|
+
emit_receipt "" "not_found" "$(now_iso)" "$TARGET"
|
|
224
|
+
exit 0
|
|
225
|
+
fi
|
|
226
|
+
|
|
227
|
+
STATUSES=$("$GH" api "repos/$REPO/deployments/$DEP_ID/statuses?per_page=1" 2>/dev/null)
|
|
228
|
+
if [ $? -ne 0 ] || [ -z "$STATUSES" ]; then
|
|
229
|
+
emit_receipt "$DEP_SHA" "query-failure" "$(now_iso)" "$TARGET"
|
|
230
|
+
exit 0
|
|
231
|
+
fi
|
|
232
|
+
|
|
233
|
+
STATE=$(printf '%s' "$STATUSES" | jq -r 'if type=="array" then (.[0].state // empty) else (.state // empty) end' 2>/dev/null)
|
|
234
|
+
CREATED_AT=$(printf '%s' "$STATUSES" | jq -r 'if type=="array" then (.[0].created_at // empty) else (.created_at // empty) end' 2>/dev/null)
|
|
235
|
+
|
|
236
|
+
if [ -z "$STATE" ]; then
|
|
237
|
+
# Deployment exists but has no status yet → not confirmed.
|
|
238
|
+
emit_receipt "$DEP_SHA" "no_status" "$(now_iso)" "$TARGET"
|
|
239
|
+
exit 0
|
|
240
|
+
fi
|
|
241
|
+
|
|
242
|
+
# readback_time MUST be the authority's own timestamp (the status
|
|
243
|
+
# created_at), so the gate's staleness check is anchored to when the
|
|
244
|
+
# authority observed the state — not to when this hook ran.
|
|
245
|
+
[ -n "$CREATED_AT" ] || CREATED_AT=$(now_iso)
|
|
246
|
+
emit_receipt "$DEP_SHA" "$STATE" "$CREATED_AT" "$TARGET"
|
|
247
|
+
exit 0
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
#
|
|
17
17
|
# See: https://github.com/anthropics/claude-code/issues/40861
|
|
18
18
|
#
|
|
19
|
-
# TRIGGER: PreToolUse MATCHER: "Bash"
|
|
19
|
+
# TRIGGER: PreToolUse MATCHER: "Bash|PowerShell"
|
|
20
20
|
#
|
|
21
21
|
# Configuration:
|
|
22
22
|
# CC_DEPLOY_COMMANDS — regex pattern for deploy commands
|
|
@@ -27,11 +27,34 @@
|
|
|
27
27
|
# ================================================================
|
|
28
28
|
|
|
29
29
|
INPUT=$(cat)
|
|
30
|
+
__CC_HOOK_INPUT="$INPUT"
|
|
31
|
+
# --- セッション識別子でファイルを分ける (2026-08-09 修正) ---
|
|
32
|
+
# 旧実装は "$$" を使っていたが、$$ はこのスクリプト自身のPIDで呼び出しごとに変わるため、
|
|
33
|
+
# 状態が一度も持続しなかった(実測: 200回呼ぶとファイルが200個でき、中身は全部 1)。
|
|
34
|
+
__CC_SID=""
|
|
35
|
+
if [ -n "${__CC_HOOK_INPUT:-}" ]; then
|
|
36
|
+
if command -v jq >/dev/null 2>&1; then
|
|
37
|
+
__CC_SID=$(printf '%s' "$__CC_HOOK_INPUT" | jq -r '.session_id // .sessionId // empty' 2>/dev/null)
|
|
38
|
+
fi
|
|
39
|
+
if [ -z "$__CC_SID" ] && command -v python3 >/dev/null 2>&1; then
|
|
40
|
+
__CC_SID=$(printf '%s' "$__CC_HOOK_INPUT" | python3 -c 'import sys,json
|
|
41
|
+
try:
|
|
42
|
+
d=json.load(sys.stdin); print(d.get("session_id") or d.get("sessionId") or "")
|
|
43
|
+
except Exception: print("")' 2>/dev/null)
|
|
44
|
+
fi
|
|
45
|
+
fi
|
|
46
|
+
if [ -n "$__CC_SID" ]; then
|
|
47
|
+
__CC_KEY=$(printf '%s' "$__CC_SID" | tr -c 'A-Za-z0-9_-' '_' | cut -c1-64)
|
|
48
|
+
else
|
|
49
|
+
__CC_KEY="nosession-$(date +%Y%m%d)"
|
|
50
|
+
fi
|
|
51
|
+
# --- ここまで ---
|
|
52
|
+
|
|
30
53
|
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
31
54
|
|
|
32
55
|
[ -z "$COMMAND" ] && exit 0
|
|
33
56
|
|
|
34
|
-
MARKER="/tmp/cc-deploy-pending
|
|
57
|
+
MARKER="/tmp/cc-deploy-pending-$__CC_KEY"
|
|
35
58
|
|
|
36
59
|
# Configurable deploy command patterns
|
|
37
60
|
DEPLOY_PATTERN="${CC_DEPLOY_COMMANDS:-systemctl\s+restart|docker\s+restart|docker-compose\s+up|docker\s+compose\s+up|\bdeploy\b|kubectl\s+apply|terraform\s+apply|heroku\s+.*push|fly\s+deploy}"
|