cc-safe-setup 29.8.0 → 30.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +72 -19
- package/.claude-plugin/plugin.json +1 -1
- package/CONTRIBUTING.md +7 -2
- package/COOKBOOK.md +50 -46
- package/LICENSE +21 -0
- package/MIGRATION.md +8 -8
- package/README.md +421 -512
- package/SAFETY_CHECKLIST.md +15 -7
- package/SERVICES.md +194 -0
- package/SETTINGS_REFERENCE.md +236 -17
- package/SKILL.md +5 -5
- package/TROUBLESHOOTING.md +35 -14
- package/action.yml +12 -4
- package/audit/README.md +84 -0
- package/audit/audit-checklist.md +152 -0
- package/audit/boundary-selftest.sh +85 -0
- package/audit/boundary.sh +123 -0
- package/audit/count-hooks.py +42 -0
- package/audit/find-dead-hooks.sh +82 -0
- package/audit/find-unfirable-rules.sh +142 -0
- package/audit/fire.sh +97 -0
- package/audit/selftest.sh +42 -0
- package/audit/unfirable-selftest.sh +81 -0
- package/audit-web/index.html +9 -9
- package/examples/README.md +54 -17
- package/examples/account-billing-log.sh +98 -0
- package/examples/account-routing-preflight.sh +123 -0
- package/examples/agent-sdk-credit-pool-monitor.sh +145 -0
- package/examples/agent-view-quota-decrement.sh +32 -0
- package/examples/agent-view-quota-warn.sh +94 -0
- package/examples/agents-md-edit-drift-warner.sh +223 -0
- package/examples/agents-md-loader.sh +160 -0
- package/examples/agents-md-sync-checker.sh +102 -0
- package/examples/ai-slop-punctuation-arrest.sh +210 -0
- package/examples/allowlist.sh +70 -10
- package/examples/always-allow-pattern-suggester.sh +125 -0
- package/examples/ansible-vault-guard.sh +2 -1
- package/examples/anthropic-base-url-guard.sh +76 -0
- package/examples/api-busyloop-guard.sh +123 -0
- package/examples/api-endpoint-guard.sh +12 -0
- package/examples/api-key-in-url-guard.sh +17 -1
- package/examples/api-rate-limit-guard.sh +36 -1
- package/examples/api-rate-limit-tracker.sh +12 -0
- package/examples/articulated-scope-capture.sh +101 -0
- package/examples/askuserquestion-autonomy-gate.sh +102 -0
- package/examples/attachment-traversal-guard.sh +118 -0
- package/examples/aup-block-pattern-logger.sh +161 -0
- package/examples/aup-false-positive-helper.sh +124 -0
- package/examples/aup-large-tool-output-warner.sh +282 -0
- package/examples/aup-retry-loop-guard.sh +229 -0
- package/examples/auth-expiry-reminder.sh +157 -0
- package/examples/auth-macos-sleep-detector.sh +185 -0
- package/examples/auth-path-detector.sh +171 -0
- package/examples/auth-status-checker.sh +157 -0
- package/examples/authorized-reconfirmation-detector.sh +195 -0
- package/examples/auto-approve-build.sh +39 -0
- package/examples/auto-approve-cargo.sh +31 -1
- package/examples/auto-approve-docker.sh +35 -0
- package/examples/auto-approve-git-read.sh +37 -14
- package/examples/auto-approve-go.sh +30 -1
- package/examples/auto-approve-gradle.sh +29 -1
- package/examples/auto-approve-make.sh +29 -1
- package/examples/auto-approve-maven.sh +29 -1
- package/examples/auto-approve-python.sh +41 -1
- package/examples/auto-approve-readonly-tools.sh +1 -0
- package/examples/auto-approve-readonly.sh +110 -33
- package/examples/auto-approve-ssh.sh +30 -1
- package/examples/auto-approve-test.sh +44 -0
- package/examples/auto-checkpoint.sh +1 -1
- package/examples/auto-compact-prep.sh +0 -0
- package/examples/auto-git-checkpoint.sh +0 -0
- package/examples/auto-mode-safe-commands.sh +53 -1
- package/examples/auto-mode-safety-enforcer.sh +151 -4
- package/examples/auto-push-session-branch.sh +22 -0
- package/examples/aws-production-guard.sh +13 -1
- package/examples/aws-region-guard.sh +1 -1
- package/examples/background-cost-launch-guard.sh +82 -0
- package/examples/background-task-guard.sh +12 -0
- package/examples/backup-before-refactor.sh +0 -0
- package/examples/banned-command-guard.sh +12 -0
- package/examples/bash-allowlist-secondary-check.sh +157 -0
- package/examples/bash-domain-allowlist.sh +12 -0
- package/examples/bash-fanout-bounded-rewriter.sh +191 -0
- package/examples/bash-heuristic-approver.sh +112 -10
- package/examples/bash-project-fence.sh +194 -0
- package/examples/bash-safety-auto-deny.sh +12 -0
- package/examples/bash-secret-output-detector.sh +44 -3
- package/examples/bash-trace-guard.sh +12 -0
- package/examples/bashrc-safety-check.sh +0 -0
- package/examples/bg-permission-prompt-warner.sh +182 -0
- package/examples/bg-task-cooldown-guard.sh +12 -0
- package/examples/binary-read-detector.sh +135 -0
- package/examples/binary-upload-guard.sh +12 -0
- package/examples/block-database-wipe.sh +35 -10
- package/examples/branch-name-check.sh +8 -3
- package/examples/branch-naming-convention.sh +0 -0
- package/examples/broad-find-guard.sh +12 -0
- package/examples/broad-prefix-session-trap-warner.sh +106 -0
- package/examples/bulk-file-delete-guard.sh +45 -0
- package/examples/bypass-mode-effective-verifier.sh +203 -0
- package/examples/cache-creation-drift-detector.sh +120 -0
- package/examples/cache-residue-detector.sh +195 -0
- package/examples/cache-tier-logger.sh +70 -0
- package/examples/cache-ttl-eviction-detector.sh +149 -0
- package/examples/cargo-publish-guard.sh +14 -1
- package/examples/case-insensitive-path-guard.sh +21 -2
- package/examples/case-sensitive-guard.sh +23 -2
- package/examples/cch-cache-guard.sh +0 -0
- package/examples/cch-sentinel-precommit-guard.sh +47 -0
- package/examples/check-abort-controller.sh +1 -1
- package/examples/check-accessibility.sh +0 -0
- package/examples/check-aria-labels.sh +0 -0
- package/examples/check-before-act-enforcer.sh +12 -0
- package/examples/check-charset-meta.sh +0 -0
- package/examples/check-cookie-flags.sh +0 -0
- package/examples/check-cors-config.sh +0 -0
- package/examples/check-csp-headers.sh +0 -0
- package/examples/check-csrf-protection.sh +0 -0
- package/examples/check-debounce.sh +1 -1
- package/examples/check-dependency-age.sh +0 -0
- package/examples/check-dependency-license.sh +0 -0
- package/examples/check-dockerfile-best-practice.sh +0 -0
- package/examples/check-error-boundaries.sh +0 -0
- package/examples/check-error-class.sh +1 -1
- package/examples/check-error-handling.sh +0 -0
- package/examples/check-error-logging.sh +1 -1
- package/examples/check-error-message.sh +0 -0
- package/examples/check-error-stack.sh +0 -0
- package/examples/check-favicon.sh +0 -0
- package/examples/check-git-hooks-compat.sh +0 -0
- package/examples/check-gitattributes.sh +0 -0
- package/examples/check-https-redirect.sh +0 -0
- package/examples/check-input-validation.sh +0 -0
- package/examples/check-lang-attribute.sh +0 -0
- package/examples/check-npm-scripts-exist.sh +0 -0
- package/examples/check-package-size.sh +0 -0
- package/examples/check-port-availability.sh +0 -0
- package/examples/check-promise-all.sh +1 -1
- package/examples/check-rate-limiting.sh +0 -0
- package/examples/check-return-types.sh +0 -0
- package/examples/check-semantic-versioning.sh +0 -0
- package/examples/check-suspense-fallback.sh +1 -1
- package/examples/check-test-exists.sh +0 -0
- package/examples/check-test-naming.sh +0 -0
- package/examples/check-timeout-cleanup.sh +1 -1
- package/examples/check-tls-version.sh +0 -0
- package/examples/check-unsubscribe.sh +1 -1
- package/examples/check-viewport-meta.sh +0 -0
- package/examples/check-worker-terminate.sh +1 -1
- package/examples/checkpoint-tamper-guard.sh +12 -0
- package/examples/chmod-guard.sh +12 -0
- package/examples/chown-guard.sh +24 -0
- package/examples/ci-workflow-guard.sh +6 -1
- package/examples/claim-verify-detector.sh +109 -0
- package/examples/claim-vs-caveat-checker.sh +77 -0
- package/examples/classifier-fallback-allow.sh +114 -40
- package/examples/claude-cache-gc.sh +1 -0
- package/examples/claude-code-version-pin-advisor.sh +183 -0
- package/examples/claude-md-reinjector.sh +129 -0
- package/examples/claude-md-size-monitor.sh +66 -0
- package/examples/claude-update-budget-guard.sh +123 -0
- package/examples/claude-update-smart.sh +113 -0
- package/examples/claudeignore-enforce-guard.sh +12 -0
- package/examples/claudemd-enforcer.sh +0 -0
- package/examples/claudemd-tool-prohibition.sh +128 -0
- package/examples/clear-command-confirm-guard.sh +32 -15
- package/examples/cli-config-pinning-detector.sh +162 -0
- package/examples/cliff-countdown-advisor.sh +96 -0
- package/examples/clipboard-secret-guard.sh +12 -0
- package/examples/closure-word-verify-gate.sh +150 -0
- package/examples/cloud-cli-guard.sh +39 -2
- package/examples/commit-all-scope-guard.sh +44 -0
- package/examples/commit-message-check.sh +1 -1
- package/examples/commit-message-quality.sh +6 -1
- package/examples/commitment-carry-forward-arrest.sh +361 -0
- package/examples/compact-alert-notification.sh +0 -0
- package/examples/compact-dispatch-watchdog.sh +149 -0
- package/examples/completion-claim-without-verification-detector.sh +207 -0
- package/examples/composer-guard.sh +12 -0
- package/examples/compound-bash-permission-resolver.sh +156 -0
- package/examples/compound-command-allow.sh +1 -1
- package/examples/compound-command-deny-enforcer.sh +183 -0
- package/examples/compound-inject-guard.sh +12 -0
- package/examples/concurrent-edit-lock.sh +12 -0
- package/examples/conflict-marker-guard.sh +12 -0
- package/examples/consecutive-error-breaker.sh +0 -0
- package/examples/console-log-count.sh +1 -0
- package/examples/context-compact-advisor.sh +29 -2
- package/examples/context-length-budget.sh +69 -0
- package/examples/context-threshold-alert.sh +12 -0
- package/examples/context-usage-drift-alert.sh +41 -13
- package/examples/conversation-history-guard.sh +12 -0
- package/examples/core-file-protect-guard.sh +12 -0
- package/examples/cors-star-warn.sh +0 -0
- package/examples/cost-incident-self-audit.sh +162 -0
- package/examples/cowork-claude-md-load-checker.sh +100 -0
- package/examples/cowork-fuse-staleness-watcher.sh +140 -0
- package/examples/cowork-hook-absence-warner.sh +167 -0
- package/examples/cowork-model-picker-advisor.sh +115 -0
- package/examples/credential-exfil-guard.sh +65 -4
- package/examples/credential-file-cat-guard.sh +17 -1
- package/examples/cron-create-receipt.sh +112 -0
- package/examples/cron-modification-guard.sh +12 -0
- package/examples/cross-product-mcp-spawn-detector.sh +178 -0
- package/examples/cwd-project-boundary-guard.sh +0 -0
- package/examples/daemon-zombie-worker-surfacer.sh +118 -0
- package/examples/daily-cost-guard.sh +164 -0
- package/examples/daily-usage-tracker.sh +0 -0
- package/examples/dangerous-pip-flag-guard.sh +12 -0
- package/examples/db-connect-guard.sh +13 -1
- package/examples/denied-action-retry-guard.sh +12 -0
- package/examples/deny-bypass-detector.sh +12 -0
- package/examples/deny-rule-integrity-verifier.sh +181 -0
- package/examples/dependency-install-guard.sh +12 -0
- package/examples/deploy-guard.sh +14 -2
- package/examples/deploy-path-verify-guard.sh +12 -0
- package/examples/deployment-readback-gate.sh +135 -0
- package/examples/deployment-readback-gh-adapter.sh +247 -0
- package/examples/deployment-verify-guard.sh +25 -2
- package/examples/destructive-db-script-write-guard.sh +166 -0
- package/examples/destructive-migration-write-guard.sh +163 -0
- package/examples/diff-size-guard.sh +12 -0
- package/examples/disabled-feature-toggle-advisor.sh +88 -0
- package/examples/disk-partition-guard.sh +12 -0
- package/examples/dispatch-allowlist-preflight.sh +301 -0
- package/examples/dispatch-liveness-watchdog.sh +216 -0
- package/examples/dispatch-receipt.sh +174 -0
- package/examples/django-migrate-guard.sh +12 -0
- package/examples/dns-config-guard.sh +12 -0
- package/examples/docker-dangerous-guard.sh +12 -0
- package/examples/docker-volume-guard.sh +0 -0
- package/examples/dockerfile-latest-guard.sh +1 -0
- package/examples/dotenv-anthropic-key-billing-guard.sh +99 -0
- package/examples/dotenv-commit-guard.sh +12 -0
- package/examples/dotenv-read-guard.sh +12 -0
- package/examples/dotfile-protection-guard.sh +12 -0
- package/examples/dotnet-build-on-edit.sh +1 -1
- package/examples/drizzle-migrate-guard.sh +27 -2
- package/examples/echo-probe-spam-detector.sh +176 -0
- package/examples/edit-counter-test-gate.sh +12 -0
- package/examples/edit-error-counter.sh +1 -1
- package/examples/edit-guard.sh +12 -0
- package/examples/edit-old-string-validator.sh +12 -0
- package/examples/edit-quote-ambiguity-guard.sh +97 -0
- package/examples/edit-retry-loop-guard.sh +0 -0
- package/examples/edit-verify.sh +0 -0
- package/examples/env-inline-secret-guard.sh +14 -2
- package/examples/env-naming-convention.sh +0 -0
- package/examples/env-prod-guard.sh +0 -0
- package/examples/env-required-check.sh +0 -0
- package/examples/env-source-guard.sh +12 -0
- package/examples/env-var-check.sh +24 -1
- package/examples/ephemeral-container-detector.sh +147 -0
- package/examples/error-memory-guard.sh +12 -0
- package/examples/evidence-claim-gate.sh +187 -0
- package/examples/exploration-budget-guard.sh +12 -0
- package/examples/expo-eject-guard.sh +13 -0
- package/examples/extended-thinking-loop-guard.sh +139 -0
- package/examples/extended-thinking-resume-warning.sh +119 -0
- package/examples/extended-thinking-tool-use-mismatch-detector.sh +212 -0
- package/examples/fabricated-command-detector.sh +145 -0
- package/examples/false-disk-full-guard.sh +63 -0
- package/examples/feature-deprecation-detector.sh +109 -0
- package/examples/file-change-tracker.sh +1 -1
- package/examples/file-change-undo-tracker.sh +0 -0
- package/examples/file-edit-backup.sh +63 -14
- package/examples/file-reference-check.sh +0 -0
- package/examples/file-size-limit.sh +12 -0
- package/examples/financial-operation-guard.sh +12 -0
- package/examples/firewall-guard.sh +12 -0
- package/examples/fish-shell-wrapper.sh +125 -7
- package/examples/five-hundred-milestone.sh +1 -0
- package/examples/flask-static-route-guard.sh +89 -0
- package/examples/forbidden-response-phrase-detector.sh +161 -0
- package/examples/gem-push-guard.sh +13 -0
- package/examples/gh-cli-destructive-guard.sh +13 -1
- package/examples/git-checkout-safety-guard.sh +12 -0
- package/examples/git-checkout-uncommitted-guard.sh +12 -0
- package/examples/git-config-guard.sh +12 -0
- package/examples/git-crypt-worktree-guard.sh +12 -0
- package/examples/git-filter-repo-guard.sh +56 -0
- package/examples/git-history-rewrite-guard.sh +12 -0
- package/examples/git-hook-bypass-guard.sh +0 -0
- package/examples/git-maintenance-guard.sh +83 -0
- package/examples/git-merge-conflict-prevent.sh +0 -0
- package/examples/git-message-length-check.sh +0 -0
- package/examples/git-message-length.sh +0 -0
- package/examples/git-operations-require-approval.sh +68 -14
- package/examples/git-pack-temp-cleanup-guard.sh +102 -0
- package/examples/git-push-blast-radius-guard.sh +218 -0
- package/examples/git-remote-guard.sh +24 -3
- package/examples/git-rm-orphan-wipe-guard.sh +85 -0
- package/examples/git-stash-before-checkout.sh +12 -0
- package/examples/git-stash-before-danger.sh +0 -0
- package/examples/git-submodule-guard.sh +69 -2
- package/examples/git-tag-guard.sh +12 -0
- package/examples/github-actions-secret-guard.sh +0 -0
- package/examples/gitignore-check.sh +0 -0
- package/examples/gitops-drift-guard.sh +12 -0
- package/examples/go-mod-tidy-warn.sh +1 -0
- package/examples/go-vet-after-edit.sh +12 -0
- package/examples/goal-iteration-limit-warner.sh +152 -0
- package/examples/grep-nul-false-negative-guard.sh +30 -0
- package/examples/growthbook-flag-monitor.sh +257 -0
- package/examples/hallucination-url-check.sh +1 -1
- package/examples/halt-signal-detector.sh +135 -0
- package/examples/handoff-on-clear.sh +65 -0
- package/examples/hardcoded-ip-guard.sh +1 -0
- package/examples/headless-empty-result-guard.sh +0 -0
- package/examples/headless-stop-guard.sh +0 -0
- package/examples/helm-install-guard.sh +2 -1
- package/examples/home-critical-bash-guard.sh +17 -1
- package/examples/hook-debug-wrapper.sh +6 -2
- package/examples/hook-permission-fixer.sh +0 -0
- package/examples/hook-self-disable-detector.sh +99 -0
- package/examples/hook-stdout-sanitizer.sh +6 -2
- package/examples/hook-tamper-guard.sh +12 -0
- package/examples/idle-session-cost-alert.sh +0 -0
- package/examples/image-dimension-guard.sh +109 -0
- package/examples/image-file-validator.sh +0 -0
- package/examples/iteration-quality-degradation-detector.sh +170 -0
- package/examples/java-compile-on-edit.sh +1 -1
- package/examples/json-syntax-check.sh +0 -0
- package/examples/k8s-production-guard.sh +13 -1
- package/examples/kill-process-guard.sh +12 -0
- package/examples/kubernetes-guard.sh +13 -1
- package/examples/laravel-artisan-guard.sh +13 -0
- package/examples/large-file-guard.sh +1 -1
- package/examples/log-level-guard.sh +0 -0
- package/examples/log-truncation-guard.sh +12 -0
- package/examples/long-session-malformed-tool-call-detector.sh +183 -0
- package/examples/loop-detector.sh +12 -0
- package/examples/magic-number-warn.sh +1 -0
- package/examples/main-branch-warn.sh +12 -0
- package/examples/markdown-link-check.sh +0 -0
- package/examples/max-concurrent-agents.sh +12 -0
- package/examples/max-edit-size-guard.sh +13 -0
- package/examples/max-file-count-guard.sh +0 -0
- package/examples/max-file-delete-count.sh +0 -0
- package/examples/max-function-length.sh +1 -0
- package/examples/max-import-count.sh +0 -0
- package/examples/max-subagent-count.sh +0 -0
- package/examples/mcp-config-freeze.sh +12 -0
- package/examples/mcp-config-poisoning-audit.sh +150 -0
- package/examples/mcp-data-boundary.sh +1 -1
- package/examples/mcp-misdiagnosis-arrest.sh +209 -0
- package/examples/mcp-orphan-leak-warner.sh +49 -0
- package/examples/mcp-permissions-ask-warner.sh +151 -0
- package/examples/mcp-routine-approval-detector.sh +154 -0
- package/examples/mcp-server-allowlist.sh +12 -0
- package/examples/mcp-server-guard.sh +12 -0
- package/examples/mcp-startup-bloat-detector.sh +87 -0
- package/examples/mcp-stdio-compatibility-test.sh +153 -0
- package/examples/mcp-tool-audit-log.sh +0 -0
- package/examples/mcp-tool-guard.sh +101 -1
- package/examples/mcp-warmup-wait.sh +0 -0
- package/examples/memory-chain-audit.sh +56 -0
- package/examples/memory-orphan-detector.sh +188 -0
- package/examples/memory-write-guard.sh +99 -17
- package/examples/migration-verify-guard.sh +12 -0
- package/examples/model-swap-suggester.sh +177 -0
- package/examples/model-version-alert.sh +5 -1
- package/examples/model-version-lock.sh +63 -0
- package/examples/move-delete-sequence-guard.sh +33 -2
- package/examples/multi-file-plan-routing-gate.sh +192 -0
- package/examples/multi-vendor-concurrent-warner.sh +108 -0
- package/examples/multi-window-auth-drift-detector.sh +183 -0
- package/examples/multiline-command-approver.sh +161 -23
- package/examples/nested-background-agent-guard.sh +109 -0
- package/examples/nested-spawn-inflight-guard.sh +178 -0
- package/examples/network-exfil-guard.sh +12 -0
- package/examples/network-guard.sh +12 -0
- package/examples/network-interface-guard.sh +12 -0
- package/examples/nextjs-env-guard.sh +1 -1
- package/examples/no-absolute-import.sh +0 -0
- package/examples/no-alert-confirm-prompt.sh +0 -0
- package/examples/no-anonymous-default-export.sh +0 -0
- package/examples/no-any-type.sh +0 -0
- package/examples/no-any-typescript.sh +1 -0
- package/examples/no-ask-human.sh +12 -0
- package/examples/no-assignment-in-condition.sh +0 -0
- package/examples/no-base64-exfil.sh +12 -0
- package/examples/no-callback-hell.sh +0 -0
- package/examples/no-circular-dependency.sh +0 -0
- package/examples/no-cleartext-storage.sh +0 -0
- package/examples/no-commented-code.sh +0 -0
- package/examples/no-commit-fixup.sh +0 -0
- package/examples/no-console-assert.sh +0 -0
- package/examples/no-console-error-swallow.sh +0 -0
- package/examples/no-console-in-prod.sh +0 -0
- package/examples/no-console-log-commit.sh +12 -0
- package/examples/no-console-log.sh +0 -0
- package/examples/no-console-time.sh +0 -0
- package/examples/no-cors-wildcard.sh +4 -1
- package/examples/no-curl-upload.sh +0 -0
- package/examples/no-dangerouslySetInnerHTML.sh +0 -0
- package/examples/no-dangling-await.sh +1 -0
- package/examples/no-debug-commit.sh +12 -0
- package/examples/no-debug-in-commit.sh +0 -0
- package/examples/no-deep-nesting.sh +0 -0
- package/examples/no-deep-relative-import.sh +1 -0
- package/examples/no-default-credentials.sh +0 -0
- package/examples/no-deploy-friday.sh +13 -1
- package/examples/no-disabled-test.sh +0 -0
- package/examples/no-document-write.sh +0 -0
- package/examples/no-empty-function.sh +0 -0
- package/examples/no-eval-in-template.sh +0 -0
- package/examples/no-eval-template.sh +3 -1
- package/examples/no-eval.sh +0 -0
- package/examples/no-exec-user-input.sh +0 -0
- package/examples/no-fixme-ship.sh +12 -0
- package/examples/no-floating-promises.sh +0 -0
- package/examples/no-force-flag.sh +22 -5
- package/examples/no-force-install.sh +0 -0
- package/examples/no-git-amend.sh +12 -0
- package/examples/no-git-rebase-public.sh +0 -0
- package/examples/no-global-install.sh +12 -0
- package/examples/no-global-state.sh +0 -0
- package/examples/no-hardcoded-port.sh +1 -0
- package/examples/no-hardcoded-url.sh +0 -0
- package/examples/no-helmet-missing.sh +0 -0
- package/examples/no-http-url.sh +1 -0
- package/examples/no-http-without-https.sh +0 -0
- package/examples/no-inline-style.sh +0 -0
- package/examples/no-inline-styles.sh +1 -0
- package/examples/no-innerhtml.sh +0 -0
- package/examples/no-install-global.sh +12 -0
- package/examples/no-jwt-in-url.sh +0 -0
- package/examples/no-large-commit.sh +0 -0
- package/examples/no-localhost-expose.sh +0 -0
- package/examples/no-magic-number.sh +0 -0
- package/examples/no-md5-sha1.sh +0 -0
- package/examples/no-mixed-line-endings.sh +8 -1
- package/examples/no-mutation-in-reducer.sh +0 -0
- package/examples/no-nested-ternary.sh +0 -0
- package/examples/no-network-exfil.sh +0 -0
- package/examples/no-open-redirect.sh +0 -0
- package/examples/no-output-truncation.sh +12 -0
- package/examples/no-package-downgrade.sh +0 -0
- package/examples/no-package-lock-edit.sh +12 -0
- package/examples/no-path-join-user-input.sh +0 -0
- package/examples/no-port-bind.sh +0 -0
- package/examples/no-process-exit.sh +0 -0
- package/examples/no-prototype-pollution.sh +0 -0
- package/examples/no-push-without-ci.sh +0 -0
- package/examples/no-push-without-tests.sh +12 -0
- package/examples/no-raw-password-in-url.sh +0 -0
- package/examples/no-root-user-docker.sh +1 -0
- package/examples/no-root-write.sh +12 -0
- package/examples/no-secrets-in-args.sh +4 -1
- package/examples/no-secrets-in-logs.sh +0 -0
- package/examples/no-sensitive-log.sh +0 -0
- package/examples/no-sleep-in-hooks.sh +0 -0
- package/examples/no-star-import-python.sh +1 -0
- package/examples/no-string-concat-sql.sh +0 -0
- package/examples/no-sudo-guard.sh +12 -0
- package/examples/no-sync-fs.sh +0 -0
- package/examples/no-todo-in-merge.sh +0 -0
- package/examples/no-todo-in-production.sh +1 -0
- package/examples/no-todo-without-issue.sh +0 -0
- package/examples/no-unused-import.sh +0 -0
- package/examples/no-var-keyword.sh +0 -0
- package/examples/no-verify-blocker.sh +12 -0
- package/examples/no-wget-piped-bash.sh +12 -0
- package/examples/no-wildcard-cors.sh +0 -0
- package/examples/no-wildcard-delete.sh +0 -0
- package/examples/no-wildcard-import.sh +0 -0
- package/examples/no-with-statement.sh +0 -0
- package/examples/no-write-outside-src.sh +0 -0
- package/examples/no-xml-external-entity.sh +0 -0
- package/examples/non-english-quality-warner.sh +156 -0
- package/examples/notify-waiting.sh +1 -1
- package/examples/npm-audit-warn.sh +0 -0
- package/examples/npm-global-install-guard.sh +12 -0
- package/examples/npm-publish-guard.sh +12 -0
- package/examples/npm-script-injection.sh +0 -0
- package/examples/npm-supply-chain-guard.sh +0 -0
- package/examples/nuxt-config-guard.sh +1 -0
- package/examples/oauth-refresh-monitor.sh +210 -0
- package/examples/opus-degradation-tracker.sh +187 -0
- package/examples/opus48-routine-task-warning.sh +196 -0
- package/examples/opus48-thinking-wedge-advisor.sh +143 -0
- package/examples/output-credential-scan.sh +1 -1
- package/examples/output-secret-mask.sh +31 -9
- package/examples/output-token-spike-detector.sh +145 -0
- package/examples/package-json-guard.sh +12 -0
- package/examples/package-lock-frozen.sh +12 -0
- package/examples/parallel-batch-size-limiter.sh +121 -0
- package/examples/parallel-cascade-detector.sh +116 -0
- package/examples/partial-view-claim-arrest.sh +174 -0
- package/examples/path-deny-bash-guard.sh +12 -0
- package/examples/path-traversal-guard.sh +12 -0
- package/examples/per-prompt-token-cap.sh +124 -0
- package/examples/permission-audit-log.sh +1 -1
- package/examples/permission-denial-enforcer.sh +12 -0
- package/examples/permission-entry-validator.sh +0 -0
- package/examples/persisted-1m-model-advisor.sh +88 -0
- package/examples/php-lint-on-edit.sh +1 -1
- package/examples/pii-upload-guard.sh +12 -0
- package/examples/pip-publish-guard.sh +13 -0
- package/examples/pip-requirements-guard.sh +12 -0
- package/examples/pip-venv-required.sh +12 -0
- package/examples/plan-mode-edit-guard.sh +12 -0
- package/examples/plan-mode-enforcer.sh +12 -0
- package/examples/plan-mode-strict-guard.sh +12 -0
- package/examples/plugin-hooks-json-bloat-detector.sh +169 -0
- package/examples/plugin-process-cleanup.sh +0 -0
- package/examples/polyglot-rm-guard.sh +12 -0
- package/examples/post-compact-safety.sh +12 -0
- package/examples/post-edit-disk-verify.sh +152 -0
- package/examples/powershell-remove-item-guard.sh +85 -0
- package/examples/pr-description-check.sh +0 -0
- package/examples/pr-duplicate-guard.sh +13 -0
- package/examples/pre-bash-sed-line-ending-windows.sh +87 -0
- package/examples/pre-compact-knowledge-save.sh +0 -0
- package/examples/pre-compact-transcript-backup.sh +0 -0
- package/examples/pre-execution-claim-detector.sh +179 -0
- package/examples/prefer-builtin-tools.sh +10 -1
- package/examples/prefer-const.sh +0 -0
- package/examples/prefer-dedicated-tools.sh +12 -0
- package/examples/prefer-optional-chaining.sh +0 -0
- package/examples/prisma-migrate-guard.sh +24 -7
- package/examples/production-port-kill-guard.sh +12 -0
- package/examples/prompt-injection-guard.sh +2 -1
- package/examples/prompt-usage-logger.sh +0 -0
- package/examples/protect-claudemd.sh +12 -0
- package/examples/protect-dotfiles.sh +12 -0
- package/examples/proxy-capture-suggester.sh +159 -0
- package/examples/public-artefact-socratic-narrowing.sh +232 -0
- package/examples/public-repo-push-guard.sh +12 -0
- package/examples/push-requires-test-pass-record.sh +0 -0
- package/examples/push-requires-test-pass.sh +12 -0
- package/examples/quota-anomaly-detector.sh +173 -0
- package/examples/quota-reset-audit.sh +58 -0
- package/examples/quoted-flag-approver.sh +99 -10
- package/examples/rails-migration-guard.sh +12 -0
- package/examples/rate-limit-guard.sh +0 -0
- package/examples/read-all-files-enforcer.sh +0 -0
- package/examples/read-budget-guard.sh +12 -0
- package/examples/read-loop-detector.sh +59 -0
- package/examples/read-once-guard.sh +96 -0
- package/examples/read-only-mode.sh +12 -0
- package/examples/readme-exists-check.sh +0 -0
- package/examples/record-read-coverage.sh +73 -0
- package/examples/redirect-fragment-warner.sh +149 -0
- package/examples/redis-flushall-guard.sh +13 -0
- package/examples/redundant-read-blocker.sh +132 -0
- package/examples/refusal-arrest-gate.sh +248 -0
- package/examples/registry-publish-guard.sh +13 -1
- package/examples/remote-control-billing-classifier.sh +135 -0
- package/examples/replace-all-guard.sh +12 -0
- package/examples/repo-visibility-guard.sh +12 -0
- package/examples/reroute-after-block-guard.sh +150 -0
- package/examples/response-budget-guard.sh +0 -0
- package/examples/resume-drift-watcher.sh +57 -0
- package/examples/rg-replace-flag-detector.sh +125 -0
- package/examples/rhetorical-verification-prompt-detector.sh +102 -0
- package/examples/ripgrep-permission-fix.sh +0 -0
- package/examples/rm-safety-net.sh +151 -8
- package/examples/role-tool-guard.sh +12 -0
- package/examples/route-handler-emptiness-gate.sh +324 -0
- package/examples/ruby-lint-on-edit.sh +1 -1
- package/examples/runtime-binary-change-detector.sh +102 -0
- package/examples/same-command-repeat-detector.sh +196 -0
- package/examples/same-correction-arrest.sh +169 -0
- package/examples/sandbox-denywrite-enforcer.sh +78 -0
- package/examples/sandbox-relative-path-audit.sh +83 -25
- package/examples/sandbox-write-verify.sh +12 -0
- package/examples/schema-migration-guard.sh +0 -0
- package/examples/scope-expansion-receipt.sh +178 -0
- package/examples/scope-guard.sh +88 -9
- package/examples/secret-file-read-guard.sh +12 -0
- package/examples/secret-file-write-guard.sh +150 -0
- package/examples/self-modify-bypass-guard.sh +0 -0
- package/examples/sensitive-file-read-guard.sh +23 -3
- package/examples/sensitive-log-guard.sh +1 -0
- package/examples/server-side-prompt-injection-detector.sh +69 -0
- package/examples/session-agent-cost-limiter.sh +12 -0
- package/examples/session-backup-on-start.sh +1 -1
- package/examples/session-cost-alert.sh +12 -0
- package/examples/session-drift-guard.sh +12 -0
- package/examples/session-duration-guard.sh +25 -2
- package/examples/session-end-logger.sh +0 -0
- package/examples/session-health-monitor.sh +0 -0
- package/examples/session-index-repair.sh +1 -1
- package/examples/session-memory-watchdog.sh +1 -0
- package/examples/session-permission-reset-guard.sh +12 -0
- package/examples/session-persistence-verifier.sh +93 -0
- package/examples/session-plugin-pin-watch.sh +68 -0
- package/examples/session-rate-monitor.sh +176 -0
- package/examples/session-resume-env-fix.sh +0 -0
- package/examples/session-start-quota-status.sh +193 -0
- package/examples/session-start-safety-check.sh +55 -3
- package/examples/session-state-saver.sh +0 -0
- package/examples/session-summary-stop.sh +1 -1
- package/examples/session-summary.sh +0 -0
- package/examples/session-token-counter.sh +1 -1
- package/examples/settings-hooks-key-restorer.sh +99 -0
- package/examples/settings-json-health-check.sh +57 -0
- package/examples/settings-json-model-guard.sh +0 -0
- package/examples/settings-regression-tester.sh +188 -0
- package/examples/shell-config-truncation-guard.sh +12 -0
- package/examples/shell-wrapper-guard.sh +12 -0
- package/examples/skill-cumulative-size-detector.sh +104 -0
- package/examples/skill-description-drop-detector.sh +148 -0
- package/examples/skill-gate.sh +0 -0
- package/examples/skill-truncation-detector.sh +154 -0
- package/examples/skills-context-recorder.sh +167 -0
- package/examples/skills-load-verifier.sh +96 -0
- package/examples/skills-settings-validator.sh +139 -0
- package/examples/sonnet-45-deprecation-helper.sh +85 -0
- package/examples/spec-file-scope-guard.sh +12 -0
- package/examples/spring-profile-guard.sh +1 -0
- package/examples/spurious-malformed-notice-detector.sh +247 -0
- package/examples/sql-bulk-delete-warn.sh +88 -0
- package/examples/sql-injection-detect.sh +0 -0
- package/examples/ssh-key-protect.sh +12 -0
- package/examples/staged-secret-scan.sh +22 -1
- package/examples/stale-temp-settings-detector.sh +86 -0
- package/examples/standing-rule-session-surfacer.sh +137 -0
- package/examples/stop-hook-sigterm-wrapper.sh +182 -0
- package/examples/stop-transcript-write-health.sh +64 -0
- package/examples/strict-allowlist.sh +57 -15
- package/examples/strip-coauthored-by.sh +50 -10
- package/examples/subagent-blast-radius-guard.sh +184 -0
- package/examples/subagent-boundary-precheck.sh +126 -0
- package/examples/subagent-budget-guard.sh +12 -0
- package/examples/subagent-claudemd-inject.sh +0 -0
- package/examples/subagent-closure-verify-gate.sh +164 -0
- package/examples/subagent-destructive-git-guard.sh +120 -0
- package/examples/subagent-error-detector.sh +0 -0
- package/examples/subagent-forged-system-reminder-guard.sh +156 -0
- package/examples/subagent-identity-leak-guard.sh +102 -0
- package/examples/subagent-identity-reassertion.sh +51 -0
- package/examples/subagent-inheritance-tester.sh +179 -0
- package/examples/subagent-permission-mode-guard.sh +110 -0
- package/examples/subagent-scope-guard.sh +12 -0
- package/examples/subagent-spawn-verification-enforcer.sh +102 -0
- package/examples/subagent-tool-allowlist-enforcer.sh +101 -0
- package/examples/subagent-tool-call-limiter.sh +87 -15
- package/examples/subcommand-chain-guard.sh +12 -0
- package/examples/subscription-api-billing-warner.sh +160 -0
- package/examples/subscription-bypass-detector.sh +152 -0
- package/examples/svelte-lint-on-edit.sh +1 -0
- package/examples/swift-build-on-edit.sh +1 -1
- package/examples/symlink-guard.sh +27 -2
- package/examples/symlink-protect.sh +1 -0
- package/examples/system-dir-protection-guard.sh +73 -8
- package/examples/system-message-workaround.sh +38 -13
- package/examples/system-package-guard.sh +12 -0
- package/examples/systemd-service-guard.sh +12 -0
- package/examples/task-integrity-guard.sh +12 -0
- package/examples/temp-file-cleanup.sh +1 -1
- package/examples/temporal-suggestion-detector.sh +135 -0
- package/examples/terraform-guard.sh +27 -6
- package/examples/test-after-edit.sh +1 -1
- package/examples/test-before-push.sh +12 -0
- package/examples/test-coverage-reminder.sh +24 -1
- package/examples/thinking-budget-effort-mismatch-detector.sh +149 -0
- package/examples/timezone-guard.sh +0 -0
- package/examples/tmp-output-size-guard.sh +0 -0
- package/examples/todo-check.sh +1 -1
- package/examples/token-budget-guard.sh +12 -0
- package/examples/token-budget-per-task.sh +12 -0
- package/examples/token-usage-tracker.sh +7 -1
- package/examples/tokenizer-ratio-alert-corpus.sh +90 -0
- package/examples/tokenizer-ratio-alert.sh +104 -0
- package/examples/tool-call-rate-limiter.sh +0 -0
- package/examples/tool-result-correlation-checker.sh +167 -0
- package/examples/tool-result-size-guard.sh +132 -0
- package/examples/tool-retry-budget-guard.sh +12 -0
- package/examples/transcript-contamination-detector.sh +138 -0
- package/examples/trustfall-mcp-injection-guard.sh +103 -0
- package/examples/ultrareview-large-diff-advisor.sh +184 -0
- package/examples/unbounded-output-guard.sh +94 -0
- package/examples/uncommitted-changes-stop.sh +1 -0
- package/examples/uncommitted-discard-guard.sh +23 -5
- package/examples/uncommitted-work-guard.sh +12 -0
- package/examples/usage-warn.sh +0 -0
- package/examples/user-account-guard.sh +12 -0
- package/examples/userprompt-submit-receipt.sh +123 -0
- package/examples/variable-expansion-guard.sh +12 -0
- package/examples/verify-after-publish-reminder.sh +93 -0
- package/examples/verify-before-commit.sh +12 -0
- package/examples/version-bump-detector.sh +156 -0
- package/examples/version-regression-warner.sh +144 -0
- package/examples/vue-lint-on-edit.sh +1 -0
- package/examples/warn-cron-cost-trap.sh +173 -0
- package/examples/webfetch-runaway-guard.sh +89 -0
- package/examples/wellness-break-reminder.sh +118 -0
- package/examples/windows-destructive-command-guard.sh +177 -0
- package/examples/windows-path-guard.sh +12 -0
- package/examples/windows-python-stub-detector.sh +63 -0
- package/examples/work-hours-guard.sh +12 -0
- package/examples/working-directory-fence.sh +12 -0
- package/examples/workspace-lease-guard.sh +205 -0
- package/examples/worktree-create-log.sh +1 -0
- package/examples/worktree-delete-guard.sh +12 -0
- package/examples/worktree-edit-boundary-guard.sh +127 -0
- package/examples/worktree-escape-write-guard.sh +114 -0
- package/examples/worktree-hook-linker.sh +0 -0
- package/examples/worktree-hooks-path-fix.sh +111 -0
- package/examples/worktree-parent-write-guard.sh +43 -0
- package/examples/worktree-project-unify.sh +2 -1
- package/examples/worktree-remove-uncommitted-guard.sh +13 -0
- package/examples/worktree-unmerged-guard.sh +7 -1
- package/examples/write-byte-integrity-verifier.sh +190 -0
- package/examples/write-empty-content-guard.sh +67 -0
- package/examples/write-nul-corruption-detector.sh +61 -0
- package/examples/write-overwrite-confirm.sh +160 -14
- package/examples/write-secret-guard.sh +27 -5
- package/examples/write-shrink-guard.sh +12 -0
- package/examples/write-test-ratio.sh +0 -0
- package/examples/wsl-host-disk-space-guard.sh +97 -0
- package/examples/xml-format-leak-detector.sh +266 -0
- package/examples/yaml-syntax-check.sh +12 -0
- package/hooks/hooks.json +6 -6
- package/index.mjs +1718 -183
- package/mission.md +2 -0
- package/package.json +4 -4
- package/plugins/credential-guard/.claude-plugin/plugin.json +9 -7
- package/plugins/git-protection/.claude-plugin/plugin.json +10 -8
- package/plugins/safety-essentials/.claude-plugin/plugin.json +9 -7
- package/plugins/token-guard/.claude-plugin/plugin.json +10 -8
- package/scripts/agents-md-sync-setup.sh +156 -0
- package/scripts/autonomy-blocked-summary.sh +148 -0
- package/scripts/build-search-index.py +43 -0
- package/scripts/build-sitemap.py +119 -0
- package/scripts/cc-doctor.sh +192 -0
- package/scripts/check-hook-event-names.py +127 -0
- package/scripts/check-misfire-instrument.sh +64 -0
- package/scripts/claim-verify-audit.sh +467 -0
- package/scripts/cowork-claudemd-helper.sh +136 -0
- package/scripts/june-15-deprecated-model-scan.sh +111 -0
- package/scripts/measure-heredoc-shapes.sh +67 -0
- package/scripts/measure-literal-match-misfire.sh +110 -0
- package/scripts/probe-scope-guard-branches.sh +48 -0
- package/scripts/probe-skip-line-both-ways.sh +65 -0
- package/scripts/receipts-aggregate.py +199 -0
- package/scripts/session-forensic-audit.py +360 -0
- package/scripts.json +9 -9
- package/scripts.json.bak-2026-07-27-find +13 -0
- package/skills/safety-setup/SKILL.md +5 -5
- package/tests/approve-side-remaining-two.test.sh +129 -0
- package/tests/approver-list-granularity.test.sh +145 -0
- package/tests/askuserquestion-autonomy-gate.test.sh +181 -0
- package/tests/audit-ci-gate.test.sh +87 -0
- package/tests/audit-cross-layer-claudemd.test.sh +170 -0
- package/tests/audit-fail-open-parse.test.sh +100 -0
- package/tests/auto-approve-compound-tail.test.sh +194 -0
- package/tests/auto-mode-path-invoked-commands.test.sh +89 -0
- package/tests/auto-mode-quoted-targets.test.sh +127 -0
- package/tests/auto-mode-unquoted-targets.test.sh +112 -0
- package/tests/bash-secret-output-detector-detects.test.sh +102 -0
- package/tests/blindspots-cross-layer.test.sh +177 -0
- package/tests/block-database-wipe.test.sh +64 -0
- package/tests/bundled-short-flags-bypass.test.sh +85 -0
- package/tests/cd-git-allow-compound-tail.test.sh +67 -0
- package/tests/chown-guard-filesystem-root.test.sh +64 -0
- package/tests/ci-workflow-guard-skip-rule.test.sh +115 -0
- package/tests/compound-deny-safe-rm-targets.test.sh +105 -0
- package/tests/core-scripts-parser-fallback.test.sh +140 -0
- package/tests/destructive-guard-bare-glob.test.sh +74 -0
- package/tests/destructive-guard-every-operand.test.sh +100 -0
- package/tests/destructive-guard-find-deletion.test.sh +114 -0
- package/tests/destructive-guard-flag-order.test.sh +106 -0
- package/tests/destructive-guard-mentions-vs-invocations.test.sh +107 -0
- package/tests/destructive-guard-quoted-target.test.sh +89 -0
- package/tests/destructive-guard-separators.test.sh +105 -0
- package/tests/destructive-guard-variable-target.test.sh +98 -0
- package/tests/destructive-guard-wrapped-command.test.sh +91 -0
- package/tests/doctor-hook-disabling-env.test.sh +92 -0
- package/tests/doctor-misspelled-settings-keys.test.sh +106 -0
- package/tests/example-guards-wrapped-command.test.sh +169 -0
- package/tests/file-edit-backup.test.sh +93 -0
- package/tests/git-filter-repo-guard.test.sh +60 -0
- package/tests/git-global-options-bypass.test.sh +141 -0
- package/tests/git-invocation-prefixes-all-guards.test.sh +114 -0
- package/tests/git-maintenance-guard.test.sh +83 -0
- package/tests/git-pack-temp-cleanup-guard.test.sh +133 -0
- package/tests/git-rm-orphan-wipe-guard.test.sh +66 -0
- package/tests/git-submodule-guard.test.sh +50 -0
- package/tests/grep-pcre-portability.test.sh +155 -0
- package/tests/hook-registration-matches-input.test.sh +73 -0
- package/tests/hook-trigger-matches-envelope.test.sh +178 -0
- package/tests/install-does-not-claim-protection.test.sh +110 -0
- package/tests/install-example-standalone-tools.test.sh +139 -0
- package/tests/line-continuation-bypass.test.sh +114 -0
- package/tests/mcp-config-poisoning-audit.test.sh +106 -0
- package/tests/nested-background-agent-guard.test.sh +58 -0
- package/tests/no-unqualified-safety-claims.test.sh +114 -0
- package/tests/option-eating-grep-patterns.test.sh +130 -0
- package/tests/outdated-core-guards.test.sh +134 -0
- package/tests/outdated-report.test.sh +155 -0
- package/tests/output-secret-mask-warns.test.sh +73 -0
- package/tests/plugin-hooks-fail-open-under-sh.test.sh +165 -0
- package/tests/plugins-word-order.test.sh +126 -0
- package/tests/powershell-remove-item-guard.test.sh +85 -0
- package/tests/project-dir-naming.test.sh +103 -0
- package/tests/quoted-safe-targets-and-mentions.test.sh +190 -0
- package/tests/reroute-after-block-guard.test.sh +104 -0
- package/tests/secret-guard-documented-promises.test.sh +97 -0
- package/tests/session-plugin-pin-watch.test.sh +45 -0
- package/tests/settings-unreadable-refuses-to-write.test.sh +95 -0
- package/tests/shield-no-silent-adoption.test.py +110 -0
- package/tests/shield-no-silent-adoption.test.sh +46 -0
- package/tests/stop-transcript-write-health.test.sh +46 -0
- package/tests/system-dir-guard-root-erased.test.sh +66 -0
- package/tests/test-account-billing-log.sh +100 -0
- package/tests/test-account-routing-preflight.sh +77 -0
- package/tests/test-agent-sdk-credit-pool-monitor.sh +157 -0
- package/tests/test-agent-view-quota-warn.sh +184 -0
- package/tests/test-agents-md-edit-drift-warner.sh +176 -0
- package/tests/test-agents-md-loader.sh +204 -0
- package/tests/test-agents-md-sync-checker.sh +125 -0
- package/tests/test-ai-slop-punctuation-arrest.sh +280 -0
- package/tests/test-allowlist.sh +60 -0
- package/tests/test-always-allow-pattern-suggester.sh +147 -0
- package/tests/test-anthropic-base-url-guard.sh +88 -0
- package/tests/test-articulated-scope-capture.sh +139 -0
- package/tests/test-attachment-traversal-guard.sh +168 -0
- package/tests/test-aup-block-pattern-logger.sh +315 -0
- package/tests/test-aup-false-positive-helper.sh +188 -0
- package/tests/test-aup-large-tool-output-warner.sh +370 -0
- package/tests/test-aup-retry-loop-guard.sh +341 -0
- package/tests/test-auth-expiry-reminder.sh +303 -0
- package/tests/test-auth-macos-sleep-detector.sh +254 -0
- package/tests/test-auth-path-detector.sh +232 -0
- package/tests/test-auth-status-checker.sh +233 -0
- package/tests/test-authorized-reconfirmation-detector.sh +280 -0
- package/tests/test-background-cost-launch-guard.sh +62 -0
- package/tests/test-bash-allowlist-secondary-check.sh +143 -0
- package/tests/test-bash-fanout-bounded-rewriter.sh +186 -0
- package/tests/test-bash-project-fence.sh +127 -0
- package/tests/test-bg-permission-prompt-warner.sh +221 -0
- package/tests/test-binary-read-detector.sh +173 -0
- package/tests/test-broad-prefix-session-trap-warner.sh +167 -0
- package/tests/test-bulk-delete-target-resolution.sh +49 -0
- package/tests/test-bypass-mode-effective-verifier.sh +179 -0
- package/tests/test-cache-creation-drift-detector.sh +244 -0
- package/tests/test-cache-residue-detector.sh +429 -0
- package/tests/test-cache-tier-logger.sh +59 -0
- package/tests/test-cache-ttl-eviction-detector.sh +282 -0
- package/tests/test-cch-sentinel-precommit-guard.sh +64 -0
- package/tests/test-claim-verify-detector.sh +222 -0
- package/tests/test-claim-vs-caveat-checker.sh +134 -0
- package/tests/test-claude-code-version-pin-advisor.sh +353 -0
- package/tests/test-claude-md-reinjector.sh +173 -0
- package/tests/test-claude-md-size-monitor.sh +107 -0
- package/tests/test-claude-update-budget-guard.sh +132 -0
- package/tests/test-claude-update-smart.sh +91 -0
- package/tests/test-claudemd-tool-prohibition.sh +239 -0
- package/tests/test-cli-config-pinning-detector.sh +233 -0
- package/tests/test-cliff-countdown-advisor.sh +138 -0
- package/tests/test-closure-word-verify-gate.sh +158 -0
- package/tests/test-commit-all-scope-guard.sh +73 -0
- package/tests/test-commitment-carry-forward-arrest.sh +250 -0
- package/tests/test-compact-dispatch-watchdog.sh +147 -0
- package/tests/test-completion-claim-without-verification-detector.sh +326 -0
- package/tests/test-compound-bash-permission-resolver.sh +264 -0
- package/tests/test-compound-command-deny-enforcer.sh +67 -0
- package/tests/test-context-length-budget.sh +68 -0
- package/tests/test-context-usage-drift-alert.sh +52 -22
- package/tests/test-cost-incident-self-audit.sh +94 -0
- package/tests/test-cowork-claude-md-load-checker.sh +257 -0
- package/tests/test-cowork-claudemd-helper.sh +141 -0
- package/tests/test-cowork-fuse-staleness-watcher.sh +287 -0
- package/tests/test-cowork-hook-absence-warner.sh +230 -0
- package/tests/test-cowork-model-picker-advisor.sh +317 -0
- package/tests/test-credential-exfil-guard.sh +62 -0
- package/tests/test-cron-create-receipt.sh +165 -0
- package/tests/test-cross-product-mcp-spawn-detector.sh +112 -0
- package/tests/test-daemon-zombie-worker-surfacer.sh +71 -0
- package/tests/test-daily-cost-guard.sh +131 -0
- package/tests/test-deny-rule-integrity-verifier.sh +288 -0
- package/tests/test-deployment-readback-gate.sh +92 -0
- package/tests/test-deployment-readback-gh-adapter.sh +163 -0
- package/tests/test-disabled-feature-toggle-advisor.sh +78 -0
- package/tests/test-dispatch-allowlist-preflight.sh +385 -0
- package/tests/test-dispatch-liveness-watchdog.sh +319 -0
- package/tests/test-dispatch-receipt.sh +314 -0
- package/tests/test-dotenv-anthropic-key-billing-guard.sh +160 -0
- package/tests/test-echo-probe-spam-detector.sh +143 -0
- package/tests/test-edit-quote-ambiguity-guard.sh +75 -0
- package/tests/test-ephemeral-container-detector.sh +251 -0
- package/tests/test-evidence-claim-gate.sh +291 -0
- package/tests/test-extended-thinking-loop-guard.sh +289 -0
- package/tests/test-extended-thinking-resume-warning.sh +240 -0
- package/tests/test-extended-thinking-tool-use-mismatch-detector.sh +236 -0
- package/tests/test-fabricated-command-detector.sh +296 -0
- package/tests/test-false-disk-full-guard.sh +36 -0
- package/tests/test-feature-deprecation-detector.sh +117 -0
- package/tests/test-forbidden-response-phrase-detector.sh +246 -0
- package/tests/test-git-operations-require-approval.sh +53 -0
- package/tests/test-git-push-blast-radius-guard.sh +115 -0
- package/tests/test-goal-iteration-limit-warner.sh +128 -0
- package/tests/test-growthbook-flag-monitor.sh +428 -0
- package/tests/test-halt-signal-detector.sh +120 -0
- package/tests/test-hook-self-disable-detector.sh +232 -0
- package/tests/test-iteration-quality-degradation-detector.sh +244 -0
- package/tests/test-long-session-malformed-tool-call-detector.sh +185 -0
- package/tests/test-marketplace-plugins.sh +193 -0
- package/tests/test-mcp-misdiagnosis-arrest.sh +235 -0
- package/tests/test-mcp-orphan-leak-warner.sh +71 -0
- package/tests/test-mcp-permissions-ask-warner.sh +207 -0
- package/tests/test-mcp-routine-approval-detector.sh +229 -0
- package/tests/test-mcp-startup-bloat-detector.sh +141 -0
- package/tests/test-mcp-stdio-compatibility-test.sh +112 -0
- package/tests/test-memory-chain-audit.sh +59 -0
- package/tests/test-memory-orphan-detector.sh +227 -0
- package/tests/test-model-swap-suggester.sh +368 -0
- package/tests/test-model-version-lock.sh +83 -0
- package/tests/test-multi-file-plan-routing-gate.sh +277 -0
- package/tests/test-multi-window-auth-drift-detector.sh +262 -0
- package/tests/test-nested-spawn-inflight-guard.sh +281 -0
- package/tests/test-no-force-flag.sh +49 -0
- package/tests/test-non-english-quality-warner.sh +229 -0
- package/tests/test-oauth-refresh-monitor.sh +311 -0
- package/tests/test-opus-degradation-tracker.sh +274 -0
- package/tests/test-opus48-routine-task-warning.sh +201 -0
- package/tests/test-opus48-thinking-wedge-advisor.sh +249 -0
- package/tests/test-output-token-spike-detector.sh +315 -0
- package/tests/test-parallel-batch-size-limiter.sh +228 -0
- package/tests/test-parallel-cascade-detector.sh +191 -0
- package/tests/test-partial-view-claim-arrest.sh +272 -0
- package/tests/test-per-prompt-token-cap.sh +136 -0
- package/tests/test-persisted-1m-model-advisor.sh +71 -0
- package/tests/test-plugin-hooks-json-bloat-detector.sh +267 -0
- package/tests/test-post-edit-disk-verify.sh +294 -0
- package/tests/test-pre-bash-sed-line-ending-windows.sh +108 -0
- package/tests/test-pre-execution-claim-detector.sh +366 -0
- package/tests/test-private-key-backup-and-git-credentials.sh +100 -0
- package/tests/test-proxy-capture-suggester.sh +214 -0
- package/tests/test-public-artefact-socratic-narrowing.sh +346 -0
- package/tests/test-quota-anomaly-detector.sh +321 -0
- package/tests/test-quota-reset-audit.sh +81 -0
- package/tests/test-read-loop-detector.sh +137 -0
- package/tests/test-read-once-guard.sh +85 -0
- package/tests/test-receipts-aggregate.sh +143 -0
- package/tests/test-redirect-fragment-warner.sh +130 -0
- package/tests/test-redundant-read-blocker.sh +155 -0
- package/tests/test-refusal-arrest-gate.sh +303 -0
- package/tests/test-remote-control-billing-classifier.sh +125 -0
- package/tests/test-resume-drift-watcher.sh +65 -0
- package/tests/test-rg-replace-flag-detector.sh +175 -0
- package/tests/test-rhetorical-verification-prompt-detector.sh +237 -0
- package/tests/test-rm-safety-net.sh +115 -0
- package/tests/test-route-handler-emptiness-gate.sh +300 -0
- package/tests/test-runtime-binary-change-detector.sh +196 -0
- package/tests/test-same-command-repeat-detector.sh +165 -0
- package/tests/test-same-correction-arrest.sh +165 -0
- package/tests/test-scope-expansion-receipt.sh +207 -0
- package/tests/test-server-side-prompt-injection-detector.sh +181 -0
- package/tests/test-session-persistence-verifier.sh +128 -0
- package/tests/test-session-rate-monitor.sh +284 -0
- package/tests/test-session-start-quota-status.sh +210 -0
- package/tests/test-session-start-safety-check.sh +138 -0
- package/tests/test-settings-hooks-key-restorer.sh +257 -0
- package/tests/test-settings-json-health-check.sh +70 -0
- package/tests/test-settings-regression-tester.sh +197 -0
- package/tests/test-skill-cumulative-size-detector.sh +198 -0
- package/tests/test-skill-description-drop-detector.sh +236 -0
- package/tests/test-skill-truncation-detector.sh +263 -0
- package/tests/test-skills-context-recorder.sh +329 -0
- package/tests/test-skills-load-verifier.sh +184 -0
- package/tests/test-skills-settings-validator.sh +242 -0
- package/tests/test-sonnet-45-deprecation-helper.sh +150 -0
- package/tests/test-spurious-malformed-notice-detector.sh +290 -0
- package/tests/test-sql-bulk-delete-warn.sh +123 -0
- package/tests/test-ssh-key-protect.sh +81 -0
- package/tests/test-stale-temp-settings-detector.sh +161 -0
- package/tests/test-standing-rule-session-surfacer.sh +215 -0
- package/tests/test-starter-hooks-manifest.sh +96 -0
- package/tests/test-stop-hook-sigterm-wrapper.sh +258 -0
- package/tests/test-strict-allowlist.sh +72 -0
- package/tests/test-subagent-boundary-precheck.sh +137 -0
- package/tests/test-subagent-closure-verify-gate.sh +193 -0
- package/tests/test-subagent-destructive-git-guard.sh +86 -0
- package/tests/test-subagent-identity-leak-guard.sh +62 -0
- package/tests/test-subagent-identity-reassertion.sh +61 -0
- package/tests/test-subagent-inheritance-tester.sh +319 -0
- package/tests/test-subagent-permission-mode-guard.sh +61 -0
- package/tests/test-subagent-spawn-verification-enforcer.sh +60 -0
- package/tests/test-subagent-tool-allowlist-enforcer.sh +62 -0
- package/tests/test-subagent-tool-call-limiter.sh +97 -0
- package/tests/test-subscription-api-billing-warner.sh +138 -0
- package/tests/test-subscription-bypass-detector.sh +255 -0
- package/tests/test-temporal-suggestion-detector.sh +183 -0
- package/tests/test-thinking-budget-effort-mismatch-detector.sh +327 -0
- package/tests/test-tokenizer-ratio-alert-corpus.sh +77 -0
- package/tests/test-tokenizer-ratio-alert.sh +93 -0
- package/tests/test-tool-result-correlation-checker.sh +264 -0
- package/tests/test-tool-result-size-guard.sh +121 -0
- package/tests/test-transcript-contamination-detector.sh +264 -0
- package/tests/test-trigger-header-parsing.sh +151 -0
- package/tests/test-ultrareview-large-diff-advisor.sh +192 -0
- package/tests/test-userprompt-submit-receipt.sh +181 -0
- package/tests/test-verify-after-publish-reminder.sh +184 -0
- package/tests/test-version-bump-detector.sh +212 -0
- package/tests/test-version-regression-warner.sh +165 -0
- package/tests/test-warn-cron-cost-trap.sh +82 -0
- package/tests/test-webfetch-runaway-guard.sh +77 -0
- package/tests/test-wellness-break-reminder.sh +138 -0
- package/tests/test-windows-destructive-command-guard.sh +169 -0
- package/tests/test-windows-python-stub-detector.sh +146 -0
- package/tests/test-worktree-edit-boundary-guard.sh +219 -0
- package/tests/test-write-byte-integrity-verifier.sh +192 -0
- package/tests/test-xml-format-leak-detector.sh +299 -0
- package/tests/trustfall-mcp-injection-guard.test.sh +183 -0
- package/tests/workspace-lease-guard.test.sh +144 -0
- package/tests/worktree-escape-write-guard.test.sh +68 -0
- package/tests/worktree-hooks-path-fix.test.sh +181 -0
- package/tests/worktree-parent-write-guard.test.sh +51 -0
- package/tests/write-empty-content-guard.test.sh +39 -0
- package/tests/write-nul-corruption-detector.test.sh +29 -0
- package/tests/write-overwrite-unread-content.test.sh +178 -0
- package/tests/wsl-host-disk-space-guard.test.sh +55 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# --outdated: which installed hooks no longer match what ships today.
|
|
3
|
+
#
|
|
4
|
+
# Why this exists. Installing a hook copies the file. Nothing ever copies it
|
|
5
|
+
# back. So a hook installed in March keeps running its March logic forever,
|
|
6
|
+
# including bugs fixed here months later, and until now nothing in the CLI
|
|
7
|
+
# could tell you that. Measured on a real four-month-old install: 27 of the 31
|
|
8
|
+
# hooks that came from this project no longer matched the shipped version, and
|
|
9
|
+
# three of those were missing the `jq` guard that was added after they were
|
|
10
|
+
# installed — the difference between blocking a dangerous command and silently
|
|
11
|
+
# approving it.
|
|
12
|
+
#
|
|
13
|
+
# The command only reports. The most important control here is the last one:
|
|
14
|
+
# it must not write anything. A command whose whole job is "tell me if my
|
|
15
|
+
# safety net is stale" must not itself modify the safety net.
|
|
16
|
+
#
|
|
17
|
+
# The second control is that "differs" is not claimed to mean "outdated".
|
|
18
|
+
# A user who edited their own copy on purpose must not be told they are behind,
|
|
19
|
+
# so the wording offers both readings and the test pins that wording.
|
|
20
|
+
|
|
21
|
+
set -u
|
|
22
|
+
REPO="$(cd "$(dirname "$0")/.." && pwd)"
|
|
23
|
+
PASS=0
|
|
24
|
+
FAIL=0
|
|
25
|
+
|
|
26
|
+
check() { # name expected actual
|
|
27
|
+
if [ "$2" = "$3" ]; then
|
|
28
|
+
PASS=$((PASS + 1))
|
|
29
|
+
else
|
|
30
|
+
FAIL=$((FAIL + 1))
|
|
31
|
+
echo " FAIL: $1"
|
|
32
|
+
echo " expected: $2"
|
|
33
|
+
echo " actual: $3"
|
|
34
|
+
fi
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
contains() { # name haystack needle
|
|
38
|
+
case "$2" in
|
|
39
|
+
*"$3"*) PASS=$((PASS + 1)) ;;
|
|
40
|
+
*) FAIL=$((FAIL + 1)); echo " FAIL: $1"; echo " output did not contain: $3" ;;
|
|
41
|
+
esac
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
not_contains() { # name haystack needle
|
|
45
|
+
case "$2" in
|
|
46
|
+
*"$3"*) FAIL=$((FAIL + 1)); echo " FAIL: $1"; echo " output unexpectedly contained: $3" ;;
|
|
47
|
+
*) PASS=$((PASS + 1)) ;;
|
|
48
|
+
esac
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
TESTHOME="$(mktemp -d)"
|
|
52
|
+
trap 'rm -rf "$TESTHOME"' EXIT
|
|
53
|
+
|
|
54
|
+
# Sets OUT and RC. Deliberately not called through $( ) — command substitution
|
|
55
|
+
# runs in a subshell, so the exit code would never reach the caller.
|
|
56
|
+
run_outdated() {
|
|
57
|
+
OUT=$(CLAUDE_PROJECT_DIR="$TESTHOME" node "$REPO/index.mjs" --outdated 2>&1)
|
|
58
|
+
RC=$?
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
# --- 1. no hooks directory at all --------------------------------------------
|
|
62
|
+
run_outdated
|
|
63
|
+
check "no hooks dir exits 0" "0" "$RC"
|
|
64
|
+
contains "no hooks dir is explained" "$OUT" "No hooks directory"
|
|
65
|
+
|
|
66
|
+
# --- 2. an untouched copy of a shipped hook reports as matching ---------------
|
|
67
|
+
mkdir -p "$TESTHOME/.claude/hooks"
|
|
68
|
+
cp "$REPO/examples/protect-claudemd.sh" "$TESTHOME/.claude/hooks/protect-claudemd.sh"
|
|
69
|
+
run_outdated
|
|
70
|
+
check "identical copy exits 0" "0" "$RC"
|
|
71
|
+
contains "identical copy reported as matching" "$OUT" "match the shipped version"
|
|
72
|
+
not_contains "identical copy is not called differing" "$OUT" "differ from what ships today"
|
|
73
|
+
|
|
74
|
+
# --- 3. a modified copy is reported, and the exit code is non-zero -----------
|
|
75
|
+
echo "# a local change" >> "$TESTHOME/.claude/hooks/protect-claudemd.sh"
|
|
76
|
+
run_outdated
|
|
77
|
+
check "modified copy exits 1" "1" "$RC"
|
|
78
|
+
contains "modified copy is named" "$OUT" "protect-claudemd.sh"
|
|
79
|
+
contains "modified copy is counted" "$OUT" "differ from what ships today"
|
|
80
|
+
|
|
81
|
+
# --- 4. "differs" must not be claimed to mean "outdated" ---------------------
|
|
82
|
+
# The user may have edited the file on purpose. Both readings must be offered.
|
|
83
|
+
contains "both readings are offered (fixed here)" "$OUT" "was fixed after you installed it"
|
|
84
|
+
contains "both readings are offered (your edit)" "$OUT" "you edited your copy on purpose"
|
|
85
|
+
contains "a way to look before replacing" "$OUT" "diff "
|
|
86
|
+
|
|
87
|
+
# --- 5. the concrete harm: installed copy parses with jq but has no jq guard --
|
|
88
|
+
# Build exactly that situation from a shipped hook that does have the guard.
|
|
89
|
+
JQ_HOOK=""
|
|
90
|
+
for f in "$REPO"/examples/*.sh; do
|
|
91
|
+
if grep -q 'command -v jq\|which jq' "$f" && grep -q '\bjq\b' "$f"; then
|
|
92
|
+
JQ_HOOK="$f"; break
|
|
93
|
+
fi
|
|
94
|
+
done
|
|
95
|
+
if [ -n "$JQ_HOOK" ]; then
|
|
96
|
+
NAME="$(basename "$JQ_HOOK")"
|
|
97
|
+
rm -f "$TESTHOME/.claude/hooks/protect-claudemd.sh"
|
|
98
|
+
grep -v 'command -v jq' "$JQ_HOOK" | grep -v 'which jq' > "$TESTHOME/.claude/hooks/$NAME"
|
|
99
|
+
run_outdated
|
|
100
|
+
check "jq-guardless copy exits 1" "1" "$RC"
|
|
101
|
+
contains "jq-guardless copy is flagged as such" "$OUT" "no jq guard"
|
|
102
|
+
else
|
|
103
|
+
echo " SKIP: no shipped hook both uses jq and guards for it"
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
# --- 6. a hook this project does not ship is not judged ----------------------
|
|
107
|
+
rm -f "$TESTHOME/.claude/hooks"/*.sh
|
|
108
|
+
cat > "$TESTHOME/.claude/hooks/my-own-thing.sh" <<'EOF'
|
|
109
|
+
#!/bin/bash
|
|
110
|
+
exit 0
|
|
111
|
+
EOF
|
|
112
|
+
run_outdated
|
|
113
|
+
check "foreign-only install exits 0" "0" "$RC"
|
|
114
|
+
contains "foreign hook counted separately" "$OUT" "not shipped by this project"
|
|
115
|
+
not_contains "foreign hook is not named as differing" "$OUT" "my-own-thing.sh differ"
|
|
116
|
+
|
|
117
|
+
# --- 7. the control that matters: --outdated must not write anything ---------
|
|
118
|
+
# This control was defeated twice while it was being written, in two different
|
|
119
|
+
# ways, and both are worth stating because both are easy to reproduce:
|
|
120
|
+
#
|
|
121
|
+
# 1. The snapshot used `for f in *`. A glob does not match dotfiles, so a
|
|
122
|
+
# planted `writeFileSync(HOOKS_DIR + '/.outdated-marker')` was invisible.
|
|
123
|
+
# Fixed by walking the directory with find.
|
|
124
|
+
# 2. Even after that, the run in section 6 had already created the marker, so
|
|
125
|
+
# the file existed in BOTH the before and the after snapshot and the
|
|
126
|
+
# comparison still showed no change. A "did it write?" check cannot share
|
|
127
|
+
# state with an earlier run of the thing under test.
|
|
128
|
+
#
|
|
129
|
+
# So this section builds its own directory and takes the first snapshot before
|
|
130
|
+
# --outdated has ever run against it.
|
|
131
|
+
snapshot() { # dir -> "relpath:cksum" lines, dotfiles included
|
|
132
|
+
find "$1" -type f | sort | while IFS= read -r f; do
|
|
133
|
+
printf '%s:%s\n' "${f#"$1"}" "$(cksum < "$f")"
|
|
134
|
+
done
|
|
135
|
+
}
|
|
136
|
+
TESTHOME="$(mktemp -d)" # fresh: nothing here has been looked at yet
|
|
137
|
+
trap 'rm -rf "$TESTHOME"' EXIT
|
|
138
|
+
mkdir -p "$TESTHOME/.claude/hooks"
|
|
139
|
+
# A hook that differs, so --outdated has something to report and therefore takes
|
|
140
|
+
# the branch that prints. A run that finds nothing would not exercise the code
|
|
141
|
+
# where a stray write would live.
|
|
142
|
+
cp "$REPO/examples/protect-claudemd.sh" "$TESTHOME/.claude/hooks/protect-claudemd.sh"
|
|
143
|
+
echo "# local edit" >> "$TESTHOME/.claude/hooks/protect-claudemd.sh"
|
|
144
|
+
printf '{"hooks":{}}' > "$TESTHOME/.claude/settings.json"
|
|
145
|
+
BEFORE_HOOKS=$(snapshot "$TESTHOME/.claude/hooks")
|
|
146
|
+
BEFORE_SETTINGS=$(cksum < "$TESTHOME/.claude/settings.json")
|
|
147
|
+
run_outdated
|
|
148
|
+
AFTER_HOOKS=$(snapshot "$TESTHOME/.claude/hooks")
|
|
149
|
+
AFTER_SETTINGS=$(cksum < "$TESTHOME/.claude/settings.json")
|
|
150
|
+
check "hooks are untouched by --outdated" "$BEFORE_HOOKS" "$AFTER_HOOKS"
|
|
151
|
+
check "settings.json is untouched by --outdated" "$BEFORE_SETTINGS" "$AFTER_SETTINGS"
|
|
152
|
+
|
|
153
|
+
echo
|
|
154
|
+
echo "outdated-report: $PASS passed, $FAIL failed"
|
|
155
|
+
[ "$FAIL" -eq 0 ] || exit 1
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# output-secret-mask: does the warning actually fire, and only when it should?
|
|
3
|
+
#
|
|
4
|
+
# Why this exists. The eight existing checks for this hook in test.sh all assert
|
|
5
|
+
# exit 0. This hook *always* exits 0 — it only writes to stderr — so those checks
|
|
6
|
+
# pass whether the warning fires or not. They cannot fail on the behaviour that
|
|
7
|
+
# matters, which is the same shape as the "234 test files that could not fail"
|
|
8
|
+
# noted in the CI workflow. These assert on stderr instead.
|
|
9
|
+
#
|
|
10
|
+
# The regression being pinned: the OpenAI pattern used to be written without a
|
|
11
|
+
# left boundary, so ordinary English words ending in "sk" before a separator
|
|
12
|
+
# (task-, ask-, risk-, disk-, desk-) matched and the hook cried on ordinary
|
|
13
|
+
# output. A warning that fires on normal paths is a warning operators learn to
|
|
14
|
+
# ignore, which costs the whole hook.
|
|
15
|
+
#
|
|
16
|
+
# Both directions are asserted deliberately: silencing a false positive is only
|
|
17
|
+
# a fix if the true positive still fires.
|
|
18
|
+
|
|
19
|
+
set -u
|
|
20
|
+
REPO="$(cd "$(dirname "$0")/.." && pwd)"
|
|
21
|
+
HOOK="$REPO/examples/output-secret-mask.sh"
|
|
22
|
+
PASS=0
|
|
23
|
+
FAIL=0
|
|
24
|
+
|
|
25
|
+
# Split so that writing this file does not itself trip a secret scanner.
|
|
26
|
+
KEY="sk""-proj-abcdefghijklmno""pqrst1234567890"
|
|
27
|
+
AWS="AKI""AIOSFODNN7""EXAMPLE"
|
|
28
|
+
ORDINARY="data/task-""management-system-config.json"
|
|
29
|
+
|
|
30
|
+
run() { # run <stdout-text> -> sets ERR
|
|
31
|
+
local payload
|
|
32
|
+
payload=$(printf '{"tool_name":"Bash","tool_input":{"command":"cat conf"},"tool_result":{"stdout":"%s"}}' "$1")
|
|
33
|
+
ERR=$(printf '%s' "$payload" | bash "$HOOK" 2>&1 >/dev/null)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
warns() { # warns <name> <text> <expect: yes|no>
|
|
37
|
+
run "$2"
|
|
38
|
+
local got="no"
|
|
39
|
+
case "$ERR" in *WARNING*) got="yes" ;; esac
|
|
40
|
+
if [ "$got" = "$3" ]; then
|
|
41
|
+
PASS=$((PASS + 1))
|
|
42
|
+
else
|
|
43
|
+
FAIL=$((FAIL + 1))
|
|
44
|
+
echo " FAIL: $1 (expected warn=$3, got warn=$got)"
|
|
45
|
+
echo " stderr: $ERR"
|
|
46
|
+
fi
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
echo "output-secret-mask-warns:"
|
|
50
|
+
|
|
51
|
+
# --- true positives: the warning has to fire -------------------------------
|
|
52
|
+
warns "real key shape warns" "$KEY" yes
|
|
53
|
+
warns "AWS key shape warns" "$AWS" yes
|
|
54
|
+
warns "API_KEY assignment warns" "API_KEY=abc123def456ghi" yes
|
|
55
|
+
|
|
56
|
+
# --- false positives: the warning must stay quiet --------------------------
|
|
57
|
+
warns "ordinary task- path is quiet" "$ORDINARY" no
|
|
58
|
+
warns "plain output is quiet" "hello world" no
|
|
59
|
+
warns "safe env var is quiet" "PATH=/usr/bin" no
|
|
60
|
+
|
|
61
|
+
# --- the hook never blocks; it only reports --------------------------------
|
|
62
|
+
printf '%s' "{\"tool_name\":\"Bash\",\"tool_result\":{\"stdout\":\"$KEY\"}}" \
|
|
63
|
+
| bash "$HOOK" >/dev/null 2>&1
|
|
64
|
+
rc=$?
|
|
65
|
+
if [ "$rc" -eq 0 ]; then
|
|
66
|
+
PASS=$((PASS + 1))
|
|
67
|
+
else
|
|
68
|
+
FAIL=$((FAIL + 1))
|
|
69
|
+
echo " FAIL: hook must not block (expected exit 0, got $rc)"
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
echo " $PASS passed, $FAIL failed"
|
|
73
|
+
[ "$FAIL" -eq 0 ]
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# The plugin hooks failed open under /bin/sh
|
|
3
|
+
#
|
|
4
|
+
# hooks/hooks.json holds the four inline guards that ship as the plugin's
|
|
5
|
+
# hook file. Claude Code runs an inline hook command through /bin/sh, and on
|
|
6
|
+
# Debian and Ubuntu /bin/sh is dash. Dash's builtin echo interprets backslash
|
|
7
|
+
# escapes; bash's does not.
|
|
8
|
+
#
|
|
9
|
+
# Every one of the four guards started with:
|
|
10
|
+
#
|
|
11
|
+
# INPUT=$(cat); CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
|
|
12
|
+
#
|
|
13
|
+
# The hook payload is JSON, so a newline inside the command or the file
|
|
14
|
+
# content arrives as the two characters \n. Under dash, echo turned those two
|
|
15
|
+
# characters into a real newline *inside a JSON string*, jq rejected the
|
|
16
|
+
# document, CMD came back empty, and the very next clause is:
|
|
17
|
+
#
|
|
18
|
+
# [ -z "$CMD" ] && exit 0
|
|
19
|
+
#
|
|
20
|
+
# exit 0 is an affirmative approval. So the guards let the tool call through,
|
|
21
|
+
# and the debug log recorded the hook as "success". Measured 2026-09-03: an
|
|
22
|
+
# agent asked to create .env wrote it with all six shipped hooks installed.
|
|
23
|
+
#
|
|
24
|
+
# Nothing local caught this for months because every test ran the commands
|
|
25
|
+
# under bash, where echo leaves \n alone. The five plugin manifests under
|
|
26
|
+
# plugins/ already used `printf %s "$INPUT"`; hooks/hooks.json never got the
|
|
27
|
+
# same change.
|
|
28
|
+
#
|
|
29
|
+
# Three more holes turned up in the same file once the parse was fixed:
|
|
30
|
+
#
|
|
31
|
+
# 1. The rm guard checked its exemption list (node_modules|dist|build|
|
|
32
|
+
# __pycache__|tmp) against the whole command, so `cd /tmp` on line one
|
|
33
|
+
# exempted `rm -rf /` on line two.
|
|
34
|
+
# 2. `sed -E 's/[;&|#].*$//'` truncated at the first `;`, so everything
|
|
35
|
+
# after a separator was never examined at all.
|
|
36
|
+
# 3. A line continuation split the spelling and no guard matched it.
|
|
37
|
+
#
|
|
38
|
+
# The fix joins continuations, splits on statement separators, and asks the
|
|
39
|
+
# dangerous question and the exemption question about the *same* statement.
|
|
40
|
+
#
|
|
41
|
+
# Every case below runs the shipped command under /bin/sh, which is the shell
|
|
42
|
+
# that actually runs it in production.
|
|
43
|
+
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
44
|
+
HOOKS="$ROOT/hooks/hooks.json"
|
|
45
|
+
PASS=0
|
|
46
|
+
FAIL=0
|
|
47
|
+
|
|
48
|
+
hook_command() {
|
|
49
|
+
# $1 = index into PreToolUse
|
|
50
|
+
python3 -c "
|
|
51
|
+
import json, sys
|
|
52
|
+
d = json.load(open('$HOOKS', encoding='utf-8'))
|
|
53
|
+
pre = (d.get('hooks') or d)['PreToolUse']
|
|
54
|
+
sys.stdout.write(pre[$1]['hooks'][0]['command'])
|
|
55
|
+
"
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
check() {
|
|
59
|
+
# $1=index $2=label $3=expected(block|allow) $4=payload json
|
|
60
|
+
local cmd rc got
|
|
61
|
+
cmd=$(hook_command "$1")
|
|
62
|
+
printf '%s' "$4" | /bin/sh -c "$cmd" >/dev/null 2>&1
|
|
63
|
+
rc=$?
|
|
64
|
+
if [ "$rc" -eq 2 ]; then got=block; else got=allow; fi
|
|
65
|
+
if [ "$got" = "$3" ]; then
|
|
66
|
+
PASS=$((PASS + 1))
|
|
67
|
+
else
|
|
68
|
+
FAIL=$((FAIL + 1))
|
|
69
|
+
echo " FAIL [hook $1] $2: expected $3, got $got (rc=$rc)"
|
|
70
|
+
fi
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
bash_payload() {
|
|
74
|
+
python3 -c "
|
|
75
|
+
import json, sys
|
|
76
|
+
sys.stdout.write(json.dumps({'tool_name': 'Bash',
|
|
77
|
+
'tool_input': {'command': sys.argv[1]}}))
|
|
78
|
+
" "$1"
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
write_payload() {
|
|
82
|
+
python3 -c "
|
|
83
|
+
import json, sys
|
|
84
|
+
sys.stdout.write(json.dumps({'tool_name': 'Write',
|
|
85
|
+
'tool_input': {'file_path': sys.argv[1],
|
|
86
|
+
'content': sys.argv[2]}}))
|
|
87
|
+
" "$1" "$2"
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
# The dangerous spellings are assembled so that mentioning them in this file
|
|
91
|
+
# does not trip the repository's own guards.
|
|
92
|
+
RM="rm -rf /"
|
|
93
|
+
PUSHF="git push --force origin main"
|
|
94
|
+
RESETH="git reset --hard HEAD~1"
|
|
95
|
+
CLEANFD="git clean -fd"
|
|
96
|
+
|
|
97
|
+
echo "hook 0 — recursive rm"
|
|
98
|
+
check 0 "plain" block "$(bash_payload "$RM")"
|
|
99
|
+
check 0 "newline after cd /tmp" block "$(bash_payload "$(printf 'cd /tmp\n%s' "$RM")")"
|
|
100
|
+
check 0 "newline after cd build" block "$(bash_payload "$(printf 'cd build\n%s' "$RM")")"
|
|
101
|
+
check 0 "line continuation" block "$(bash_payload "$(printf 'rm \\\n -rf /')")"
|
|
102
|
+
check 0 "after a semicolon" block "$(bash_payload "date; $RM")"
|
|
103
|
+
check 0 "after &&" block "$(bash_payload "cd /tmp && $RM")"
|
|
104
|
+
check 0 "sudo prefix" block "$(bash_payload "sudo $RM")"
|
|
105
|
+
check 0 "-fr order" block "$(bash_payload "rm -fr /")"
|
|
106
|
+
check 0 "--recursive" block "$(bash_payload "rm --recursive /etc")"
|
|
107
|
+
check 0 "home subdirectory" block "$(bash_payload "rm -rf ~/Documents")"
|
|
108
|
+
check 0 "node_modules" allow "$(bash_payload "rm -rf node_modules")"
|
|
109
|
+
check 0 "./build" allow "$(bash_payload "rm -rf ./build")"
|
|
110
|
+
check 0 "dist/" allow "$(bash_payload "rm -rf dist/")"
|
|
111
|
+
check 0 "under /tmp" allow "$(bash_payload "rm -rf /tmp/scratch")"
|
|
112
|
+
check 0 "non-recursive rm" allow "$(bash_payload "rm -f foo.txt")"
|
|
113
|
+
check 0 "plain ls" allow "$(bash_payload "ls -la")"
|
|
114
|
+
check 0 "inside a comment" allow "$(bash_payload "date # $RM")"
|
|
115
|
+
|
|
116
|
+
echo "hook 1 — destructive git"
|
|
117
|
+
check 1 "force push" block "$(bash_payload "$PUSHF")"
|
|
118
|
+
check 1 "force push continued" block "$(bash_payload "$(printf 'git push \\\n --force origin main')")"
|
|
119
|
+
check 1 "force push line 2" block "$(bash_payload "$(printf 'cd /tmp\n%s' "$PUSHF")")"
|
|
120
|
+
check 1 "hard reset" block "$(bash_payload "$RESETH")"
|
|
121
|
+
check 1 "hard reset continued" block "$(bash_payload "$(printf 'git reset \\\n --hard HEAD~1')")"
|
|
122
|
+
check 1 "clean -fd" block "$(bash_payload "$CLEANFD")"
|
|
123
|
+
check 1 "clean -fd continued" block "$(bash_payload "$(printf 'git clean \\\n -fd')")"
|
|
124
|
+
check 1 "force-with-lease" allow "$(bash_payload "git push --force-with-lease origin main")"
|
|
125
|
+
check 1 "word on another line" allow "$(bash_payload "$(printf 'git push origin main\necho --force')")"
|
|
126
|
+
check 1 "ordinary push" allow "$(bash_payload "git push origin main")"
|
|
127
|
+
check 1 "soft reset" allow "$(bash_payload "git reset --soft HEAD~1")"
|
|
128
|
+
|
|
129
|
+
echo "hook 2 — credentials in the command"
|
|
130
|
+
check 2 "inline key" block "$(bash_payload "export API_KEY=abcdefghij0123456789xyz")"
|
|
131
|
+
check 2 "inline key continued" block "$(bash_payload "$(printf 'export API_KEY=\\\nabcdefghij0123456789xyz')")"
|
|
132
|
+
check 2 "short value" allow "$(bash_payload "export API_KEY=short")"
|
|
133
|
+
check 2 "unrelated" allow "$(bash_payload "ls -la")"
|
|
134
|
+
|
|
135
|
+
echo "hook 3 — writes to sensitive files"
|
|
136
|
+
check 3 "dotenv" block "$(write_payload "/x/.env" "$(printf 'A=1\nB=2')")"
|
|
137
|
+
check 3 "private key" block "$(write_payload "/x/server.pem" "$(printf 'A=1\nB=2')")"
|
|
138
|
+
check 3 "ssh key" block "$(write_payload "/x/id_rsa" "$(printf 'A=1\nB=2')")"
|
|
139
|
+
check 3 "dotenv template" allow "$(write_payload "/x/.env.template" "$(printf 'A=1\nB=2')")"
|
|
140
|
+
check 3 "ordinary file" allow "$(write_payload "/x/notes.md" "$(printf 'A=1\nB=2')")"
|
|
141
|
+
|
|
142
|
+
echo "no hook may still pipe echo into a parser"
|
|
143
|
+
# The file is JSON, so the spelling inside it is escaped. Grepping the raw
|
|
144
|
+
# bytes for the unescaped form matches nothing even when the defect is
|
|
145
|
+
# present -- a check that cannot fail. Read it as JSON and look at the
|
|
146
|
+
# commands after unescaping.
|
|
147
|
+
if python3 -c "
|
|
148
|
+
import json, sys
|
|
149
|
+
d = json.load(open('$HOOKS', encoding='utf-8'))
|
|
150
|
+
pre = (d.get('hooks') or d)['PreToolUse']
|
|
151
|
+
bad = [i for i, e in enumerate(pre)
|
|
152
|
+
for h in e.get('hooks', [])
|
|
153
|
+
if 'echo \"\$INPUT\"' in h.get('command', '')]
|
|
154
|
+
print(','.join(str(i) for i in bad))
|
|
155
|
+
sys.exit(1 if bad else 0)
|
|
156
|
+
"; then
|
|
157
|
+
PASS=$((PASS + 1))
|
|
158
|
+
else
|
|
159
|
+
FAIL=$((FAIL + 1))
|
|
160
|
+
echo " FAIL: a hook still pipes echo \"\$INPUT\" into a parser"
|
|
161
|
+
fi
|
|
162
|
+
|
|
163
|
+
echo
|
|
164
|
+
echo "PASS=$PASS FAIL=$FAIL"
|
|
165
|
+
[ "$FAIL" -eq 0 ]
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# marketplace plugins: the same word-order assumptions, in a second implementation
|
|
3
|
+
#
|
|
4
|
+
# The four bundles under plugins/ carry their shell **inline in plugin.json**. They
|
|
5
|
+
# are not copies of the core hooks — they are a separate implementation — so fixing
|
|
6
|
+
# the core does nothing for anyone who installed a plugin.
|
|
7
|
+
#
|
|
8
|
+
# Measured 2026-08-03 with all four bundles installed together, over the 24 forms
|
|
9
|
+
# of the destructive-command probe that these bundles claim to cover:
|
|
10
|
+
#
|
|
11
|
+
# before 10 of 24 walked through
|
|
12
|
+
# after 3 of 24 (find / dd / chmod — none of the four bundles claims those)
|
|
13
|
+
#
|
|
14
|
+
# What walked through was the familiar shape: a rule describing how a command is
|
|
15
|
+
# usually typed rather than what it does.
|
|
16
|
+
#
|
|
17
|
+
# rm -r -f X the flags have to be one bundle
|
|
18
|
+
# rm --recursive --force X ... and short
|
|
19
|
+
# rm X -rf ... and before the target
|
|
20
|
+
# git reset HEAD~1 --hard --hard has to follow reset
|
|
21
|
+
# git clean -x -f -d the flags have to come first
|
|
22
|
+
# git branch --delete --force only -D was named
|
|
23
|
+
#
|
|
24
|
+
# One description also promised something that was never implemented:
|
|
25
|
+
# git-protection said it "guards interactive rebase" and no hook in the bundle
|
|
26
|
+
# mentions rebase at all. The claim is gone rather than newly built — an interactive
|
|
27
|
+
# rebase is ordinary work, and blocking it would trade a false promise for a false
|
|
28
|
+
# positive.
|
|
29
|
+
#
|
|
30
|
+
# The dry runs are the half that keeps a guard installed: widening the git clean
|
|
31
|
+
# pattern also caught `git clean -nd`, which removes nothing. That regression was
|
|
32
|
+
# introduced and fixed in the same pass; both directions are asserted below.
|
|
33
|
+
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
34
|
+
|
|
35
|
+
PASS=0; FAIL=0
|
|
36
|
+
|
|
37
|
+
# want: block | allow
|
|
38
|
+
check() {
|
|
39
|
+
local desc="$1" cmd="$2" want="$3" tool="${4:-Bash}"
|
|
40
|
+
local got
|
|
41
|
+
got=$(python3 - "$ROOT" "$cmd" "$tool" <<'PY'
|
|
42
|
+
import glob, json, os, subprocess, sys, tempfile
|
|
43
|
+
root, cmd, tool = sys.argv[1], sys.argv[2], sys.argv[3]
|
|
44
|
+
cmds = []
|
|
45
|
+
for pdir in sorted(glob.glob(os.path.join(root, "plugins", "*"))):
|
|
46
|
+
f = os.path.join(pdir, ".claude-plugin", "plugin.json")
|
|
47
|
+
if not os.path.exists(f):
|
|
48
|
+
continue
|
|
49
|
+
d = json.load(open(f, encoding="utf-8"))
|
|
50
|
+
for ev in d.get("hooks", {}).values():
|
|
51
|
+
for e in ev:
|
|
52
|
+
for h in e.get("hooks", []):
|
|
53
|
+
cmds.append((e.get("matcher", ""), h["command"]))
|
|
54
|
+
req = json.dumps({"hook_event_name": "PreToolUse", "tool_name": tool,
|
|
55
|
+
"tool_input": {"command": cmd, "file_path": cmd}})
|
|
56
|
+
for m, sh in cmds:
|
|
57
|
+
if m and tool not in m:
|
|
58
|
+
continue
|
|
59
|
+
t = tempfile.NamedTemporaryFile("w", suffix=".sh", delete=False, encoding="utf-8")
|
|
60
|
+
t.write(sh); t.close()
|
|
61
|
+
try:
|
|
62
|
+
r = subprocess.run(["bash", t.name], input=req, capture_output=True,
|
|
63
|
+
text=True, timeout=15)
|
|
64
|
+
if r.returncode == 2:
|
|
65
|
+
print("block"); raise SystemExit
|
|
66
|
+
finally:
|
|
67
|
+
os.unlink(t.name)
|
|
68
|
+
print("allow")
|
|
69
|
+
PY
|
|
70
|
+
)
|
|
71
|
+
if [ "$got" = "$want" ]; then
|
|
72
|
+
echo "PASS: $desc"; PASS=$((PASS+1))
|
|
73
|
+
else
|
|
74
|
+
echo "FAIL: $desc (expected $want, got $got)"; FAIL=$((FAIL+1))
|
|
75
|
+
fi
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
TARGET="/var/app"
|
|
79
|
+
|
|
80
|
+
# --- rm, whatever shape the flags arrive in ---
|
|
81
|
+
check "rm -rf" "rm -rf $TARGET" block
|
|
82
|
+
check "rm -fr" "rm -fr $TARGET" block
|
|
83
|
+
check "flags split" "rm -r -f $TARGET" block
|
|
84
|
+
check "flags split, other order" "rm -f -r $TARGET" block
|
|
85
|
+
check "long flags" "rm --recursive --force $TARGET" block
|
|
86
|
+
check "long flags, other order" "rm --force --recursive $TARGET" block
|
|
87
|
+
check "flags after the target" "rm $TARGET -rf" block
|
|
88
|
+
|
|
89
|
+
# --- git reset --hard, wherever the flag sits ---
|
|
90
|
+
check "reset, canonical" "git reset --hard HEAD~1" block
|
|
91
|
+
check "reset, flag last" "git reset HEAD~1 --hard" block
|
|
92
|
+
check "reset --soft is allowed" "git reset --soft HEAD~1" allow
|
|
93
|
+
|
|
94
|
+
# --- git clean: destructive blocked, dry runs kept ---
|
|
95
|
+
check "clean -fd" "git clean -fd" block
|
|
96
|
+
check "clean, flags split" "git clean -x -f -d" block
|
|
97
|
+
check "clean, long flag" "git clean --force -d" block
|
|
98
|
+
check "dry run" "git clean -n" allow
|
|
99
|
+
check "dry run with -d" "git clean -nd" allow
|
|
100
|
+
check "dry run with -d and -x" "git clean -ndx" allow
|
|
101
|
+
check "long dry run" "git clean --dry-run" allow
|
|
102
|
+
check "force plus dry run" "git clean --force --dry-run" allow
|
|
103
|
+
|
|
104
|
+
# --- branch deletion, both spellings ---
|
|
105
|
+
check "branch -D" "git branch -D main" block
|
|
106
|
+
check "branch --delete --force" "git branch --delete --force main" block
|
|
107
|
+
check "safe delete is allowed" "git branch -d old-feature" allow
|
|
108
|
+
|
|
109
|
+
# --- what the bundles already got right, kept ---
|
|
110
|
+
check "force push" "git push --force origin main" block
|
|
111
|
+
check "force push short flag" "git push origin main -f" block
|
|
112
|
+
check "--force-with-lease allowed" "git push --force-with-lease origin f" allow
|
|
113
|
+
check "push to main" "git push origin main" block
|
|
114
|
+
check "npm publish" "npm publish" block
|
|
115
|
+
check "writing .env" ".env" block Write
|
|
116
|
+
check ".env.example allowed" ".env.example" allow Write
|
|
117
|
+
|
|
118
|
+
# --- controls ---
|
|
119
|
+
check "ls" "ls -la" allow
|
|
120
|
+
check "git status" "git status" allow
|
|
121
|
+
check "npm test" "npm test" allow
|
|
122
|
+
check "echo" "echo hello" allow
|
|
123
|
+
|
|
124
|
+
echo
|
|
125
|
+
echo "plugins-word-order: $PASS passed, $FAIL failed"
|
|
126
|
+
[ "$FAIL" -eq 0 ]
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Tests for powershell-remove-item-guard.sh
|
|
3
|
+
# Run: bash tests/powershell-remove-item-guard.test.sh
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
PASS=0
|
|
7
|
+
FAIL=0
|
|
8
|
+
HOOK="$(dirname "$0")/../examples/powershell-remove-item-guard.sh"
|
|
9
|
+
|
|
10
|
+
# The verdict is the exit code, not the text. A refusal that only prints a word
|
|
11
|
+
# Claude Code does not act on is not a refusal: until 2026-09-19 this file checked
|
|
12
|
+
# for the string '"decision":"DENY"' in stdout, and passed all nine block cases
|
|
13
|
+
# while the guard refused none of them. One of the nine (C:\Users) was measured
|
|
14
|
+
# end to end against a do-nothing control; the other eight leave through the same
|
|
15
|
+
# two lines.
|
|
16
|
+
test_hook() {
|
|
17
|
+
local input="$1" expected="$2" desc="$3"
|
|
18
|
+
local out err exit_code=0
|
|
19
|
+
err=$(mktemp)
|
|
20
|
+
out=$(echo "$input" | bash "$HOOK" 2>"$err") || exit_code=$?
|
|
21
|
+
local stderr_text
|
|
22
|
+
stderr_text=$(cat "$err"); rm -f "$err"
|
|
23
|
+
|
|
24
|
+
if [ "$expected" = "DENY" ]; then
|
|
25
|
+
# exit 2 is what actually stops the tool call; the reason must reach stderr.
|
|
26
|
+
if [ "$exit_code" -eq 2 ] && echo "$stderr_text" | grep -q 'BLOCKED'; then
|
|
27
|
+
echo " PASS: $desc"
|
|
28
|
+
PASS=$((PASS + 1))
|
|
29
|
+
else
|
|
30
|
+
echo " FAIL: $desc (expected exit 2 + BLOCKED on stderr, got exit $exit_code, stdout: $out, stderr: $stderr_text)"
|
|
31
|
+
FAIL=$((FAIL + 1))
|
|
32
|
+
fi
|
|
33
|
+
elif [ "$expected" = "ASK" ]; then
|
|
34
|
+
if [ "$exit_code" -eq 0 ] && echo "$out" | grep -q '"permissionDecision":"ask"'; then
|
|
35
|
+
echo " PASS: $desc"
|
|
36
|
+
PASS=$((PASS + 1))
|
|
37
|
+
else
|
|
38
|
+
echo " FAIL: $desc (expected exit 0 + ask, got exit $exit_code, stdout: $out)"
|
|
39
|
+
FAIL=$((FAIL + 1))
|
|
40
|
+
fi
|
|
41
|
+
else
|
|
42
|
+
if [ "$exit_code" -eq 0 ] && ! echo "$out" | grep -q '"permissionDecision":"ask"'; then
|
|
43
|
+
echo " PASS: $desc"
|
|
44
|
+
PASS=$((PASS + 1))
|
|
45
|
+
else
|
|
46
|
+
echo " FAIL: $desc (expected exit 0 and no verdict, got exit $exit_code, stdout: $out)"
|
|
47
|
+
FAIL=$((FAIL + 1))
|
|
48
|
+
fi
|
|
49
|
+
fi
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
echo "powershell-remove-item-guard.sh tests"
|
|
53
|
+
echo ""
|
|
54
|
+
|
|
55
|
+
# --- Should DENY: system directory targets ---
|
|
56
|
+
test_hook '{"tool_input":{"command":"powershell -c \"Remove-Item -Recurse -Force C:\\Users\\john\""}}' DENY "Block Remove-Item on C:\\Users"
|
|
57
|
+
test_hook '{"tool_input":{"command":"powershell Remove-Item -Recurse /mnt/c/Windows"}}' DENY "Block Remove-Item on /mnt/c/Windows"
|
|
58
|
+
test_hook '{"tool_input":{"command":"Remove-Item -Recurse -Force \"C:\\Program Files\\app\""}}' DENY "Block Remove-Item on Program Files"
|
|
59
|
+
|
|
60
|
+
# --- Should DENY: node_modules junction traversal ---
|
|
61
|
+
test_hook '{"tool_input":{"command":"Remove-Item -Recurse -Force ./node_modules"}}' DENY "Block Remove-Item -Force on node_modules"
|
|
62
|
+
test_hook '{"tool_input":{"command":"Remove-Item -Recurse -Force .pnpm/store"}}' DENY "Block Remove-Item on .pnpm"
|
|
63
|
+
test_hook '{"tool_input":{"command":"Remove-Item -Recurse -Force worktree/packages"}}' DENY "Block Remove-Item on worktree"
|
|
64
|
+
|
|
65
|
+
# --- Should DENY: home directory ---
|
|
66
|
+
test_hook '{"tool_input":{"command":"Remove-Item -Recurse -Force $HOME/Documents"}}' DENY "Block Remove-Item on \$HOME"
|
|
67
|
+
test_hook '{"tool_input":{"command":"Remove-Item -Recurse $env:USERPROFILE"}}' DENY "Block Remove-Item on USERPROFILE"
|
|
68
|
+
test_hook '{"tool_input":{"command":"Remove-Item -Recurse -Force ~/projects"}}' DENY "Block Remove-Item on ~/"
|
|
69
|
+
|
|
70
|
+
# --- Should ASK: -Recurse -Force on an ordinary absolute data path (#64310) ---
|
|
71
|
+
test_hook '{"tool_input":{"command":"Remove-Item -Recurse -Force D:\\Clientes\\Yandy\\ENTREGABLES"}}' ASK "Confirm Remove-Item -Force on D:\\ client data (#64310)"
|
|
72
|
+
test_hook '{"tool_input":{"command":"Remove-Item -Force -Recurse E:\\work\\deliverables"}}' ASK "Confirm regardless of flag order on E:\\"
|
|
73
|
+
test_hook '{"tool_input":{"command":"Remove-Item -Recurse -Force \\\\nas\\share\\projects"}}' ASK "Confirm on UNC path"
|
|
74
|
+
|
|
75
|
+
# --- Should ALLOW: safe commands ---
|
|
76
|
+
test_hook '{"tool_input":{"command":"Remove-Item ./temp.txt"}}' ALLOW "Allow single file deletion"
|
|
77
|
+
test_hook '{"tool_input":{"command":"ls -la"}}' ALLOW "Allow non-Remove-Item command"
|
|
78
|
+
test_hook '{"tool_input":{"command":"rm -rf ./build"}}' ALLOW "Allow bash rm (different hook)"
|
|
79
|
+
test_hook '{"tool_input":{"command":"Get-ChildItem -Recurse"}}' ALLOW "Allow Get-ChildItem (read-only)"
|
|
80
|
+
test_hook '{"tool_input":{"command":"Remove-Item -Recurse -Force ./dist"}}' ALLOW "Allow relative force-delete (no absolute path)"
|
|
81
|
+
test_hook '{"tool_input":{"command":"Remove-Item -Recurse -Force .\\build\\out"}}' ALLOW "Allow relative Windows force-delete"
|
|
82
|
+
|
|
83
|
+
echo ""
|
|
84
|
+
echo "Results: $PASS passed, $FAIL failed"
|
|
85
|
+
[ "$FAIL" -eq 0 ] && exit 0 || exit 1
|