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,141 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# The three git guards never saw `git -C <path> <verb>`
|
|
3
|
+
#
|
|
4
|
+
# git accepts its own options between the program name and the verb:
|
|
5
|
+
#
|
|
6
|
+
# git -C /repo push --force
|
|
7
|
+
# git -c core.pager=cat reset --hard
|
|
8
|
+
# git --git-dir=/repo/.git clean -fd
|
|
9
|
+
#
|
|
10
|
+
# All three do exactly what the unprefixed form does. Every git pattern in
|
|
11
|
+
# branch-guard, secret-guard and destructive-guard expected the verb to sit
|
|
12
|
+
# directly after the word `git`, so all three hooks exited 0 -- which is an
|
|
13
|
+
# affirmative approval, not a shrug.
|
|
14
|
+
#
|
|
15
|
+
# Measured 2026-09-03 against the shipped copies: 10 bypasses.
|
|
16
|
+
# branch-guard git -C push to a protected branch, git -C force push,
|
|
17
|
+
# --git-dir force push, and `git push origin +branch`
|
|
18
|
+
# (a force push with no --force anywhere in it)
|
|
19
|
+
# secret-guard git -C add .env, git -C add id_rsa
|
|
20
|
+
# destructive-guard git -C reset --hard, git -C clean -fd,
|
|
21
|
+
# git -C checkout --force, --git-dir reset --hard
|
|
22
|
+
#
|
|
23
|
+
# destructive-guard needed two fixes, not one. The checks themselves were only
|
|
24
|
+
# half of it: the mentions-vs-invocations pre-filter counted git verbs in the
|
|
25
|
+
# raw text, scored `git -C /repo reset --hard` as zero verbs, and exited before
|
|
26
|
+
# any check ran. Fixing the checks alone changed nothing measurable. Same shape,
|
|
27
|
+
# one layer earlier -- which is where it decided the outcome.
|
|
28
|
+
#
|
|
29
|
+
# The fix normalises the globals away once (cc_strip_git_globals) and leaves the
|
|
30
|
+
# patterns readable, rather than threading an optional-globals group through
|
|
31
|
+
# every one of them.
|
|
32
|
+
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
33
|
+
BG=$(mktemp); SG=$(mktemp); DG=$(mktemp)
|
|
34
|
+
trap 'rm -f "$BG" "$SG" "$DG"' EXIT
|
|
35
|
+
python3 -c "
|
|
36
|
+
import json,sys
|
|
37
|
+
d=json.load(open('$ROOT/scripts.json'))
|
|
38
|
+
open('$BG','w').write(d['branch-guard'])
|
|
39
|
+
open('$SG','w').write(d['secret-guard'])
|
|
40
|
+
open('$DG','w').write(d['destructive-guard'])"
|
|
41
|
+
|
|
42
|
+
PASS=0; FAIL=0
|
|
43
|
+
|
|
44
|
+
check() {
|
|
45
|
+
local guard="$1" desc="$2" cmd="$3" want="$4" # want: 2 = blocked, 0 = allowed
|
|
46
|
+
local payload code
|
|
47
|
+
payload=$(python3 -c '
|
|
48
|
+
import json,sys
|
|
49
|
+
print(json.dumps({"tool_name":"Bash","tool_input":{"command":sys.argv[1]}}))' "$cmd")
|
|
50
|
+
printf '%s' "$payload" | bash "$guard" >/dev/null 2>&1
|
|
51
|
+
code=$?
|
|
52
|
+
if [ "$code" = "$want" ]; then
|
|
53
|
+
echo "PASS: $desc"; PASS=$((PASS+1))
|
|
54
|
+
else
|
|
55
|
+
echo "FAIL: $desc (expected exit $want, got $code)"; FAIL=$((FAIL+1))
|
|
56
|
+
fi
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# --- branch-guard: globals before `push` ---
|
|
60
|
+
check "$BG" "bg: -C, protected branch" 'git -C /repo push origin main' 2
|
|
61
|
+
check "$BG" "bg: -C, force push" 'git -C /repo push origin feature --force' 2
|
|
62
|
+
check "$BG" "bg: --git-dir, force push" 'git --git-dir=/r/.git push --force' 2
|
|
63
|
+
check "$BG" "bg: -c, force push" 'git -c core.pager=cat push --force' 2
|
|
64
|
+
check "$BG" "bg: --no-pager then -C" 'git --no-pager -C /r push --force' 2
|
|
65
|
+
check "$BG" "bg: canonical force push" 'git push --force origin feature' 2
|
|
66
|
+
check "$BG" "bg: canonical protected push" 'git push origin main' 2
|
|
67
|
+
|
|
68
|
+
# --- branch-guard: `+branch` is a force push with no --force in it ---
|
|
69
|
+
check "$BG" "bg: +refspec, feature branch" 'git push origin +feature:feature' 2
|
|
70
|
+
check "$BG" "bg: +refspec, no colon" 'git push origin +feature' 2
|
|
71
|
+
check "$BG" "bg: -C plus +refspec" 'git -C /repo push origin +feature' 2
|
|
72
|
+
|
|
73
|
+
# --- branch-guard: still lets ordinary work through ---
|
|
74
|
+
check "$BG" "bg: ordinary push" 'git push origin feature' 0
|
|
75
|
+
check "$BG" "bg: -C ordinary push" 'git -C /repo push origin feature' 0
|
|
76
|
+
check "$BG" "bg: dry run" 'git push --dry-run origin feature' 0
|
|
77
|
+
check "$BG" "bg: rm -f after the push is not a force push" \
|
|
78
|
+
'git push origin feature && rm -f tmp.txt' 0
|
|
79
|
+
|
|
80
|
+
# --- secret-guard: globals before `add` ---
|
|
81
|
+
check "$SG" "sg: -C, .env" 'git -C /repo add .env' 2
|
|
82
|
+
check "$SG" "sg: -C, private key" 'git -C /repo add id_rsa' 2
|
|
83
|
+
check "$SG" "sg: --git-dir, .env" 'git --git-dir=/r/.git add .env' 2
|
|
84
|
+
check "$SG" "sg: canonical .env" 'git add .env' 2
|
|
85
|
+
check "$SG" "sg: ordinary add" 'git add src/main.py' 0
|
|
86
|
+
check "$SG" "sg: -C ordinary add" 'git -C /repo add src/main.py' 0
|
|
87
|
+
|
|
88
|
+
# --- destructive-guard: globals before the verb ---
|
|
89
|
+
check "$DG" "dg: -C, reset --hard" 'git -C /repo reset --hard HEAD~5' 2
|
|
90
|
+
check "$DG" "dg: --git-dir, reset --hard" 'git --git-dir=/r/.git reset --hard' 2
|
|
91
|
+
check "$DG" "dg: -c, reset --hard" 'git -c user.name=x reset --hard' 2
|
|
92
|
+
check "$DG" "dg: -C, clean -fd" 'git -C /repo clean -fd' 2
|
|
93
|
+
check "$DG" "dg: -C, checkout --force" 'git -C /repo checkout --force main' 2
|
|
94
|
+
check "$DG" "dg: canonical reset --hard" 'git reset --hard HEAD~1' 2
|
|
95
|
+
check "$DG" "dg: canonical clean" 'git clean -fd' 2
|
|
96
|
+
|
|
97
|
+
# --- destructive-guard: the pre-filter still tells mentions from invocations ---
|
|
98
|
+
check "$DG" "dg: reset --hard inside a quoted string" \
|
|
99
|
+
"echo 'git reset --hard は危険'" 0
|
|
100
|
+
check "$DG" "dg: -C read-only" 'git -C /repo log' 0
|
|
101
|
+
check "$DG" "dg: -C dry-run clean" 'git -C /repo clean --dry-run' 0
|
|
102
|
+
check "$DG" "dg: reset --soft" 'git reset --soft HEAD~1' 0
|
|
103
|
+
check "$DG" "dg: status" 'git status' 0
|
|
104
|
+
|
|
105
|
+
# --- 2026-09-14: the same family, found by applying an outside report (#1117) ---
|
|
106
|
+
# The normaliser only knew the `=` spelling of the long options, and its list
|
|
107
|
+
# was missing several that git accepts. git takes `--git-dir <path>` with a
|
|
108
|
+
# space just as happily as `--git-dir=<path>`.
|
|
109
|
+
check "$DG" "dg: --git-dir with a separate value" 'git --git-dir /tmp/r/repo.d reset --hard' 2
|
|
110
|
+
check "$DG" "dg: --config-env" 'git --config-env=a=B reset --hard' 2
|
|
111
|
+
check "$DG" "dg: --no-optional-locks" 'git --no-optional-locks reset --hard' 2
|
|
112
|
+
check "$DG" "dg: --work-tree with a separate value" 'git --work-tree /tmp/w reset --hard' 2
|
|
113
|
+
check "$DG" "dg: --no-advice" 'git --no-advice clean -fd' 2
|
|
114
|
+
# and the same options must not start blocking ordinary work
|
|
115
|
+
check "$DG" "dg: --no-optional-locks status" 'git --no-optional-locks status' 0
|
|
116
|
+
check "$DG" "dg: --git-dir separate value, log" 'git --git-dir /tmp/r/repo.d log --oneline' 0
|
|
117
|
+
|
|
118
|
+
# --- 2026-09-14, second pass: `git` did not have to be the first word ---
|
|
119
|
+
# The git checks anchored on `(^|;|&+|\|\|)\s*git`, so anything in front of the
|
|
120
|
+
# word - an absolute path, a wrapper, an environment assignment - put the
|
|
121
|
+
# command out of reach. Check 0y, the pre-filter that decides whether a
|
|
122
|
+
# destructive verb is present at all, already counted every one of these as an
|
|
123
|
+
# invocation; only the checks it feeds were narrower. Both now share CC_CMDPOS.
|
|
124
|
+
check "$DG" "dg: git by absolute path" '/usr/bin/git reset --hard' 2
|
|
125
|
+
check "$DG" "dg: git by relative path" './git reset --hard' 2
|
|
126
|
+
check "$DG" "dg: sudo git" 'sudo git reset --hard' 2
|
|
127
|
+
check "$DG" "dg: env assignment in front" 'env FOO=1 git reset --hard' 2
|
|
128
|
+
check "$DG" "dg: bare assignment in front" 'FOO=1 git reset --hard' 2
|
|
129
|
+
check "$DG" "dg: time wrapper" 'time git reset --hard' 2
|
|
130
|
+
check "$DG" "dg: nohup wrapper" 'nohup git reset --hard' 2
|
|
131
|
+
check "$DG" "dg: absolute path, clean -fd" '/usr/bin/git clean -fd' 2
|
|
132
|
+
check "$DG" "dg: absolute path, checkout -f" '/usr/bin/git checkout --force main' 2
|
|
133
|
+
# The widened head must not start blocking the things it never blocked before.
|
|
134
|
+
check "$DG" "dg: mentions it in an echo" "echo 'git reset --hard' >> notes.md" 0
|
|
135
|
+
check "$DG" "dg: greps for the words" "grep -r 'git reset --hard' ./docs" 0
|
|
136
|
+
check "$DG" "dg: reset --soft" 'git reset --soft HEAD~1' 0
|
|
137
|
+
check "$DG" "dg: a path that merely ends in git" 'ls /usr/bin/git' 0
|
|
138
|
+
|
|
139
|
+
echo
|
|
140
|
+
echo "PASS: $PASS FAIL: $FAIL"
|
|
141
|
+
[ "$FAIL" = 0 ]
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# `git` did not have to be the first word -- in branch-guard and secret-guard too
|
|
3
|
+
#
|
|
4
|
+
# 2026-09-14, #1121 moved destructive-guard's git checks off the "start of a
|
|
5
|
+
# segment" anchor and onto CC_CMDPOS, which also recognises a path, a sudo, an
|
|
6
|
+
# environment assignment and the usual wrappers. branch-guard and secret-guard
|
|
7
|
+
# have the same gate and were not touched, so the fix covered one guard out of
|
|
8
|
+
# three.
|
|
9
|
+
#
|
|
10
|
+
# Measured the same day, nine invocation forms against each subcommand, feeding
|
|
11
|
+
# each guard the same JSON on stdin:
|
|
12
|
+
#
|
|
13
|
+
# destructive-guard 9 of 9 blocked (fixed in #1121)
|
|
14
|
+
# branch-guard 2 of 9 blocked
|
|
15
|
+
# secret-guard 2 of 9 blocked
|
|
16
|
+
#
|
|
17
|
+
# The seven that walked through were `/usr/bin/git`, `./git`, `sudo git`,
|
|
18
|
+
# `env FOO=1 git`, `FOO=1 git`, `time git` and `nohup git`. Each one exited 0,
|
|
19
|
+
# which Claude Code reads as "this hook has no objection", not as "this hook did
|
|
20
|
+
# not look".
|
|
21
|
+
#
|
|
22
|
+
# Also here: `git send-pack --force origin main`. send-pack is the plumbing
|
|
23
|
+
# spelling of a push, and branch-guard's own header says it blocks force pushes
|
|
24
|
+
# on all branches and pushes to protected branches -- but its gate only knew the
|
|
25
|
+
# word `push`, so both of those promises were spelled around.
|
|
26
|
+
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
27
|
+
BG=$(mktemp); SG=$(mktemp); DG=$(mktemp)
|
|
28
|
+
trap 'rm -f "$BG" "$SG" "$DG"' EXIT
|
|
29
|
+
python3 -c "
|
|
30
|
+
import json,sys
|
|
31
|
+
d=json.load(open('$ROOT/scripts.json'))
|
|
32
|
+
open('$BG','w').write(d['branch-guard'])
|
|
33
|
+
open('$SG','w').write(d['secret-guard'])
|
|
34
|
+
open('$DG','w').write(d['destructive-guard'])"
|
|
35
|
+
|
|
36
|
+
PASS=0; FAIL=0
|
|
37
|
+
|
|
38
|
+
check() {
|
|
39
|
+
local guard="$1" desc="$2" cmd="$3" want="$4" # want: 2 = blocked, 0 = allowed
|
|
40
|
+
local payload code
|
|
41
|
+
payload=$(python3 -c '
|
|
42
|
+
import json,sys
|
|
43
|
+
print(json.dumps({"tool_name":"Bash","tool_input":{"command":sys.argv[1]}}))' "$cmd")
|
|
44
|
+
printf '%s' "$payload" | bash "$guard" >/dev/null 2>&1
|
|
45
|
+
code=$?
|
|
46
|
+
if [ "$code" = "$want" ]; then
|
|
47
|
+
echo "PASS: $desc"; PASS=$((PASS+1))
|
|
48
|
+
else
|
|
49
|
+
echo "FAIL: $desc (expected exit $want, got $code)"; FAIL=$((FAIL+1))
|
|
50
|
+
fi
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# The nine invocation forms. `%s` is where the git command goes.
|
|
54
|
+
PREFIXES=(
|
|
55
|
+
'%s'
|
|
56
|
+
'/usr/bin/%s'
|
|
57
|
+
'./%s'
|
|
58
|
+
'sudo %s'
|
|
59
|
+
'env FOO=1 %s'
|
|
60
|
+
'FOO=1 %s'
|
|
61
|
+
'time %s'
|
|
62
|
+
'nohup %s'
|
|
63
|
+
'cd /tmp && %s'
|
|
64
|
+
)
|
|
65
|
+
PREFIX_NAMES=(bare abspath relpath sudo env-assign leading-assign time nohup separator)
|
|
66
|
+
|
|
67
|
+
# Each guard with a subcommand it owns. All nine forms must be refused.
|
|
68
|
+
run_grid() {
|
|
69
|
+
local guard="$1" label="$2" sub="$3"
|
|
70
|
+
local i fmt name cmd
|
|
71
|
+
for i in "${!PREFIXES[@]}"; do
|
|
72
|
+
fmt="${PREFIXES[$i]}"; name="${PREFIX_NAMES[$i]}"
|
|
73
|
+
# shellcheck disable=SC2059
|
|
74
|
+
cmd=$(printf "$fmt" "$sub")
|
|
75
|
+
check "$guard" "$label [$name]" "$cmd" 2
|
|
76
|
+
done
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
run_grid "$DG" "dg: reset --hard" 'git reset --hard HEAD~5'
|
|
80
|
+
run_grid "$DG" "dg: clean -fd" 'git clean -fd'
|
|
81
|
+
run_grid "$BG" "bg: force push" 'git push --force origin main'
|
|
82
|
+
run_grid "$BG" "bg: +main is a force" 'git push origin +main'
|
|
83
|
+
run_grid "$BG" "bg: -uf short cluster" 'git push -uf origin feature'
|
|
84
|
+
run_grid "$SG" "sg: stage .env" 'git add .env'
|
|
85
|
+
|
|
86
|
+
# --- send-pack: the plumbing spelling of a push ---
|
|
87
|
+
check "$BG" "bg: send-pack --force" 'git send-pack --force origin main' 2
|
|
88
|
+
check "$BG" "bg: send-pack --force with -C" 'git -C /repo send-pack --force origin main' 2
|
|
89
|
+
check "$BG" "bg: send-pack to main" 'git send-pack origin main' 2
|
|
90
|
+
check "$BG" "bg: send-pack to a feature" 'git send-pack origin feature' 0
|
|
91
|
+
|
|
92
|
+
# --- the approve side: widening the gate must not start refusing ordinary work ---
|
|
93
|
+
check "$BG" "bg: ordinary push to a feature" 'git push origin feature' 0
|
|
94
|
+
check "$BG" "bg: -u to a feature" 'git push -u origin feature' 0
|
|
95
|
+
check "$SG" "sg: staging a normal file" 'git add README.md' 0
|
|
96
|
+
check "$SG" "sg: staging a source file" 'git add src/config.ts' 0
|
|
97
|
+
check "$DG" "dg: a dry run is not a delete" 'git clean -nd' 0
|
|
98
|
+
|
|
99
|
+
# CC_CMDPOS accepts a path in front of git. It must still require the word to be
|
|
100
|
+
# `git` and not merely to start with it.
|
|
101
|
+
check "$BG" "bg: gitk is not git" '/usr/bin/gitk push --force origin main' 0
|
|
102
|
+
check "$DG" "dg: a lookalike binary" './gitfoo reset --hard HEAD~5' 0
|
|
103
|
+
|
|
104
|
+
# Mentions are not invocations. A space in front of `git` is not a segment break,
|
|
105
|
+
# so quoting or grepping for the command must stay allowed.
|
|
106
|
+
check "$BG" "bg: the command inside a string" 'echo "run git push --force if you must"' 0
|
|
107
|
+
check "$DG" "dg: searching for the command" "grep -rn 'git reset --hard' ." 0
|
|
108
|
+
|
|
109
|
+
# ...but the wrappers that really do run it are invocations.
|
|
110
|
+
check "$BG" "bg: xargs runs it" 'echo main | xargs git push --force origin' 2
|
|
111
|
+
|
|
112
|
+
echo
|
|
113
|
+
echo "PASS: $PASS FAIL: $FAIL"
|
|
114
|
+
[ "$FAIL" -eq 0 ]
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Tests for git-maintenance-guard.sh
|
|
3
|
+
# Run: bash tests/git-maintenance-guard.test.sh
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
PASS=0
|
|
7
|
+
FAIL=0
|
|
8
|
+
HOOK="$(dirname "$0")/../examples/git-maintenance-guard.sh"
|
|
9
|
+
|
|
10
|
+
run_hook() {
|
|
11
|
+
local input="$1" expected_exit="$2" desc="$3" extra_env="${4:-}"
|
|
12
|
+
local actual_exit=0
|
|
13
|
+
if [ -n "$extra_env" ]; then
|
|
14
|
+
# shellcheck disable=SC2086
|
|
15
|
+
echo "$input" | env $extra_env bash "$HOOK" >/dev/null 2>/dev/null || actual_exit=$?
|
|
16
|
+
else
|
|
17
|
+
echo "$input" | bash "$HOOK" >/dev/null 2>/dev/null || actual_exit=$?
|
|
18
|
+
fi
|
|
19
|
+
if [ "$actual_exit" -eq "$expected_exit" ]; then
|
|
20
|
+
echo " PASS: $desc"
|
|
21
|
+
PASS=$((PASS + 1))
|
|
22
|
+
else
|
|
23
|
+
echo " FAIL: $desc (expected exit $expected_exit, got $actual_exit)"
|
|
24
|
+
FAIL=$((FAIL + 1))
|
|
25
|
+
fi
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
echo "git-maintenance-guard.sh tests"
|
|
29
|
+
echo ""
|
|
30
|
+
|
|
31
|
+
# --- Block git gc ---
|
|
32
|
+
run_hook '{"tool_input":{"command":"git gc"}}' 2 "Block bare git gc"
|
|
33
|
+
run_hook '{"tool_input":{"command":"git gc --auto"}}' 2 "Block git gc --auto"
|
|
34
|
+
run_hook '{"tool_input":{"command":"git gc --aggressive"}}' 2 "Block git gc --aggressive"
|
|
35
|
+
run_hook '{"tool_input":{"command":"git gc --prune=now"}}' 2 "Block git gc --prune=now"
|
|
36
|
+
run_hook '{"tool_input":{"command":"cd ~/repo && git gc"}}' 2 "Block git gc after cd"
|
|
37
|
+
|
|
38
|
+
# --- Block git repack ---
|
|
39
|
+
run_hook '{"tool_input":{"command":"git repack"}}' 2 "Block bare git repack"
|
|
40
|
+
run_hook '{"tool_input":{"command":"git repack -a -d"}}' 2 "Block git repack -a -d"
|
|
41
|
+
run_hook '{"tool_input":{"command":"git repack -A -d --depth=250 --window=250"}}' 2 "Block heavy git repack"
|
|
42
|
+
|
|
43
|
+
# --- Block git maintenance ---
|
|
44
|
+
run_hook '{"tool_input":{"command":"git maintenance run"}}' 2 "Block git maintenance run"
|
|
45
|
+
run_hook '{"tool_input":{"command":"git maintenance start"}}' 2 "Block git maintenance start"
|
|
46
|
+
run_hook '{"tool_input":{"command":"git maintenance stop"}}' 2 "Block git maintenance stop"
|
|
47
|
+
run_hook '{"tool_input":{"command":"git maintenance register"}}' 2 "Block git maintenance register"
|
|
48
|
+
|
|
49
|
+
# --- Block git prune ---
|
|
50
|
+
run_hook '{"tool_input":{"command":"git prune"}}' 2 "Block bare git prune"
|
|
51
|
+
run_hook '{"tool_input":{"command":"git prune --expire=now"}}' 2 "Block git prune --expire=now"
|
|
52
|
+
run_hook '{"tool_input":{"command":"git prune-packed"}}' 2 "Block git prune-packed"
|
|
53
|
+
|
|
54
|
+
# --- Allow safe git commands ---
|
|
55
|
+
run_hook '{"tool_input":{"command":"git status"}}' 0 "Allow git status"
|
|
56
|
+
run_hook '{"tool_input":{"command":"git log --oneline"}}' 0 "Allow git log"
|
|
57
|
+
run_hook '{"tool_input":{"command":"git commit -m fix"}}' 0 "Allow git commit"
|
|
58
|
+
run_hook '{"tool_input":{"command":"git push"}}' 0 "Allow git push"
|
|
59
|
+
run_hook '{"tool_input":{"command":"git fetch"}}' 0 "Allow git fetch"
|
|
60
|
+
run_hook '{"tool_input":{"command":"git pull"}}' 0 "Allow git pull"
|
|
61
|
+
run_hook '{"tool_input":{"command":"git checkout main"}}' 0 "Allow git checkout"
|
|
62
|
+
run_hook '{"tool_input":{"command":"git rebase main"}}' 0 "Allow git rebase"
|
|
63
|
+
run_hook '{"tool_input":{"command":"git count-objects -v"}}' 0 "Allow git count-objects"
|
|
64
|
+
run_hook '{"tool_input":{"command":"git fsck"}}' 0 "Allow git fsck"
|
|
65
|
+
run_hook '{"tool_input":{"command":"git verify-pack -v test.idx"}}' 0 "Allow git verify-pack"
|
|
66
|
+
|
|
67
|
+
# --- Allow unrelated commands containing the words ---
|
|
68
|
+
run_hook '{"tool_input":{"command":"echo gc"}}' 0 "Allow non-git gc"
|
|
69
|
+
run_hook '{"tool_input":{"command":"echo repack"}}' 0 "Allow non-git repack"
|
|
70
|
+
run_hook '{"tool_input":{"command":"echo maintenance"}}' 0 "Allow non-git maintenance"
|
|
71
|
+
run_hook '{"tool_input":{"command":"prune-old-files.sh"}}' 0 "Allow non-git prune"
|
|
72
|
+
|
|
73
|
+
# --- Override flags ---
|
|
74
|
+
run_hook '{"tool_input":{"command":"git gc"}}' 0 "ALLOW flag bypasses block" "CC_GIT_MAINTENANCE_ALLOW=1"
|
|
75
|
+
run_hook '{"tool_input":{"command":"git repack"}}' 0 "DISABLE flag bypasses block" "CC_GIT_MAINTENANCE_DISABLE=1"
|
|
76
|
+
|
|
77
|
+
# --- Empty / missing input ---
|
|
78
|
+
run_hook '{}' 0 "Allow empty input"
|
|
79
|
+
run_hook '{"tool_input":{}}' 0 "Allow missing command"
|
|
80
|
+
|
|
81
|
+
echo ""
|
|
82
|
+
echo "Results: $PASS passed, $FAIL failed out of $((PASS + FAIL)) tests"
|
|
83
|
+
[ "$FAIL" -eq 0 ] && exit 0 || exit 1
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Tests for git-pack-temp-cleanup-guard.sh
|
|
3
|
+
# Run: bash tests/git-pack-temp-cleanup-guard.test.sh
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
PASS=0
|
|
7
|
+
FAIL=0
|
|
8
|
+
HOOK="$(cd "$(dirname "$0")" && pwd)/../examples/git-pack-temp-cleanup-guard.sh"
|
|
9
|
+
|
|
10
|
+
run_hook() {
|
|
11
|
+
local input="$1" expected_exit="$2" desc="$3" extra_env="${4:-}"
|
|
12
|
+
local actual_exit=0
|
|
13
|
+
local stderr_out
|
|
14
|
+
if [ -n "$extra_env" ]; then
|
|
15
|
+
# shellcheck disable=SC2086
|
|
16
|
+
stderr_out=$(echo "$input" | env $extra_env bash "$HOOK" 2>&1 >/dev/null) || actual_exit=$?
|
|
17
|
+
else
|
|
18
|
+
stderr_out=$(echo "$input" | bash "$HOOK" 2>&1 >/dev/null) || actual_exit=$?
|
|
19
|
+
fi
|
|
20
|
+
if [ "$actual_exit" -eq "$expected_exit" ]; then
|
|
21
|
+
echo " PASS: $desc"
|
|
22
|
+
PASS=$((PASS + 1))
|
|
23
|
+
else
|
|
24
|
+
echo " FAIL: $desc (expected exit $expected_exit, got $actual_exit)"
|
|
25
|
+
echo " stderr: $stderr_out"
|
|
26
|
+
FAIL=$((FAIL + 1))
|
|
27
|
+
fi
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
# Each test sets up an isolated git repo in a temp dir so we never
|
|
31
|
+
# touch the user's real .git directory.
|
|
32
|
+
setup_repo() {
|
|
33
|
+
local dir
|
|
34
|
+
dir=$(mktemp -d)
|
|
35
|
+
(cd "$dir" && git init -q)
|
|
36
|
+
echo "$dir"
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
teardown_repo() {
|
|
40
|
+
rm -rf "$1"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
echo "git-pack-temp-cleanup-guard.sh tests"
|
|
44
|
+
echo ""
|
|
45
|
+
|
|
46
|
+
# --- No-op cases ---
|
|
47
|
+
run_hook '{}' 0 "Allow empty input"
|
|
48
|
+
run_hook '{"tool_input":{}}' 0 "Allow missing command"
|
|
49
|
+
run_hook '{"tool_input":{"command":"echo hello"}}' 0 "Allow non-git command"
|
|
50
|
+
run_hook '{"tool_input":{"command":"ls -la"}}' 0 "Allow ls command"
|
|
51
|
+
|
|
52
|
+
# --- Git commands outside a repo ---
|
|
53
|
+
TMP=$(mktemp -d)
|
|
54
|
+
pushd "$TMP" >/dev/null
|
|
55
|
+
run_hook '{"tool_input":{"command":"git status"}}' 0 "Allow git command outside a repo (no .git found)"
|
|
56
|
+
popd >/dev/null
|
|
57
|
+
rm -rf "$TMP"
|
|
58
|
+
|
|
59
|
+
# --- Git commands inside a clean repo (no tmp_pack_*) ---
|
|
60
|
+
REPO=$(setup_repo)
|
|
61
|
+
pushd "$REPO" >/dev/null
|
|
62
|
+
run_hook '{"tool_input":{"command":"git status"}}' 0 "Allow git status in clean repo"
|
|
63
|
+
run_hook '{"tool_input":{"command":"git log"}}' 0 "Allow git log in clean repo"
|
|
64
|
+
run_hook '{"tool_input":{"command":"git commit -m fix"}}' 0 "Allow git commit in clean repo"
|
|
65
|
+
popd >/dev/null
|
|
66
|
+
teardown_repo "$REPO"
|
|
67
|
+
|
|
68
|
+
# --- Cleanup of stale tmp_pack_* (older than threshold) ---
|
|
69
|
+
REPO=$(setup_repo)
|
|
70
|
+
mkdir -p "$REPO/.git/objects/pack"
|
|
71
|
+
# Create a fake stale tmp_pack_ file. Use touch -d to backdate it 2 hours.
|
|
72
|
+
touch -d "2 hours ago" "$REPO/.git/objects/pack/tmp_pack_stale1"
|
|
73
|
+
touch -d "2 hours ago" "$REPO/.git/objects/pack/tmp_pack_stale2"
|
|
74
|
+
echo "x" > "$REPO/.git/objects/pack/tmp_pack_stale1"
|
|
75
|
+
touch -d "2 hours ago" "$REPO/.git/objects/pack/tmp_pack_stale1"
|
|
76
|
+
pushd "$REPO" >/dev/null
|
|
77
|
+
run_hook '{"tool_input":{"command":"git status"}}' 0 "Allow git status, stale tmp_pack_* should be removed"
|
|
78
|
+
remaining=$(find "$REPO/.git/objects/pack" -name 'tmp_pack_*' | wc -l)
|
|
79
|
+
if [ "$remaining" -eq 0 ]; then
|
|
80
|
+
echo " PASS: stale tmp_pack_* removed"
|
|
81
|
+
PASS=$((PASS + 1))
|
|
82
|
+
else
|
|
83
|
+
echo " FAIL: $remaining stale tmp_pack_* still present"
|
|
84
|
+
FAIL=$((FAIL + 1))
|
|
85
|
+
fi
|
|
86
|
+
popd >/dev/null
|
|
87
|
+
teardown_repo "$REPO"
|
|
88
|
+
|
|
89
|
+
# --- Recent tmp_pack_* must NOT be removed ---
|
|
90
|
+
REPO=$(setup_repo)
|
|
91
|
+
mkdir -p "$REPO/.git/objects/pack"
|
|
92
|
+
echo "x" > "$REPO/.git/objects/pack/tmp_pack_fresh"
|
|
93
|
+
pushd "$REPO" >/dev/null
|
|
94
|
+
run_hook '{"tool_input":{"command":"git status"}}' 0 "Allow git status, recent tmp_pack_* preserved"
|
|
95
|
+
remaining=$(find "$REPO/.git/objects/pack" -name 'tmp_pack_*' | wc -l)
|
|
96
|
+
if [ "$remaining" -eq 1 ]; then
|
|
97
|
+
echo " PASS: recent tmp_pack_* preserved"
|
|
98
|
+
PASS=$((PASS + 1))
|
|
99
|
+
else
|
|
100
|
+
echo " FAIL: recent tmp_pack_* changed (remaining=$remaining)"
|
|
101
|
+
FAIL=$((FAIL + 1))
|
|
102
|
+
fi
|
|
103
|
+
popd >/dev/null
|
|
104
|
+
teardown_repo "$REPO"
|
|
105
|
+
|
|
106
|
+
# --- Block when tmp_pack_* total exceeds CC_GIT_PACK_BLOCK_GB ---
|
|
107
|
+
# Use a tiny block threshold so a small synthetic file trips it.
|
|
108
|
+
REPO=$(setup_repo)
|
|
109
|
+
mkdir -p "$REPO/.git/objects/pack"
|
|
110
|
+
# Create a 2 MiB recent file; with CC_GIT_PACK_BLOCK_GB=0 even the
|
|
111
|
+
# smallest non-empty tmp_pack_ trips the block branch (>= 0 GiB).
|
|
112
|
+
dd if=/dev/zero of="$REPO/.git/objects/pack/tmp_pack_big" bs=1M count=2 status=none
|
|
113
|
+
pushd "$REPO" >/dev/null
|
|
114
|
+
run_hook '{"tool_input":{"command":"git status"}}' 2 "Block when remaining > block threshold" "CC_GIT_PACK_BLOCK_GB=0 CC_GIT_PACK_AGE_MIN=10000"
|
|
115
|
+
popd >/dev/null
|
|
116
|
+
teardown_repo "$REPO"
|
|
117
|
+
|
|
118
|
+
# --- DISABLE flag should bypass everything ---
|
|
119
|
+
REPO=$(setup_repo)
|
|
120
|
+
mkdir -p "$REPO/.git/objects/pack"
|
|
121
|
+
dd if=/dev/zero of="$REPO/.git/objects/pack/tmp_pack_big" bs=1M count=2 status=none
|
|
122
|
+
pushd "$REPO" >/dev/null
|
|
123
|
+
run_hook '{"tool_input":{"command":"git status"}}' 0 "DISABLE flag bypasses all checks" "CC_GIT_PACK_DISABLE=1 CC_GIT_PACK_BLOCK_GB=0"
|
|
124
|
+
popd >/dev/null
|
|
125
|
+
teardown_repo "$REPO"
|
|
126
|
+
|
|
127
|
+
# --- Non-git binaries with 'git' substring ---
|
|
128
|
+
run_hook '{"tool_input":{"command":"echo /usr/local/lib/gitlab"}}' 0 "Allow path with 'gitlab' substring"
|
|
129
|
+
run_hook '{"tool_input":{"command":"npm install gitignore-cli"}}' 0 "Allow npm install with gitignore-cli"
|
|
130
|
+
|
|
131
|
+
echo ""
|
|
132
|
+
echo "Results: $PASS passed, $FAIL failed out of $((PASS + FAIL)) tests"
|
|
133
|
+
[ "$FAIL" -eq 0 ] && exit 0 || exit 1
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Tests for git-rm-orphan-wipe-guard.sh
|
|
3
|
+
# Run: bash tests/git-rm-orphan-wipe-guard.test.sh
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
PASS=0
|
|
7
|
+
FAIL=0
|
|
8
|
+
HOOK="$(dirname "$0")/../examples/git-rm-orphan-wipe-guard.sh"
|
|
9
|
+
|
|
10
|
+
test_hook() {
|
|
11
|
+
local input="$1" expected_exit="$2" desc="$3"
|
|
12
|
+
local actual_exit=0
|
|
13
|
+
echo "$input" | bash "$HOOK" > /dev/null 2>/dev/null || actual_exit=$?
|
|
14
|
+
if [ "$actual_exit" -eq "$expected_exit" ]; then
|
|
15
|
+
echo " PASS: $desc"
|
|
16
|
+
PASS=$((PASS + 1))
|
|
17
|
+
else
|
|
18
|
+
echo " FAIL: $desc (expected exit $expected_exit, got $actual_exit)"
|
|
19
|
+
FAIL=$((FAIL + 1))
|
|
20
|
+
fi
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
echo "git-rm-orphan-wipe-guard.sh tests"
|
|
24
|
+
echo ""
|
|
25
|
+
|
|
26
|
+
# --- Block: the #70687 compound command ---
|
|
27
|
+
test_hook '{"tool_input":{"command":"git checkout --orphan wipe && git rm -rf . --quiet"}}' 2 "Block the #70687 orphan+rm compound"
|
|
28
|
+
|
|
29
|
+
# --- Block: git checkout/switch --orphan ---
|
|
30
|
+
test_hook '{"tool_input":{"command":"git checkout --orphan newroot"}}' 2 "Block git checkout --orphan"
|
|
31
|
+
test_hook '{"tool_input":{"command":"git switch --orphan fresh"}}' 2 "Block git switch --orphan"
|
|
32
|
+
test_hook '{"tool_input":{"command":"git checkout --orphan"}}' 2 "Block bare git checkout --orphan"
|
|
33
|
+
|
|
34
|
+
# --- Block: recursive git rm ---
|
|
35
|
+
test_hook '{"tool_input":{"command":"git rm -rf ."}}' 2 "Block git rm -rf ."
|
|
36
|
+
test_hook '{"tool_input":{"command":"git rm -r src"}}' 2 "Block git rm -r <dir>"
|
|
37
|
+
test_hook '{"tool_input":{"command":"git rm -fr ."}}' 2 "Block git rm -fr ."
|
|
38
|
+
test_hook '{"tool_input":{"command":"git rm --recursive build"}}' 2 "Block git rm --recursive"
|
|
39
|
+
|
|
40
|
+
# --- Block: whole-tree target without -r ---
|
|
41
|
+
test_hook '{"tool_input":{"command":"git rm ."}}' 2 "Block git rm ."
|
|
42
|
+
test_hook '{"tool_input":{"command":"git rm *"}}' 2 "Block git rm *"
|
|
43
|
+
|
|
44
|
+
# --- Allow: --cached (untracks only, disk files survive) ---
|
|
45
|
+
test_hook '{"tool_input":{"command":"git rm -r --cached ."}}' 0 "Allow git rm -r --cached . (untrack, no disk loss)"
|
|
46
|
+
test_hook '{"tool_input":{"command":"git rm --cached secrets.env"}}' 0 "Allow git rm --cached <file>"
|
|
47
|
+
|
|
48
|
+
# --- Allow: single-file git rm ---
|
|
49
|
+
test_hook '{"tool_input":{"command":"git rm old.txt"}}' 0 "Allow git rm <file>"
|
|
50
|
+
test_hook '{"tool_input":{"command":"git rm path/to/file.js"}}' 0 "Allow git rm <path/file>"
|
|
51
|
+
|
|
52
|
+
# --- Allow: safe git / unrelated commands ---
|
|
53
|
+
test_hook '{"tool_input":{"command":"git checkout main"}}' 0 "Allow git checkout <branch>"
|
|
54
|
+
test_hook '{"tool_input":{"command":"git switch feature"}}' 0 "Allow git switch <branch>"
|
|
55
|
+
test_hook '{"tool_input":{"command":"git status"}}' 0 "Allow git status"
|
|
56
|
+
test_hook '{"tool_input":{"command":"git commit -m fix"}}' 0 "Allow git commit"
|
|
57
|
+
test_hook '{"tool_input":{"command":"grep orphan README.md"}}' 0 "Allow grep orphan (substring, not git)"
|
|
58
|
+
test_hook '{"tool_input":{"command":"rm -rf node_modules"}}' 0 "Allow plain rm (rm-safety-net territory, not this guard)"
|
|
59
|
+
|
|
60
|
+
# --- Allow: empty/missing input ---
|
|
61
|
+
test_hook '{}' 0 "Allow empty input"
|
|
62
|
+
test_hook '{"tool_input":{}}' 0 "Allow missing command"
|
|
63
|
+
|
|
64
|
+
echo ""
|
|
65
|
+
echo "Results: $PASS passed, $FAIL failed out of $((PASS + FAIL)) tests"
|
|
66
|
+
[ "$FAIL" -eq 0 ] && exit 0 || exit 1
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Tests for git-submodule-guard.sh
|
|
3
|
+
# Verifies that the data-destroying forced deinit (#68920) is blocked,
|
|
4
|
+
# while unforced deinit / submodule rm are warn-only.
|
|
5
|
+
HOOK="examples/git-submodule-guard.sh"
|
|
6
|
+
PASS=0 FAIL=0
|
|
7
|
+
|
|
8
|
+
HOOK_ABS="$PWD/$HOOK"
|
|
9
|
+
|
|
10
|
+
# run_hook <command> [env assignments...] -> sets $OUT and $RC
|
|
11
|
+
run_hook() {
|
|
12
|
+
local cmd="$1"; shift
|
|
13
|
+
OUT=$(printf '{"tool_input":{"command":"%s"}}' "$cmd" | env "$@" bash "$HOOK_ABS" 2>&1)
|
|
14
|
+
RC=$?
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
assert_exit() { if [ "$2" = "$3" ]; then PASS=$((PASS+1)); else FAIL=$((FAIL+1)); echo "FAIL: $1 (expected exit $3, got $2)"; fi; }
|
|
18
|
+
assert_contains() { if echo "$2" | grep -q "$3"; then PASS=$((PASS+1)); else FAIL=$((FAIL+1)); echo "FAIL: $1 (expected '$3')"; fi; }
|
|
19
|
+
|
|
20
|
+
# Forced deinit must be blocked (exit 2) by default.
|
|
21
|
+
run_hook "git submodule deinit -f libs/foo"
|
|
22
|
+
assert_exit "forced -f blocks" "$RC" 2
|
|
23
|
+
assert_contains "block message cites #68920" "$OUT" "#68920"
|
|
24
|
+
|
|
25
|
+
run_hook "git submodule deinit --force libs/foo"
|
|
26
|
+
assert_exit "forced --force blocks" "$RC" 2
|
|
27
|
+
|
|
28
|
+
run_hook "git submodule deinit --all -f"
|
|
29
|
+
assert_exit "deinit --all -f blocks" "$RC" 2
|
|
30
|
+
|
|
31
|
+
# Unforced deinit: git's own safety still applies, so warn-only (exit 0).
|
|
32
|
+
run_hook "git submodule deinit libs/foo"
|
|
33
|
+
assert_exit "unforced deinit warns (exit 0)" "$RC" 0
|
|
34
|
+
assert_contains "unforced deinit warns" "$OUT" "WARNING"
|
|
35
|
+
|
|
36
|
+
# submodule rm: warn-only.
|
|
37
|
+
run_hook "git submodule rm libs/foo"
|
|
38
|
+
assert_exit "submodule rm warns (exit 0)" "$RC" 0
|
|
39
|
+
|
|
40
|
+
# Unrelated command: untouched.
|
|
41
|
+
run_hook "git status"
|
|
42
|
+
assert_exit "unrelated passes" "$RC" 0
|
|
43
|
+
|
|
44
|
+
# Opt-out: warn-only mode lets the forced deinit through (exit 0).
|
|
45
|
+
run_hook "git submodule deinit -f libs/foo" SUBMODULE_DEINIT_BLOCK=0
|
|
46
|
+
assert_exit "opt-out warn-only (exit 0)" "$RC" 0
|
|
47
|
+
assert_contains "opt-out still warns" "$OUT" "WARNING"
|
|
48
|
+
|
|
49
|
+
echo "PASS=$PASS FAIL=$FAIL"
|
|
50
|
+
[ "$FAIL" -eq 0 ]
|