cc-safe-setup 29.6.32 → 29.6.36
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/COOKBOOK.md +70 -0
- package/README.md +6 -2
- package/examples/absolute-rule-enforcer.sh +42 -0
- package/examples/allow-claude-settings.sh +2 -0
- package/examples/allow-git-hooks-dir.sh +2 -0
- package/examples/allow-protected-dirs.sh +2 -0
- package/examples/allowlist.sh +2 -0
- package/examples/ansible-vault-guard.sh +2 -0
- package/examples/auto-approve-build.sh +2 -0
- package/examples/auto-approve-compound-git.sh +2 -0
- package/examples/auto-approve-docker.sh +2 -0
- package/examples/auto-approve-git-read.sh +2 -0
- package/examples/auto-approve-python.sh +2 -0
- package/examples/auto-approve-readonly-tools.sh +2 -0
- package/examples/auto-approve-ssh.sh +2 -0
- package/examples/auto-approve-test.sh +2 -0
- package/examples/auto-checkpoint.sh +2 -0
- package/examples/auto-git-checkpoint.sh +2 -0
- package/examples/auto-mode-safe-commands.sh +2 -0
- package/examples/auto-snapshot.sh +2 -0
- package/examples/backup-before-refactor.sh +2 -0
- package/examples/banned-command-guard.sh +3 -3
- package/examples/bash-domain-allowlist.sh +72 -0
- package/examples/bash-safety-auto-deny.sh +56 -0
- package/examples/bash-secret-output-detector.sh +68 -0
- package/examples/bash-timeout-guard.sh +2 -0
- package/examples/bashrc-safety-check.sh +59 -0
- package/examples/bg-task-cooldown-guard.sh +46 -0
- package/examples/block-database-wipe.sh +2 -0
- package/examples/branch-name-check.sh +2 -0
- package/examples/branch-naming-convention.sh +2 -0
- package/examples/cargo-publish-guard.sh +2 -0
- package/examples/check-abort-controller.sh +2 -0
- package/examples/check-accessibility.sh +2 -0
- package/examples/check-aria-labels.sh +2 -0
- package/examples/check-async-await-consistency.sh +2 -0
- package/examples/check-before-act-enforcer.sh +47 -0
- package/examples/check-charset-meta.sh +2 -0
- package/examples/check-cleanup-effect.sh +2 -0
- package/examples/check-content-type.sh +2 -0
- package/examples/check-controlled-input.sh +2 -0
- package/examples/check-cookie-flags.sh +2 -0
- package/examples/check-cors-config.sh +2 -0
- package/examples/check-csp-headers.sh +2 -0
- package/examples/check-csrf-protection.sh +2 -0
- package/examples/check-debounce.sh +2 -0
- package/examples/check-dependency-age.sh +2 -0
- package/examples/check-dependency-license.sh +2 -0
- package/examples/check-dockerfile-best-practice.sh +2 -0
- package/examples/check-error-boundaries.sh +2 -0
- package/examples/check-error-class.sh +2 -0
- package/examples/check-error-handling.sh +2 -0
- package/examples/check-error-logging.sh +2 -0
- package/examples/check-error-message.sh +2 -0
- package/examples/check-error-page.sh +2 -0
- package/examples/check-error-stack.sh +2 -0
- package/examples/check-favicon.sh +2 -0
- package/examples/check-form-validation.sh +2 -0
- package/examples/check-git-hooks-compat.sh +2 -0
- package/examples/check-https-redirect.sh +2 -0
- package/examples/check-image-optimization.sh +2 -0
- package/examples/check-input-validation.sh +2 -0
- package/examples/check-key-prop.sh +2 -0
- package/examples/check-lang-attribute.sh +2 -0
- package/examples/check-lazy-loading.sh +2 -0
- package/examples/check-loading-state.sh +2 -0
- package/examples/check-memo-deps.sh +2 -0
- package/examples/check-meta-description.sh +2 -0
- package/examples/check-npm-scripts-exist.sh +2 -0
- package/examples/check-null-check.sh +2 -0
- package/examples/check-package-size.sh +2 -0
- package/examples/check-pagination.sh +2 -0
- package/examples/check-port-availability.sh +2 -0
- package/examples/check-promise-all.sh +2 -0
- package/examples/check-prop-types.sh +2 -0
- package/examples/check-rate-limiting.sh +2 -0
- package/examples/check-responsive-design.sh +2 -0
- package/examples/check-retry-logic.sh +2 -0
- package/examples/check-return-types.sh +2 -0
- package/examples/check-semantic-html.sh +2 -0
- package/examples/check-semantic-versioning.sh +2 -0
- package/examples/check-suspense-fallback.sh +2 -0
- package/examples/check-test-naming.sh +2 -0
- package/examples/check-timeout-cleanup.sh +2 -0
- package/examples/check-tls-version.sh +2 -0
- package/examples/check-type-coercion.sh +2 -0
- package/examples/check-unsubscribe.sh +2 -0
- package/examples/check-viewport-meta.sh +2 -0
- package/examples/check-worker-terminate.sh +2 -0
- package/examples/checkpoint-tamper-guard.sh +2 -0
- package/examples/chmod-guard.sh +2 -0
- package/examples/chown-guard.sh +2 -0
- package/examples/ci-workflow-guard.sh +59 -0
- package/examples/classifier-fallback-allow.sh +2 -0
- package/examples/claude-cache-gc.sh +15 -0
- package/examples/claudeignore-enforce-guard.sh +60 -0
- package/examples/claudemd-enforcer.sh +2 -0
- package/examples/claudemd-violation-detector.sh +36 -0
- package/examples/clear-command-confirm-guard.sh +21 -0
- package/examples/commit-message-check.sh +2 -0
- package/examples/compact-blocker.sh +25 -0
- package/examples/composer-guard.sh +2 -0
- package/examples/compound-command-allow.sh +2 -0
- package/examples/consecutive-failure-circuit-breaker.sh +49 -0
- package/examples/console-log-count.sh +2 -0
- package/examples/context-compact-advisor.sh +16 -0
- package/examples/core-file-protect-guard.sh +91 -0
- package/examples/cors-star-warn.sh +2 -0
- package/examples/credential-exfil-guard.sh +2 -0
- package/examples/credential-file-cat-guard.sh +2 -0
- package/examples/cron-modification-guard.sh +40 -0
- package/examples/cwd-drift-detector.sh +47 -0
- package/examples/cwd-project-boundary-guard.sh +50 -0
- package/examples/denied-action-retry-guard.sh +41 -0
- package/examples/dependency-install-guard.sh +2 -0
- package/examples/deploy-guard.sh +2 -0
- package/examples/deploy-path-verify-guard.sh +62 -0
- package/examples/deployment-verify-guard.sh +81 -0
- package/examples/django-migrate-guard.sh +2 -0
- package/examples/docker-volume-guard.sh +2 -0
- package/examples/dockerfile-latest-guard.sh +2 -0
- package/examples/dotenv-commit-guard.sh +44 -0
- package/examples/dotenv-example-sync.sh +55 -0
- package/examples/dotnet-build-on-edit.sh +2 -0
- package/examples/drizzle-migrate-guard.sh +2 -0
- package/examples/edit-counter-test-gate.sh +44 -0
- package/examples/edit-error-counter.sh +2 -0
- package/examples/edit-guard.sh +2 -0
- package/examples/edit-old-string-validator.sh +37 -0
- package/examples/edit-retry-loop-guard.sh +2 -0
- package/examples/edit-verify.sh +2 -0
- package/examples/encoding-preserve-guard.sh +34 -0
- package/examples/enforce-tests.sh +2 -0
- package/examples/env-inherit-guard.sh +2 -0
- package/examples/env-inline-secret-guard.sh +36 -0
- package/examples/env-prod-guard.sh +2 -0
- package/examples/env-required-check.sh +2 -0
- package/examples/env-var-check.sh +2 -0
- package/examples/expo-eject-guard.sh +2 -0
- package/examples/export-overwrite-guard.sh +29 -0
- package/examples/file-change-tracker.sh +2 -0
- package/examples/file-change-undo-tracker.sh +46 -0
- package/examples/file-recycle-bin.sh +48 -0
- package/examples/file-size-limit.sh +2 -0
- package/examples/five-hundred-milestone.sh +2 -0
- package/examples/flask-debug-guard.sh +2 -0
- package/examples/gem-push-guard.sh +2 -0
- package/examples/git-checkout-safety-guard.sh +2 -0
- package/examples/git-config-guard.sh +2 -0
- package/examples/git-crypt-worktree-guard.sh +36 -0
- package/examples/git-hook-bypass-guard.sh +2 -0
- package/examples/git-merge-conflict-prevent.sh +2 -0
- package/examples/git-message-length.sh +2 -0
- package/examples/git-operations-require-approval.sh +99 -0
- package/examples/git-show-flag-sanitizer.sh +41 -0
- package/examples/git-stash-before-danger.sh +2 -0
- package/examples/git-submodule-guard.sh +2 -0
- package/examples/git-tag-guard.sh +2 -0
- package/examples/github-actions-secret-guard.sh +59 -0
- package/examples/gitignore-check.sh +2 -0
- package/examples/gitops-drift-guard.sh +53 -0
- package/examples/go-mod-tidy-warn.sh +2 -0
- package/examples/hallucination-url-check.sh +2 -0
- package/examples/hardcoded-ip-guard.sh +2 -0
- package/examples/headless-empty-result-guard.sh +46 -0
- package/examples/headless-stop-guard.sh +43 -0
- package/examples/helm-install-guard.sh +2 -0
- package/examples/issue-draft-redact-guard.sh +45 -0
- package/examples/java-compile-on-edit.sh +2 -0
- package/examples/k8s-production-guard.sh +77 -0
- package/examples/laravel-artisan-guard.sh +2 -0
- package/examples/large-file-guard.sh +2 -0
- package/examples/line-ending-guard.sh +30 -0
- package/examples/log-level-guard.sh +2 -0
- package/examples/magic-number-warn.sh +2 -0
- package/examples/max-edit-size-guard.sh +2 -0
- package/examples/max-file-count-guard.sh +2 -0
- package/examples/max-file-delete-count.sh +2 -0
- package/examples/max-function-length.sh +2 -0
- package/examples/max-import-count.sh +2 -0
- package/examples/max-subagent-count.sh +2 -0
- package/examples/mcp-orphan-process-guard.sh +39 -0
- package/examples/mcp-server-allowlist.sh +45 -0
- package/examples/mcp-tool-audit-log.sh +41 -0
- package/examples/mcp-tool-guard.sh +2 -0
- package/examples/migration-verify-guard.sh +44 -0
- package/examples/monorepo-scope-guard.sh +2 -0
- package/examples/network-exfil-guard.sh +61 -0
- package/examples/network-guard.sh +2 -0
- package/examples/nextjs-env-guard.sh +2 -0
- package/examples/no-absolute-import.sh +2 -0
- package/examples/no-alert-confirm-prompt.sh +2 -0
- package/examples/no-any-type.sh +2 -0
- package/examples/no-any-typescript.sh +2 -0
- package/examples/no-assignment-in-condition.sh +2 -0
- package/examples/no-callback-hell.sh +2 -0
- package/examples/no-catch-all-route.sh +2 -0
- package/examples/no-circular-dependency.sh +2 -0
- package/examples/no-class-in-functional.sh +2 -0
- package/examples/no-cleartext-storage.sh +2 -0
- package/examples/no-commented-code.sh +2 -0
- package/examples/no-commit-fixup.sh +2 -0
- package/examples/no-console-assert.sh +2 -0
- package/examples/no-console-error-swallow.sh +2 -0
- package/examples/no-console-in-prod.sh +2 -0
- package/examples/no-console-log.sh +2 -0
- package/examples/no-console-time.sh +2 -0
- package/examples/no-cors-wildcard.sh +2 -0
- package/examples/no-curl-upload.sh +2 -0
- package/examples/no-dangerouslySetInnerHTML.sh +2 -0
- package/examples/no-dangling-await.sh +2 -0
- package/examples/no-debug-in-commit.sh +2 -0
- package/examples/no-deep-nesting.sh +2 -0
- package/examples/no-deep-relative-import.sh +2 -0
- package/examples/no-default-credentials.sh +2 -0
- package/examples/no-deprecated-api.sh +2 -0
- package/examples/no-direct-dom-manipulation.sh +2 -0
- package/examples/no-disabled-test.sh +2 -0
- package/examples/no-document-cookie.sh +2 -0
- package/examples/no-document-write.sh +2 -0
- package/examples/no-empty-function.sh +2 -0
- package/examples/no-eval-in-template.sh +2 -0
- package/examples/no-eval-template.sh +2 -0
- package/examples/no-eval.sh +2 -0
- package/examples/no-exec-user-input.sh +2 -0
- package/examples/no-expose-internal-ids.sh +2 -0
- package/examples/no-floating-promises.sh +2 -0
- package/examples/no-force-install.sh +2 -0
- package/examples/no-git-rebase-public.sh +2 -0
- package/examples/no-global-state.sh +2 -0
- package/examples/no-hardcoded-port.sh +2 -0
- package/examples/no-hardcoded-url.sh +2 -0
- package/examples/no-helmet-missing.sh +2 -0
- package/examples/no-http-url.sh +2 -0
- package/examples/no-http-without-https.sh +2 -0
- package/examples/no-index-as-key.sh +2 -0
- package/examples/no-infinite-scroll-mem.sh +2 -0
- package/examples/no-inline-event-handler.sh +2 -0
- package/examples/no-inline-handler.sh +2 -0
- package/examples/no-inline-style.sh +2 -0
- package/examples/no-inline-styles.sh +2 -0
- package/examples/no-innerhtml.sh +2 -0
- package/examples/no-install-global.sh +2 -0
- package/examples/no-jwt-in-url.sh +2 -0
- package/examples/no-large-commit.sh +2 -0
- package/examples/no-localhost-expose.sh +2 -0
- package/examples/no-long-switch.sh +2 -0
- package/examples/no-magic-number.sh +2 -0
- package/examples/no-md5-sha1.sh +2 -0
- package/examples/no-memory-leak-interval.sh +2 -0
- package/examples/no-mixed-line-endings.sh +2 -0
- package/examples/no-mutation-in-reducer.sh +2 -0
- package/examples/no-mutation-observer-leak.sh +2 -0
- package/examples/no-nested-subscribe.sh +2 -0
- package/examples/no-nested-ternary.sh +2 -0
- package/examples/no-network-exfil.sh +2 -0
- package/examples/no-new-array-fill.sh +2 -0
- package/examples/no-object-freeze-mutation.sh +2 -0
- package/examples/no-open-redirect.sh +2 -0
- package/examples/no-output-truncation.sh +44 -0
- package/examples/no-package-downgrade.sh +2 -0
- package/examples/no-package-lock-edit.sh +2 -0
- package/examples/no-path-join-user-input.sh +2 -0
- package/examples/no-port-bind.sh +2 -0
- package/examples/no-process-exit.sh +2 -0
- package/examples/no-prototype-pollution.sh +2 -0
- package/examples/no-push-without-ci.sh +2 -0
- package/examples/no-raw-ref.sh +2 -0
- package/examples/no-redundant-fragment.sh +2 -0
- package/examples/no-render-in-loop.sh +2 -0
- package/examples/no-root-user-docker.sh +2 -0
- package/examples/no-root-write.sh +2 -0
- package/examples/no-secrets-in-args.sh +2 -0
- package/examples/no-secrets-in-logs.sh +2 -0
- package/examples/no-sensitive-log.sh +2 -0
- package/examples/no-side-effects-in-render.sh +2 -0
- package/examples/no-sleep-in-hooks.sh +2 -0
- package/examples/no-star-import-python.sh +2 -0
- package/examples/no-string-concat-sql.sh +2 -0
- package/examples/no-sudo-guard.sh +2 -0
- package/examples/no-sync-external-call.sh +2 -0
- package/examples/no-sync-fs.sh +2 -0
- package/examples/no-table-layout.sh +2 -0
- package/examples/no-throw-string.sh +2 -0
- package/examples/no-todo-in-merge.sh +2 -0
- package/examples/no-todo-in-production.sh +2 -0
- package/examples/no-todo-without-issue.sh +2 -0
- package/examples/no-triple-slash-ref.sh +2 -0
- package/examples/no-unreachable-code.sh +2 -0
- package/examples/no-unused-import.sh +2 -0
- package/examples/no-unused-state.sh +2 -0
- package/examples/no-var-keyword.sh +2 -0
- package/examples/no-wildcard-cors.sh +2 -0
- package/examples/no-wildcard-import.sh +2 -0
- package/examples/no-window-location.sh +2 -0
- package/examples/no-with-statement.sh +2 -0
- package/examples/no-write-outside-src.sh +2 -0
- package/examples/no-xml-external-entity.sh +2 -0
- package/examples/notify-waiting.sh +2 -0
- package/examples/npm-audit-warn.sh +2 -0
- package/examples/npm-publish-guard.sh +2 -0
- package/examples/npm-script-injection.sh +2 -0
- package/examples/npm-supply-chain-guard.sh +92 -0
- package/examples/nuxt-config-guard.sh +2 -0
- package/examples/output-secret-mask.sh +2 -0
- package/examples/package-json-guard.sh +2 -0
- package/examples/parallel-session-guard.sh +2 -0
- package/examples/path-traversal-guard.sh +2 -0
- package/examples/permission-audit-log.sh +2 -0
- package/examples/permission-entry-validator.sh +48 -0
- package/examples/permission-pattern-auto-allow.sh +50 -0
- package/examples/php-lint-on-edit.sh +2 -0
- package/examples/pip-publish-guard.sh +2 -0
- package/examples/plain-language-danger-warn.sh +37 -0
- package/examples/plan-mode-enforcer.sh +2 -0
- package/examples/plugin-process-cleanup.sh +50 -0
- package/examples/polyglot-rm-guard.sh +59 -0
- package/examples/pr-description-check.sh +2 -0
- package/examples/pre-compact-knowledge-save.sh +53 -0
- package/examples/pre-compact-transcript-export.sh +85 -0
- package/examples/prefer-builtin-tools.sh +2 -0
- package/examples/prefer-const.sh +2 -0
- package/examples/prefer-dedicated-tools.sh +55 -0
- package/examples/prefer-optional-chaining.sh +2 -0
- package/examples/prisma-migrate-guard.sh +2 -0
- package/examples/prompt-injection-detector.sh +2 -0
- package/examples/prompt-length-guard.sh +2 -0
- package/examples/protect-dotfiles.sh +2 -0
- package/examples/public-repo-push-guard.sh +58 -0
- package/examples/push-requires-test-pass-record.sh +2 -0
- package/examples/push-requires-test-pass.sh +2 -0
- package/examples/rails-migration-guard.sh +2 -0
- package/examples/rate-limit-guard.sh +2 -0
- package/examples/read-all-files-enforcer.sh +51 -0
- package/examples/read-audit-log.sh +34 -0
- package/examples/readme-exists-check.sh +2 -0
- package/examples/redis-flushall-guard.sh +2 -0
- package/examples/rm-safety-net.sh +2 -0
- package/examples/role-tool-guard.sh +69 -0
- package/examples/ruby-lint-on-edit.sh +2 -0
- package/examples/schema-migration-guard.sh +57 -0
- package/examples/scope-guard.sh +2 -0
- package/examples/secret-file-read-guard.sh +74 -0
- package/examples/self-modify-bypass-guard.sh +54 -0
- package/examples/sensitive-log-guard.sh +2 -0
- package/examples/session-checkpoint.sh +2 -0
- package/examples/session-duration-guard.sh +51 -0
- package/examples/session-end-logger.sh +57 -0
- package/examples/session-error-rate-monitor.sh +65 -0
- package/examples/session-health-monitor.sh +61 -0
- package/examples/session-memory-watchdog.sh +17 -0
- package/examples/session-permission-reset-guard.sh +39 -0
- package/examples/session-resume-env-fix.sh +49 -0
- package/examples/session-state-saver.sh +2 -0
- package/examples/session-summary-stop.sh +2 -0
- package/examples/session-summary.sh +2 -0
- package/examples/session-token-counter.sh +2 -0
- package/examples/settings-auto-backup.sh +53 -0
- package/examples/settings-mutation-detector.sh +45 -0
- package/examples/shell-wrapper-guard.sh +2 -0
- package/examples/skill-gate.sh +2 -0
- package/examples/skill-injection-detector.sh +41 -0
- package/examples/spec-file-scope-guard.sh +69 -0
- package/examples/spring-profile-guard.sh +2 -0
- package/examples/sql-injection-detect.sh +2 -0
- package/examples/subagent-budget-guard.sh +2 -0
- package/examples/subagent-claudemd-inject.sh +45 -0
- package/examples/subagent-context-size-guard.sh +26 -0
- package/examples/subagent-tool-call-limiter.sh +48 -0
- package/examples/svelte-lint-on-edit.sh +2 -0
- package/examples/swift-build-on-edit.sh +2 -0
- package/examples/symlink-protect.sh +12 -0
- package/examples/system-message-workaround.sh +44 -0
- package/examples/system-package-guard.sh +2 -0
- package/examples/temp-file-cleanup-stop.sh +28 -0
- package/examples/temp-file-cleanup.sh +2 -0
- package/examples/terminal-state-restore.sh +23 -0
- package/examples/test-after-edit.sh +2 -0
- package/examples/test-before-commit.sh +13 -14
- package/examples/test-before-push.sh +2 -0
- package/examples/test-exit-code-verify.sh +2 -0
- package/examples/timeout-guard.sh +2 -0
- package/examples/timezone-guard.sh +2 -0
- package/examples/tmp-output-size-guard.sh +46 -0
- package/examples/todo-check.sh +2 -0
- package/examples/todo-deadline-warn.sh +48 -0
- package/examples/token-budget-per-task.sh +55 -0
- package/examples/token-spike-alert.sh +51 -0
- package/examples/token-usage-tracker.sh +14 -0
- package/examples/turbo-cache-guard.sh +2 -0
- package/examples/uncommitted-changes-stop.sh +2 -0
- package/examples/uncommitted-work-shield.sh +37 -0
- package/examples/usage-warn.sh +2 -0
- package/examples/verify-before-commit.sh +2 -0
- package/examples/virtual-cwd-helper.sh +40 -0
- package/examples/vue-lint-on-edit.sh +2 -0
- package/examples/webfetch-domain-allow.sh +96 -0
- package/examples/worktree-delete-guard.sh +43 -0
- package/examples/worktree-memory-guard.sh +47 -0
- package/examples/worktree-path-validator.sh +42 -0
- package/examples/worktree-project-unify.sh +19 -0
- package/examples/worktree-unmerged-guard.sh +2 -0
- package/examples/write-overwrite-confirm.sh +40 -0
- package/examples/write-secret-guard.sh +2 -0
- package/examples/write-shrink-guard.sh +46 -0
- package/examples/write-test-ratio.sh +2 -0
- package/index.mjs +631 -138
- package/package.json +2 -2
- package/scripts/generate-categories.mjs +206 -0
- package/scripts.json +4 -1
- package/test.sh.new_tests +0 -0
- package/test.sh.patch +0 -0
- package/tests/test-core-file-protect-guard.sh +73 -0
- package/tests/test-deployment-verify-guard.sh +74 -0
- package/tests/test-git-operations-require-approval.sh +65 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# file-recycle-bin.sh — Move deleted files to recycle bin instead of permanent deletion
|
|
3
|
+
#
|
|
4
|
+
# Solves: No undo for file operations during Claude Code sessions (#39949).
|
|
5
|
+
# When Claude deletes or overwrites files, they're gone permanently
|
|
6
|
+
# unless git tracked. This hook intercepts rm commands and moves
|
|
7
|
+
# files to a session recycle bin.
|
|
8
|
+
#
|
|
9
|
+
# How it works: PreToolUse hook on Bash that intercepts rm commands,
|
|
10
|
+
# copies target files to .claude/recycle-bin/ before allowing deletion.
|
|
11
|
+
# Restore with: cp .claude/recycle-bin/<file> <original-path>
|
|
12
|
+
#
|
|
13
|
+
# TRIGGER: PreToolUse
|
|
14
|
+
# MATCHER: "Bash"
|
|
15
|
+
|
|
16
|
+
set -euo pipefail
|
|
17
|
+
|
|
18
|
+
INPUT=$(cat)
|
|
19
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
20
|
+
[ -z "$COMMAND" ] && exit 0
|
|
21
|
+
|
|
22
|
+
# Only intercept rm commands (not rm -rf which destructive-guard handles)
|
|
23
|
+
if ! echo "$COMMAND" | grep -qE '^\s*rm\s'; then
|
|
24
|
+
exit 0
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
# Skip if destructive-guard would catch it (rm -rf /, rm -rf ~, etc.)
|
|
28
|
+
if echo "$COMMAND" | grep -qE 'rm\s+(-[rf]+\s+)*(/|~|\$HOME)'; then
|
|
29
|
+
exit 0 # Let destructive-guard handle these
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
# Extract file paths from rm command (simple extraction)
|
|
33
|
+
FILES=$(echo "$COMMAND" | sed 's/^[[:space:]]*rm[[:space:]]*//' | sed 's/-[rfiv]*//g' | tr ' ' '\n' | grep -v '^$' | grep -v '^-')
|
|
34
|
+
|
|
35
|
+
BIN_DIR=".claude/recycle-bin"
|
|
36
|
+
mkdir -p "$BIN_DIR"
|
|
37
|
+
|
|
38
|
+
for FILE in $FILES; do
|
|
39
|
+
if [ -f "$FILE" ]; then
|
|
40
|
+
BASENAME=$(basename "$FILE")
|
|
41
|
+
TIMESTAMP=$(date +%H%M%S)
|
|
42
|
+
cp "$FILE" "$BIN_DIR/${TIMESTAMP}-${BASENAME}" 2>/dev/null || true
|
|
43
|
+
echo "Backed up: $FILE -> $BIN_DIR/${TIMESTAMP}-${BASENAME}" >&2
|
|
44
|
+
fi
|
|
45
|
+
done
|
|
46
|
+
|
|
47
|
+
# Allow the rm to proceed (files are backed up)
|
|
48
|
+
exit 0
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# git-crypt-worktree-guard.sh — Block worktree creation in git-crypt repos
|
|
3
|
+
#
|
|
4
|
+
# Solves: When Claude creates a worktree in a git-crypt repo,
|
|
5
|
+
# the smudge filter fails because git-crypt hasn't been
|
|
6
|
+
# unlocked in the new worktree. This produces destructive
|
|
7
|
+
# commits that delete all encrypted files (#38538).
|
|
8
|
+
#
|
|
9
|
+
# How it works: Before git worktree add, checks if the repo
|
|
10
|
+
# uses git-crypt (.gitattributes contains filter=git-crypt).
|
|
11
|
+
# If yes, blocks the worktree creation with a warning.
|
|
12
|
+
#
|
|
13
|
+
# TRIGGER: PreToolUse
|
|
14
|
+
# MATCHER: "Bash"
|
|
15
|
+
|
|
16
|
+
set -euo pipefail
|
|
17
|
+
INPUT=$(cat)
|
|
18
|
+
|
|
19
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
20
|
+
[ -z "$COMMAND" ] && exit 0
|
|
21
|
+
|
|
22
|
+
# Only check git worktree add
|
|
23
|
+
if ! echo "$COMMAND" | grep -qE 'git\s+worktree\s+add'; then
|
|
24
|
+
exit 0
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
# Check if repo uses git-crypt
|
|
28
|
+
if [ -f ".gitattributes" ] && grep -q "filter=git-crypt" .gitattributes 2>/dev/null; then
|
|
29
|
+
echo "BLOCKED: Cannot create worktree in a git-crypt repo." >&2
|
|
30
|
+
echo "git-crypt is not automatically unlocked in new worktrees." >&2
|
|
31
|
+
echo "This would produce destructive commits that delete all encrypted files." >&2
|
|
32
|
+
echo "Work in the main repo instead, or manually run 'git-crypt unlock' in the worktree first." >&2
|
|
33
|
+
exit 2
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
exit 0
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# TRIGGER: PreToolUse MATCHER: "Bash"
|
|
2
4
|
COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
3
5
|
[ -z "$COMMAND" ] && exit 0
|
|
4
6
|
if echo "$COMMAND" | grep -qE "git\s+(commit|push|merge).*--no-verify"; then echo "WARNING: --no-verify bypasses git hooks" >&2; fi
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# TRIGGER: PreToolUse MATCHER: "Bash"
|
|
2
4
|
COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
3
5
|
[ -z "$COMMAND" ] && exit 0
|
|
4
6
|
echo "$COMMAND" | grep -qE "git\s+commit\s+-m" || exit 0; MSG=$(echo "$COMMAND" | grep -oP "(?<=-m\s[\x27\x22])[^\x27\x22]+"); [ ${#MSG} -lt 10 ] && echo "WARNING: Commit message too short (${#MSG} chars)" >&2
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ================================================================
|
|
3
|
+
# git-operations-require-approval.sh — Block git write operations
|
|
4
|
+
# ================================================================
|
|
5
|
+
# PURPOSE:
|
|
6
|
+
# Claude Code sometimes ignores CLAUDE.md rules about git commit,
|
|
7
|
+
# push, and branch creation — performing these operations without
|
|
8
|
+
# user approval. This hook enforces the restriction at process level.
|
|
9
|
+
#
|
|
10
|
+
# Blocks:
|
|
11
|
+
# git commit, git push (including --force), git checkout -b,
|
|
12
|
+
# git switch -c, git branch <name>
|
|
13
|
+
#
|
|
14
|
+
# Does NOT block:
|
|
15
|
+
# git status, git log, git diff, git show, git branch (list),
|
|
16
|
+
# git fetch, git stash, git add
|
|
17
|
+
#
|
|
18
|
+
# Handles compound commands (&&, ;, ||) by checking each segment.
|
|
19
|
+
#
|
|
20
|
+
# See: https://github.com/anthropics/claude-code/issues/40695
|
|
21
|
+
#
|
|
22
|
+
# TRIGGER: PreToolUse MATCHER: "Bash"
|
|
23
|
+
# ================================================================
|
|
24
|
+
|
|
25
|
+
INPUT=$(cat)
|
|
26
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
27
|
+
|
|
28
|
+
[ -z "$COMMAND" ] && exit 0
|
|
29
|
+
|
|
30
|
+
# Skip if the command is inside echo/printf (not actual execution)
|
|
31
|
+
echo "$COMMAND" | grep -qE '^\s*(echo|printf)\s' && exit 0
|
|
32
|
+
|
|
33
|
+
# Check each segment of compound commands
|
|
34
|
+
check_segment() {
|
|
35
|
+
local seg="$1"
|
|
36
|
+
# Trim whitespace
|
|
37
|
+
seg=$(echo "$seg" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
38
|
+
[ -z "$seg" ] && return 0
|
|
39
|
+
|
|
40
|
+
# git commit
|
|
41
|
+
if echo "$seg" | grep -qE '\bgit\s+commit\b'; then
|
|
42
|
+
echo "BLOCKED: git commit requires explicit user approval." >&2
|
|
43
|
+
echo "Command: $seg" >&2
|
|
44
|
+
echo "" >&2
|
|
45
|
+
echo "See: https://github.com/anthropics/claude-code/issues/40695" >&2
|
|
46
|
+
return 1
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
# git push (including force variants)
|
|
50
|
+
if echo "$seg" | grep -qE '\bgit\s+push\b'; then
|
|
51
|
+
echo "BLOCKED: git push requires explicit user approval." >&2
|
|
52
|
+
echo "Command: $seg" >&2
|
|
53
|
+
echo "" >&2
|
|
54
|
+
echo "See: https://github.com/anthropics/claude-code/issues/40695" >&2
|
|
55
|
+
return 1
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
# git checkout -b (branch creation)
|
|
59
|
+
if echo "$seg" | grep -qE '\bgit\s+checkout\s+(-b|--branch)\b'; then
|
|
60
|
+
echo "BLOCKED: git branch creation requires explicit user approval." >&2
|
|
61
|
+
echo "Command: $seg" >&2
|
|
62
|
+
return 1
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
# git switch -c / --create (branch creation)
|
|
66
|
+
if echo "$seg" | grep -qE '\bgit\s+switch\s+(-c|--create)\b'; then
|
|
67
|
+
echo "BLOCKED: git branch creation requires explicit user approval." >&2
|
|
68
|
+
echo "Command: $seg" >&2
|
|
69
|
+
return 1
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
# git branch <name> (creation, not listing)
|
|
73
|
+
# git branch without flags or with only -a/-r/-l/--list is listing
|
|
74
|
+
if echo "$seg" | grep -qE '\bgit\s+branch\s'; then
|
|
75
|
+
# Allow listing flags
|
|
76
|
+
if echo "$seg" | grep -qE '\bgit\s+branch\s+(-[arl]|--list|--merged|--no-merged|--contains|-v|--verbose|-d|--delete|-D)\b'; then
|
|
77
|
+
return 0
|
|
78
|
+
fi
|
|
79
|
+
# If it has a name argument after "git branch", it's creation
|
|
80
|
+
local args
|
|
81
|
+
args=$(echo "$seg" | sed 's/.*\bgit\s\+branch\s\+//')
|
|
82
|
+
if [ -n "$args" ] && ! echo "$args" | grep -qE '^\s*$'; then
|
|
83
|
+
echo "BLOCKED: git branch creation requires explicit user approval." >&2
|
|
84
|
+
echo "Command: $seg" >&2
|
|
85
|
+
return 1
|
|
86
|
+
fi
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
return 0
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
# Split on && ; || and check each part
|
|
93
|
+
while IFS= read -r segment; do
|
|
94
|
+
if ! check_segment "$segment"; then
|
|
95
|
+
exit 2
|
|
96
|
+
fi
|
|
97
|
+
done < <(echo "$COMMAND" | sed 's/&&/\n/g; s/;/\n/g; s/||/\n/g')
|
|
98
|
+
|
|
99
|
+
exit 0
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ================================================================
|
|
3
|
+
# git-show-flag-sanitizer.sh — Strip invalid --no-stat from git show
|
|
4
|
+
# ================================================================
|
|
5
|
+
# PURPOSE:
|
|
6
|
+
# Claude Code frequently runs `git show <ref> --no-stat`, but --no-stat
|
|
7
|
+
# is not a valid git-show flag. The command fails with exit code 128,
|
|
8
|
+
# wasting context on error output and retries.
|
|
9
|
+
# This hook silently rewrites the command to remove --no-stat.
|
|
10
|
+
#
|
|
11
|
+
# TRIGGER: PreToolUse
|
|
12
|
+
# MATCHER: "Bash"
|
|
13
|
+
#
|
|
14
|
+
# See: https://github.com/anthropics/claude-code/issues/13071
|
|
15
|
+
# ================================================================
|
|
16
|
+
|
|
17
|
+
INPUT=$(cat)
|
|
18
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
19
|
+
[ -z "$COMMAND" ] && exit 0
|
|
20
|
+
|
|
21
|
+
# Only act on git show commands containing --no-stat
|
|
22
|
+
case "$COMMAND" in
|
|
23
|
+
*git\ show*--no-stat*|*git\ show*--no-stat*) ;;
|
|
24
|
+
*) exit 0 ;;
|
|
25
|
+
esac
|
|
26
|
+
|
|
27
|
+
# Remove --no-stat flag (handles multiple spaces)
|
|
28
|
+
NEW_COMMAND=$(echo "$COMMAND" | sed 's/ --no-stat//')
|
|
29
|
+
|
|
30
|
+
# Collapse any double spaces left behind
|
|
31
|
+
NEW_COMMAND=$(echo "$NEW_COMMAND" | sed 's/ */ /g')
|
|
32
|
+
|
|
33
|
+
jq -n --arg cmd "$NEW_COMMAND" '{
|
|
34
|
+
hookSpecificOutput: {
|
|
35
|
+
hookEventName: "PreToolUse",
|
|
36
|
+
permissionDecision: "allow",
|
|
37
|
+
updatedInput: { command: $cmd }
|
|
38
|
+
}
|
|
39
|
+
}'
|
|
40
|
+
|
|
41
|
+
exit 0
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# github-actions-secret-guard.sh — Prevent hardcoded secrets in GitHub Actions workflows
|
|
3
|
+
#
|
|
4
|
+
# Solves: Claude writes API keys, tokens, or passwords directly into
|
|
5
|
+
# .github/workflows/*.yml files instead of using ${{ secrets.NAME }}.
|
|
6
|
+
# These get committed and pushed to public/private repos.
|
|
7
|
+
#
|
|
8
|
+
# How it works: PostToolUse hook on Edit/Write that checks if the target
|
|
9
|
+
# is a GitHub Actions workflow file, then scans for patterns that look
|
|
10
|
+
# like hardcoded secrets instead of ${{ secrets.* }} references.
|
|
11
|
+
#
|
|
12
|
+
# TRIGGER: PostToolUse
|
|
13
|
+
# MATCHER: "Edit|Write"
|
|
14
|
+
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
|
|
17
|
+
INPUT=$(cat)
|
|
18
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
19
|
+
case "$TOOL" in Edit|Write) ;; *) exit 0 ;; esac
|
|
20
|
+
|
|
21
|
+
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
|
|
22
|
+
[ -z "$FILE" ] && exit 0
|
|
23
|
+
|
|
24
|
+
# Only check GitHub Actions workflow files
|
|
25
|
+
case "$FILE" in
|
|
26
|
+
.github/workflows/*.yml|.github/workflows/*.yaml) ;;
|
|
27
|
+
*/.github/workflows/*.yml|*/.github/workflows/*.yaml) ;;
|
|
28
|
+
*) exit 0 ;;
|
|
29
|
+
esac
|
|
30
|
+
|
|
31
|
+
[ -f "$FILE" ] || exit 0
|
|
32
|
+
|
|
33
|
+
# Check for hardcoded secret patterns (not using ${{ secrets.* }})
|
|
34
|
+
ISSUES=""
|
|
35
|
+
|
|
36
|
+
# API keys / tokens in env or with: blocks
|
|
37
|
+
if grep -nE '(APIKEY|API_KEY|TOKEN|SECRET|PASSWORD|PRIVATE_KEY)\s*[:=]\s*["\x27]?[A-Za-z0-9+/=_-]{20,}' "$FILE" 2>/dev/null | grep -v '\${{' | head -3 | grep -q .; then
|
|
38
|
+
ISSUES="${ISSUES}Hardcoded API key/token found (use \${{ secrets.NAME }} instead)\n"
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
# Bearer tokens
|
|
42
|
+
if grep -nE 'Bearer\s+[A-Za-z0-9._-]{20,}' "$FILE" 2>/dev/null | grep -v '\${{' | head -1 | grep -q .; then
|
|
43
|
+
ISSUES="${ISSUES}Hardcoded Bearer token found\n"
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
# AWS credentials
|
|
47
|
+
if grep -nE 'AKIA[0-9A-Z]{16}' "$FILE" 2>/dev/null | head -1 | grep -q .; then
|
|
48
|
+
ISSUES="${ISSUES}AWS access key ID found in workflow\n"
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
if [ -n "$ISSUES" ]; then
|
|
52
|
+
echo "WARNING: Potential secrets in GitHub Actions workflow:" >&2
|
|
53
|
+
echo " File: $FILE" >&2
|
|
54
|
+
echo -e " $ISSUES" >&2
|
|
55
|
+
echo " Use \${{ secrets.NAME }} instead of hardcoded values." >&2
|
|
56
|
+
echo " Add secrets via: gh secret set NAME --body 'value'" >&2
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
exit 0
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# gitops-drift-guard.sh — Warn when editing infrastructure files without PR
|
|
3
|
+
#
|
|
4
|
+
# Solves: Claude directly modifying Terraform, Kubernetes manifests, or
|
|
5
|
+
# Helm values on the default branch instead of creating a PR.
|
|
6
|
+
# In GitOps workflows, direct commits to main trigger immediate
|
|
7
|
+
# infrastructure changes.
|
|
8
|
+
#
|
|
9
|
+
# How it works: PreToolUse hook on Edit/Write that checks if the target
|
|
10
|
+
# file is an infrastructure file AND the current branch is main/master.
|
|
11
|
+
# Warns that changes should go through a PR for review.
|
|
12
|
+
#
|
|
13
|
+
# TRIGGER: PreToolUse
|
|
14
|
+
# MATCHER: "Edit|Write"
|
|
15
|
+
|
|
16
|
+
set -euo pipefail
|
|
17
|
+
|
|
18
|
+
INPUT=$(cat)
|
|
19
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
20
|
+
case "$TOOL" in Edit|Write) ;; *) exit 0 ;; esac
|
|
21
|
+
|
|
22
|
+
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
|
|
23
|
+
[ -z "$FILE" ] && exit 0
|
|
24
|
+
|
|
25
|
+
# Check if this is an infrastructure file
|
|
26
|
+
IS_INFRA=false
|
|
27
|
+
case "$FILE" in
|
|
28
|
+
*.tf|*.tfvars) IS_INFRA=true ;; # Terraform
|
|
29
|
+
*/k8s/*.yaml|*/k8s/*.yml) IS_INFRA=true ;; # Kubernetes manifests
|
|
30
|
+
*/helm/**/values*.yaml) IS_INFRA=true ;; # Helm values
|
|
31
|
+
*/charts/**/*.yaml) IS_INFRA=true ;; # Helm charts
|
|
32
|
+
.github/workflows/*.yml) IS_INFRA=true ;; # CI/CD workflows
|
|
33
|
+
Dockerfile*|docker-compose*.yml) IS_INFRA=true ;; # Docker
|
|
34
|
+
*/argocd/*.yaml) IS_INFRA=true ;; # ArgoCD
|
|
35
|
+
*/flux/*.yaml) IS_INFRA=true ;; # Flux
|
|
36
|
+
ansible/*.yml|*/playbooks/*.yml) IS_INFRA=true ;; # Ansible
|
|
37
|
+
esac
|
|
38
|
+
|
|
39
|
+
$IS_INFRA || exit 0
|
|
40
|
+
|
|
41
|
+
# Check if we're on a protected branch
|
|
42
|
+
BRANCH=$(git branch --show-current 2>/dev/null || echo "")
|
|
43
|
+
case "$BRANCH" in
|
|
44
|
+
main|master|production|release|release/*)
|
|
45
|
+
echo "WARNING: Editing infrastructure file on protected branch '$BRANCH'." >&2
|
|
46
|
+
echo " File: $FILE" >&2
|
|
47
|
+
echo " In GitOps workflows, changes to $BRANCH trigger immediate deployment." >&2
|
|
48
|
+
echo " Consider creating a feature branch and PR instead." >&2
|
|
49
|
+
# Warning only — change to exit 2 to block
|
|
50
|
+
;;
|
|
51
|
+
esac
|
|
52
|
+
|
|
53
|
+
exit 0
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# headless-empty-result-guard.sh — Detect empty results in headless mode
|
|
3
|
+
#
|
|
4
|
+
# Solves: claude -p exits with empty result when stream is interrupted (#40432).
|
|
5
|
+
# stop_reason: "tool_use" with result: "" is treated as success,
|
|
6
|
+
# but the model was actually mid-generation.
|
|
7
|
+
#
|
|
8
|
+
# How it works: Stop hook that checks environment for headless indicators
|
|
9
|
+
# and warns if the session produced no visible output. In CI/automation,
|
|
10
|
+
# this can trigger a retry or alert.
|
|
11
|
+
#
|
|
12
|
+
# CONFIG:
|
|
13
|
+
# CC_HEADLESS_MARKER="/tmp/claude-headless-result"
|
|
14
|
+
# Set this in your CI script to check after claude -p exits.
|
|
15
|
+
#
|
|
16
|
+
# TRIGGER: Stop
|
|
17
|
+
# MATCHER: ""
|
|
18
|
+
|
|
19
|
+
set -euo pipefail
|
|
20
|
+
|
|
21
|
+
INPUT=$(cat)
|
|
22
|
+
|
|
23
|
+
# Check if we're in headless/print mode
|
|
24
|
+
# Indicators: CLAUDE_PRINT_MODE env var, or no TTY
|
|
25
|
+
if [ -z "${CLAUDE_PRINT_MODE:-}" ] && [ -t 0 ]; then
|
|
26
|
+
# Interactive mode — skip
|
|
27
|
+
exit 0
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
# Check for empty/incomplete result indicators
|
|
31
|
+
STOP_REASON=$(echo "$INPUT" | jq -r '.stop_reason // empty' 2>/dev/null)
|
|
32
|
+
RESULT=$(echo "$INPUT" | jq -r '.result // empty' 2>/dev/null)
|
|
33
|
+
|
|
34
|
+
MARKER="${CC_HEADLESS_MARKER:-/tmp/claude-headless-result-${PPID:-0}}"
|
|
35
|
+
|
|
36
|
+
if [ "$STOP_REASON" = "tool_use" ] || [ -z "$RESULT" ]; then
|
|
37
|
+
echo "WARNING: Headless session ended with incomplete result." >&2
|
|
38
|
+
echo " stop_reason: ${STOP_REASON:-unknown}" >&2
|
|
39
|
+
echo " result length: ${#RESULT}" >&2
|
|
40
|
+
echo " This may indicate a stream interruption, not a completed task." >&2
|
|
41
|
+
echo "incomplete" > "$MARKER"
|
|
42
|
+
else
|
|
43
|
+
echo "complete" > "$MARKER"
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
exit 0
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# headless-stop-guard.sh — Skip Stop hooks in headless (-p) mode
|
|
3
|
+
#
|
|
4
|
+
# Solves: Stop hook causes empty result in print mode (#38651).
|
|
5
|
+
# Any Stop hook (even no-op) causes `claude -p` to return
|
|
6
|
+
# empty string instead of the model's response.
|
|
7
|
+
#
|
|
8
|
+
# How it works: Detects headless mode via parent process inspection
|
|
9
|
+
# and exits immediately, preventing the Stop hook from interfering
|
|
10
|
+
# with result collection.
|
|
11
|
+
#
|
|
12
|
+
# TRIGGER: Stop
|
|
13
|
+
# MATCHER: ""
|
|
14
|
+
#
|
|
15
|
+
# Usage: Use as a wrapper around your actual Stop hook:
|
|
16
|
+
# {
|
|
17
|
+
# "hooks": {
|
|
18
|
+
# "Stop": [{
|
|
19
|
+
# "hooks": [{ "type": "command", "command": "bash ~/.claude/hooks/headless-stop-guard.sh ~/.claude/hooks/my-stop-hook.sh" }]
|
|
20
|
+
# }]
|
|
21
|
+
# }
|
|
22
|
+
# }
|
|
23
|
+
#
|
|
24
|
+
# Or set CC_HEADLESS=1 in your wrapper script before `claude -p`.
|
|
25
|
+
|
|
26
|
+
# Method 1: Environment variable (most reliable)
|
|
27
|
+
[ "$CC_HEADLESS" = "1" ] && exit 0
|
|
28
|
+
|
|
29
|
+
# Method 2: Parent process detection
|
|
30
|
+
PARENT_CMD=$(ps -o args= -p $PPID 2>/dev/null || true)
|
|
31
|
+
if echo "$PARENT_CMD" | grep -qE '\bclaude\b.*\s-p\b'; then
|
|
32
|
+
exit 0
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
# Not headless — run the wrapped hook if provided
|
|
36
|
+
TARGET="$1"
|
|
37
|
+
if [ -n "$TARGET" ] && [ -f "$TARGET" ]; then
|
|
38
|
+
shift
|
|
39
|
+
cat | bash "$TARGET" "$@"
|
|
40
|
+
exit $?
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
exit 0
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# issue-draft-redact-guard.sh — Redact sensitive info from public issue drafts
|
|
3
|
+
#
|
|
4
|
+
# Solves: Claude drafting public bug reports with sensitive project info (#29121).
|
|
5
|
+
# Internal org names, private URLs, IP addresses, and file paths
|
|
6
|
+
# get included in gh issue create commands.
|
|
7
|
+
#
|
|
8
|
+
# How it works: PreToolUse hook on Bash that intercepts gh issue/pr create
|
|
9
|
+
# commands and scans the body for sensitive patterns.
|
|
10
|
+
#
|
|
11
|
+
# TRIGGER: PreToolUse
|
|
12
|
+
# MATCHER: "Bash"
|
|
13
|
+
|
|
14
|
+
set -euo pipefail
|
|
15
|
+
|
|
16
|
+
INPUT=$(cat)
|
|
17
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
18
|
+
[ -z "$COMMAND" ] && exit 0
|
|
19
|
+
|
|
20
|
+
# Only check issue/PR creation commands
|
|
21
|
+
if ! echo "$COMMAND" | grep -qE 'gh\s+(issue|pr)\s+create'; then
|
|
22
|
+
exit 0
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
# Scan for sensitive patterns in the command body
|
|
26
|
+
SENSITIVE_PATTERNS=(
|
|
27
|
+
'[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' # IP addresses
|
|
28
|
+
'(internal|private|corp|staging)\.[a-z]+\.[a-z]+' # Internal domains
|
|
29
|
+
'/home/[a-zA-Z]+/' # Home directory paths
|
|
30
|
+
'/Users/[a-zA-Z]+/' # macOS home paths
|
|
31
|
+
'password|passwd|secret_key|private_key' # Secret keywords
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
for pattern in "${SENSITIVE_PATTERNS[@]}"; do
|
|
35
|
+
if echo "$COMMAND" | grep -qEi "$pattern"; then
|
|
36
|
+
MATCH=$(echo "$COMMAND" | grep -oEi "$pattern" | head -1)
|
|
37
|
+
echo "WARNING: Sensitive pattern detected in issue draft." >&2
|
|
38
|
+
echo "Pattern match: $MATCH" >&2
|
|
39
|
+
echo "Review and redact before posting publicly." >&2
|
|
40
|
+
# Warn but don't block — user may intentionally include their own info
|
|
41
|
+
break
|
|
42
|
+
fi
|
|
43
|
+
done
|
|
44
|
+
|
|
45
|
+
exit 0
|