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,59 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ci-workflow-guard.sh — Prevent dangerous CI/CD workflow modifications
|
|
3
|
+
#
|
|
4
|
+
# Solves: Claude modifying CI workflows to add `--no-verify`, skip tests,
|
|
5
|
+
# disable security scanning, or add overly broad permissions.
|
|
6
|
+
# A compromised workflow can exfiltrate secrets or deploy malicious code.
|
|
7
|
+
#
|
|
8
|
+
# How it works: PostToolUse hook on Edit/Write that checks workflow files
|
|
9
|
+
# for dangerous patterns after modification.
|
|
10
|
+
#
|
|
11
|
+
# TRIGGER: PostToolUse
|
|
12
|
+
# MATCHER: "Edit|Write"
|
|
13
|
+
|
|
14
|
+
set -euo pipefail
|
|
15
|
+
|
|
16
|
+
INPUT=$(cat)
|
|
17
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
18
|
+
case "$TOOL" in Edit|Write) ;; *) exit 0 ;; esac
|
|
19
|
+
|
|
20
|
+
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
|
|
21
|
+
[ -z "$FILE" ] && exit 0
|
|
22
|
+
|
|
23
|
+
# Only check CI workflow files
|
|
24
|
+
case "$FILE" in
|
|
25
|
+
.github/workflows/*.yml|.github/workflows/*.yaml) ;;
|
|
26
|
+
.gitlab-ci.yml|.circleci/config.yml|Jenkinsfile) ;;
|
|
27
|
+
*/.github/workflows/*.yml) ;;
|
|
28
|
+
*) exit 0 ;;
|
|
29
|
+
esac
|
|
30
|
+
|
|
31
|
+
[ -f "$FILE" ] || exit 0
|
|
32
|
+
|
|
33
|
+
WARNINGS=""
|
|
34
|
+
|
|
35
|
+
# Check for dangerous patterns
|
|
36
|
+
if grep -qE 'permissions:\s*write-all|permissions:\s*\{[^}]*contents:\s*write' "$FILE" 2>/dev/null; then
|
|
37
|
+
WARNINGS="${WARNINGS} - Broad write permissions detected\n"
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
if grep -qE '--no-verify|--skip-tests|--no-check|SKIP_CI|skip ci|\[ci skip\]' "$FILE" 2>/dev/null; then
|
|
41
|
+
WARNINGS="${WARNINGS} - Test/verification skip detected\n"
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
if grep -qE 'curl.*\|.*sh|wget.*\|.*bash|bash\s*<\(curl' "$FILE" 2>/dev/null; then
|
|
45
|
+
WARNINGS="${WARNINGS} - Remote script execution (curl|sh) detected\n"
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
if grep -qE 'dangerously-skip-permissions|--force|--no-verify' "$FILE" 2>/dev/null; then
|
|
49
|
+
WARNINGS="${WARNINGS} - Safety bypass flags detected\n"
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
if [ -n "$WARNINGS" ]; then
|
|
53
|
+
echo "WARNING: Potentially dangerous CI workflow changes:" >&2
|
|
54
|
+
echo " File: $FILE" >&2
|
|
55
|
+
echo -e "$WARNINGS" >&2
|
|
56
|
+
echo " Review these changes carefully before committing." >&2
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
exit 0
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
CLAUDE_DIR="$HOME/.claude"
|
|
2
|
+
MAX_AGE_DAYS="${CC_GC_MAX_AGE:-30}"
|
|
3
|
+
MAX_SIZE_MB="${CC_GC_MAX_SIZE:-500}"
|
|
4
|
+
DRY_RUN="${CC_GC_DRY_RUN:-0}"
|
|
5
|
+
find "$CLAUDE_DIR/projects" -name "*.jsonl" -mtime +"$MAX_AGE_DAYS" -type f 2>/dev/null | while read f; do
|
|
6
|
+
[ "$DRY_RUN" = "1" ] && echo " [dry-run] would delete: $f" >&2 && continue
|
|
7
|
+
rm "$f"
|
|
8
|
+
done
|
|
9
|
+
find "$CLAUDE_DIR/projects" -maxdepth 1 -type d -empty -delete 2>/dev/null
|
|
10
|
+
find "$CLAUDE_DIR" -path "*/tool-results/*" -mtime +"$MAX_AGE_DAYS" -type f -delete 2>/dev/null
|
|
11
|
+
TOTAL_MB=$(du -sm "$CLAUDE_DIR" 2>/dev/null | cut -f1)
|
|
12
|
+
if [ "$TOTAL_MB" -gt "$MAX_SIZE_MB" ] 2>/dev/null; then
|
|
13
|
+
echo "~/.claude is ${TOTAL_MB}MB (cap: ${MAX_SIZE_MB}MB)" >&2
|
|
14
|
+
fi
|
|
15
|
+
exit 0
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# claudeignore-enforce-guard.sh — Enforce .claudeignore at the tool level
|
|
3
|
+
#
|
|
4
|
+
# Solves: .claudeignore patterns not blocking Read/Edit/Grep tool calls (#16704).
|
|
5
|
+
# Claude can directly access files listed in .claudeignore via tool calls.
|
|
6
|
+
# This hook enforces ignore rules that the built-in system misses.
|
|
7
|
+
#
|
|
8
|
+
# How it works: PreToolUse hook on Read/Edit/Write/Grep that checks if the
|
|
9
|
+
# target file matches any pattern in .claudeignore. Uses glob-style matching.
|
|
10
|
+
#
|
|
11
|
+
# TRIGGER: PreToolUse
|
|
12
|
+
# MATCHER: "Read|Edit|Write|Grep|Glob"
|
|
13
|
+
|
|
14
|
+
set -euo pipefail
|
|
15
|
+
|
|
16
|
+
INPUT=$(cat)
|
|
17
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
18
|
+
|
|
19
|
+
# Extract file path based on tool type
|
|
20
|
+
case "$TOOL" in
|
|
21
|
+
Read|Edit|Write)
|
|
22
|
+
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
|
|
23
|
+
;;
|
|
24
|
+
Grep|Glob)
|
|
25
|
+
FILE=$(echo "$INPUT" | jq -r '.tool_input.path // empty' 2>/dev/null)
|
|
26
|
+
;;
|
|
27
|
+
*) exit 0 ;;
|
|
28
|
+
esac
|
|
29
|
+
|
|
30
|
+
[ -z "$FILE" ] && exit 0
|
|
31
|
+
|
|
32
|
+
# Find .claudeignore in project root or current directory
|
|
33
|
+
IGNORE_FILE=""
|
|
34
|
+
for candidate in ".claudeignore" "../.claudeignore" "../../.claudeignore"; do
|
|
35
|
+
if [ -f "$candidate" ]; then
|
|
36
|
+
IGNORE_FILE="$candidate"
|
|
37
|
+
break
|
|
38
|
+
fi
|
|
39
|
+
done
|
|
40
|
+
|
|
41
|
+
[ -z "$IGNORE_FILE" ] && exit 0
|
|
42
|
+
|
|
43
|
+
# Check each pattern in .claudeignore
|
|
44
|
+
while IFS= read -r pattern || [ -n "$pattern" ]; do
|
|
45
|
+
# Skip empty lines and comments
|
|
46
|
+
[[ -z "$pattern" || "$pattern" == \#* ]] && continue
|
|
47
|
+
# Strip trailing whitespace
|
|
48
|
+
pattern=$(echo "$pattern" | sed 's/[[:space:]]*$//')
|
|
49
|
+
[ -z "$pattern" ] && continue
|
|
50
|
+
|
|
51
|
+
# Match: exact path, basename, or glob
|
|
52
|
+
BASENAME=$(basename "$FILE")
|
|
53
|
+
if [[ "$FILE" == $pattern ]] || [[ "$BASENAME" == $pattern ]] || [[ "$FILE" == */$pattern ]]; then
|
|
54
|
+
echo "BLOCKED: File '$FILE' matches .claudeignore pattern '$pattern'." >&2
|
|
55
|
+
echo "This file is excluded from Claude Code access." >&2
|
|
56
|
+
exit 2
|
|
57
|
+
fi
|
|
58
|
+
done < "$IGNORE_FILE"
|
|
59
|
+
|
|
60
|
+
exit 0
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# claudemd-violation-detector.sh — Remind critical CLAUDE.md rules after tool use
|
|
3
|
+
#
|
|
4
|
+
# Solves: Claude ignores CLAUDE.md instructions, especially after
|
|
5
|
+
# context compaction or in long sessions (#40930).
|
|
6
|
+
#
|
|
7
|
+
# How it works: After each tool use, extracts and prints
|
|
8
|
+
# critical rules (ABSOLUTE/MUST NEVER/NEVER/禁止) from CLAUDE.md
|
|
9
|
+
# as a reminder. Runs every N tool calls to avoid noise.
|
|
10
|
+
#
|
|
11
|
+
# TRIGGER: PostToolUse
|
|
12
|
+
# MATCHER: ""
|
|
13
|
+
|
|
14
|
+
set -euo pipefail
|
|
15
|
+
|
|
16
|
+
# Rate limit: only remind every 20 tool calls
|
|
17
|
+
COUNTER_FILE="/tmp/claudemd-reminder-counter"
|
|
18
|
+
COUNT=$(cat "$COUNTER_FILE" 2>/dev/null || echo "0")
|
|
19
|
+
COUNT=$((COUNT + 1))
|
|
20
|
+
echo "$COUNT" > "$COUNTER_FILE"
|
|
21
|
+
[ $((COUNT % 20)) -ne 0 ] && exit 0
|
|
22
|
+
|
|
23
|
+
# Find CLAUDE.md
|
|
24
|
+
CLAUDEMD=""
|
|
25
|
+
for candidate in "CLAUDE.md" ".claude/CLAUDE.md" "../CLAUDE.md"; do
|
|
26
|
+
[ -f "$candidate" ] && CLAUDEMD="$candidate" && break
|
|
27
|
+
done
|
|
28
|
+
[ -z "$CLAUDEMD" ] && exit 0
|
|
29
|
+
|
|
30
|
+
# Extract critical rules
|
|
31
|
+
RULES=$(grep -iE '(ABSOLUTE|MUST NEVER|NEVER DO|禁止|絶対)' "$CLAUDEMD" 2>/dev/null | head -5 || true)
|
|
32
|
+
[ -z "$RULES" ] && exit 0
|
|
33
|
+
|
|
34
|
+
echo "📋 CLAUDE.md critical rules reminder:" >&2
|
|
35
|
+
echo "$RULES" >&2
|
|
36
|
+
exit 0
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# clear-command-confirm-guard.sh — Block accidental /clear command
|
|
3
|
+
#
|
|
4
|
+
# Solves: /clear destroys all conversation context with zero
|
|
5
|
+
# confirmation. Prefix matching means /c + Enter can
|
|
6
|
+
# accidentally trigger /clear instead of /commit or /compact (#40931).
|
|
7
|
+
#
|
|
8
|
+
# How it works: Blocks /clear entirely. Use /compact to reduce
|
|
9
|
+
# context without losing it.
|
|
10
|
+
#
|
|
11
|
+
# TRIGGER: UserPromptSubmit
|
|
12
|
+
# MATCHER: "^/clear$"
|
|
13
|
+
|
|
14
|
+
INPUT=$(cat)
|
|
15
|
+
PROMPT=$(echo "$INPUT" | jq -r '.prompt // empty' 2>/dev/null)
|
|
16
|
+
|
|
17
|
+
if echo "$PROMPT" | grep -qE '^/clear$'; then
|
|
18
|
+
echo "BLOCKED: /clear permanently destroys all context. Use /compact instead to reduce context safely." >&2
|
|
19
|
+
exit 2
|
|
20
|
+
fi
|
|
21
|
+
exit 0
|
|
@@ -24,6 +24,8 @@
|
|
|
24
24
|
#
|
|
25
25
|
# The "if" field (v2.1.85+) skips this hook for non-commit commands.
|
|
26
26
|
# Without "if", the hook still works — it checks internally and exits early.
|
|
27
|
+
#
|
|
28
|
+
# TRIGGER: PreToolUse MATCHER: "Bash"
|
|
27
29
|
|
|
28
30
|
INPUT=$(cat)
|
|
29
31
|
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ================================================================
|
|
3
|
+
# compact-blocker.sh — Block auto-compaction entirely
|
|
4
|
+
# ================================================================
|
|
5
|
+
# PURPOSE:
|
|
6
|
+
# Power users who manage context manually (file-backed plans,
|
|
7
|
+
# checkpoint scripts) lose nuanced context when auto-compaction
|
|
8
|
+
# fires. This hook blocks compaction via exit code 2.
|
|
9
|
+
#
|
|
10
|
+
# For conditional blocking (e.g., only during plan mode), modify
|
|
11
|
+
# the guard condition below.
|
|
12
|
+
#
|
|
13
|
+
# TRIGGER: PreCompact
|
|
14
|
+
# MATCHER: (none — PreCompact has no matcher)
|
|
15
|
+
#
|
|
16
|
+
# DECISION: exit 2 = block compaction
|
|
17
|
+
#
|
|
18
|
+
# See: https://github.com/anthropics/claude-code/issues/6689
|
|
19
|
+
# ================================================================
|
|
20
|
+
|
|
21
|
+
# Unconditional block — compaction never fires
|
|
22
|
+
# To make it conditional, add logic here:
|
|
23
|
+
# e.g., [ -f /tmp/allow-compact ] && exit 0
|
|
24
|
+
echo '{"decision":"block","reason":"auto-compaction disabled by compact-blocker hook"}' >&2
|
|
25
|
+
exit 2
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# consecutive-failure-circuit-breaker.sh — Stop after repeated failures
|
|
3
|
+
#
|
|
4
|
+
# Solves: Claude escalating to destructive actions after repeated failures (#31946).
|
|
5
|
+
# Without this, Claude retries failing commands dozens of times,
|
|
6
|
+
# eventually trying increasingly dangerous alternatives.
|
|
7
|
+
#
|
|
8
|
+
# How it works: PostToolUse hook on Bash that tracks consecutive non-zero
|
|
9
|
+
# exit codes. After CC_MAX_CONSECUTIVE_FAILURES (default 3), blocks
|
|
10
|
+
# further Bash calls until a success resets the counter.
|
|
11
|
+
#
|
|
12
|
+
# TRIGGER: PostToolUse
|
|
13
|
+
# MATCHER: "Bash"
|
|
14
|
+
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
|
|
17
|
+
INPUT=$(cat)
|
|
18
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
19
|
+
[ "$TOOL" != "Bash" ] && exit 0
|
|
20
|
+
|
|
21
|
+
EXIT_CODE=$(echo "$INPUT" | jq -r '.tool_result.exitCode // "0"' 2>/dev/null)
|
|
22
|
+
MAX_FAILURES="${CC_MAX_CONSECUTIVE_FAILURES:-3}"
|
|
23
|
+
COUNTER_FILE="/tmp/claude-consecutive-failures-${PPID:-0}"
|
|
24
|
+
|
|
25
|
+
if [ "$EXIT_CODE" = "0" ]; then
|
|
26
|
+
# Success — reset counter
|
|
27
|
+
echo "0" > "$COUNTER_FILE"
|
|
28
|
+
exit 0
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
# Failure — increment counter
|
|
32
|
+
COUNT=$(cat "$COUNTER_FILE" 2>/dev/null || echo 0)
|
|
33
|
+
COUNT=$((COUNT + 1))
|
|
34
|
+
echo "$COUNT" > "$COUNTER_FILE"
|
|
35
|
+
|
|
36
|
+
if [ "$COUNT" -ge "$MAX_FAILURES" ]; then
|
|
37
|
+
echo "CIRCUIT BREAKER: $COUNT consecutive Bash failures detected." >&2
|
|
38
|
+
echo "" >&2
|
|
39
|
+
echo "Stop and reassess your approach. Repeated failures often lead to" >&2
|
|
40
|
+
echo "increasingly risky workarounds. Consider:" >&2
|
|
41
|
+
echo " 1. Read the error messages carefully" >&2
|
|
42
|
+
echo " 2. Check your assumptions" >&2
|
|
43
|
+
echo " 3. Try a completely different approach" >&2
|
|
44
|
+
echo " 4. Ask the user for help" >&2
|
|
45
|
+
# Don't block (exit 0) — just warn strongly via stderr
|
|
46
|
+
# PostToolUse can't block, but the warning enters context
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
exit 0
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
INPUT=$(cat)
|
|
2
|
+
COUNTER="/tmp/cc-tool-count-$$"
|
|
3
|
+
COUNT=$(cat "$COUNTER" 2>/dev/null || echo 0)
|
|
4
|
+
COUNT=$((COUNT + 1))
|
|
5
|
+
echo "$COUNT" > "$COUNTER"
|
|
6
|
+
THRESHOLD="${CC_COMPACT_THRESHOLD:-50}"
|
|
7
|
+
if [ "$((COUNT % THRESHOLD))" -eq 0 ]; then
|
|
8
|
+
TRANSCRIPT=$(ls -t ~/.claude/projects/*/sessions/*/transcript.jsonl 2>/dev/null | head -1)
|
|
9
|
+
if [ -f "$TRANSCRIPT" ]; then
|
|
10
|
+
SIZE_KB=$(($(wc -c < "$TRANSCRIPT") / 1024))
|
|
11
|
+
if [ "$SIZE_KB" -gt 200 ]; then
|
|
12
|
+
echo "Context ~${SIZE_KB}KB ($COUNT calls). Consider /compact." >&2
|
|
13
|
+
fi
|
|
14
|
+
fi
|
|
15
|
+
fi
|
|
16
|
+
exit 0
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ================================================================
|
|
3
|
+
# core-file-protect-guard.sh — Block edits to core/config/rules files
|
|
4
|
+
# ================================================================
|
|
5
|
+
# PURPOSE:
|
|
6
|
+
# Claude Code sometimes makes unprompted architectural changes to
|
|
7
|
+
# game rules, configuration files, and core logic files. This hook
|
|
8
|
+
# blocks modifications to files matching configurable glob patterns.
|
|
9
|
+
#
|
|
10
|
+
# Protects files matching CC_PROTECTED_FILES patterns (colon-separated).
|
|
11
|
+
# Default: "*rules*:*config*:*core*"
|
|
12
|
+
#
|
|
13
|
+
# Catches:
|
|
14
|
+
# - Edit/Write tool targeting protected files
|
|
15
|
+
# - Bash commands using sed -i or awk -i on protected files
|
|
16
|
+
#
|
|
17
|
+
# See: https://github.com/anthropics/claude-code/issues/40788
|
|
18
|
+
#
|
|
19
|
+
# TRIGGER: PreToolUse MATCHER: "Edit|Write|Bash"
|
|
20
|
+
#
|
|
21
|
+
# Configuration:
|
|
22
|
+
# CC_PROTECTED_FILES — colon-separated glob patterns
|
|
23
|
+
# Default: "*rules*:*config*:*core*"
|
|
24
|
+
# ================================================================
|
|
25
|
+
|
|
26
|
+
INPUT=$(cat)
|
|
27
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
28
|
+
|
|
29
|
+
# Configurable protected file patterns (colon-separated globs)
|
|
30
|
+
PROTECTED="${CC_PROTECTED_FILES:-*rules*:*config*:*core*}"
|
|
31
|
+
|
|
32
|
+
# Convert colon-separated globs to a function that checks a filename
|
|
33
|
+
matches_protected() {
|
|
34
|
+
local filepath="$1"
|
|
35
|
+
local basename
|
|
36
|
+
basename=$(basename "$filepath")
|
|
37
|
+
|
|
38
|
+
IFS=':' read -ra PATTERNS <<< "$PROTECTED"
|
|
39
|
+
for pattern in "${PATTERNS[@]}"; do
|
|
40
|
+
# Use bash glob matching (case-insensitive via shopt)
|
|
41
|
+
if [[ "$basename" == $pattern ]] || [[ "$filepath" == *$pattern* ]]; then
|
|
42
|
+
return 0
|
|
43
|
+
fi
|
|
44
|
+
done
|
|
45
|
+
return 1
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# Handle Edit/Write tools
|
|
49
|
+
if [[ "$TOOL" == "Edit" || "$TOOL" == "Write" ]]; then
|
|
50
|
+
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
|
|
51
|
+
[ -z "$FILE" ] && exit 0
|
|
52
|
+
|
|
53
|
+
if matches_protected "$FILE"; then
|
|
54
|
+
echo "BLOCKED: Cannot modify protected file: $FILE" >&2
|
|
55
|
+
echo "" >&2
|
|
56
|
+
echo "Protected patterns: $PROTECTED" >&2
|
|
57
|
+
echo "Configure with CC_PROTECTED_FILES env var." >&2
|
|
58
|
+
echo "" >&2
|
|
59
|
+
echo "See: https://github.com/anthropics/claude-code/issues/40788" >&2
|
|
60
|
+
exit 2
|
|
61
|
+
fi
|
|
62
|
+
exit 0
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
# Handle Bash tool — check for sed -i / awk -i targeting protected files
|
|
66
|
+
if [[ "$TOOL" == "Bash" ]]; then
|
|
67
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
68
|
+
[ -z "$COMMAND" ] && exit 0
|
|
69
|
+
|
|
70
|
+
# Skip echo/printf
|
|
71
|
+
echo "$COMMAND" | grep -qE '^\s*(echo|printf)\s' && exit 0
|
|
72
|
+
|
|
73
|
+
# Check for sed -i or awk -i inplace targeting protected files
|
|
74
|
+
if echo "$COMMAND" | grep -qE '(sed\s+-i|awk\s+-i\s+inplace)'; then
|
|
75
|
+
# Extract potential file arguments from the command
|
|
76
|
+
IFS=':' read -ra PATTERNS <<< "$PROTECTED"
|
|
77
|
+
for pattern in "${PATTERNS[@]}"; do
|
|
78
|
+
if echo "$COMMAND" | grep -qE "$pattern"; then
|
|
79
|
+
echo "BLOCKED: In-place edit targets protected file pattern: $pattern" >&2
|
|
80
|
+
echo "" >&2
|
|
81
|
+
echo "Command: $COMMAND" >&2
|
|
82
|
+
echo "Protected patterns: $PROTECTED" >&2
|
|
83
|
+
echo "" >&2
|
|
84
|
+
echo "See: https://github.com/anthropics/claude-code/issues/40788" >&2
|
|
85
|
+
exit 2
|
|
86
|
+
fi
|
|
87
|
+
done
|
|
88
|
+
fi
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
exit 0
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# cron-modification-guard.sh — Block unreviewed cron job modifications
|
|
3
|
+
#
|
|
4
|
+
# Solves: Claude creating cron jobs that sabotage live services (#40421).
|
|
5
|
+
# A cron job that polls 200 content IDs against a single-connection
|
|
6
|
+
# proxy caused stream resets every 10 minutes for days.
|
|
7
|
+
#
|
|
8
|
+
# Why this matters:
|
|
9
|
+
# Cron jobs are persistent, invisible, and run unattended. A bad cron
|
|
10
|
+
# can cause sustained damage long after the session ends. This hook
|
|
11
|
+
# blocks crontab edits, /etc/cron.d writes, and systemd timer creation.
|
|
12
|
+
#
|
|
13
|
+
# TRIGGER: PreToolUse
|
|
14
|
+
# MATCHER: "Bash"
|
|
15
|
+
|
|
16
|
+
INPUT=$(cat)
|
|
17
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
18
|
+
[ -z "$COMMAND" ] && exit 0
|
|
19
|
+
|
|
20
|
+
# Detect cron/timer modifications
|
|
21
|
+
if echo "$COMMAND" | grep -qEi '(crontab\s+-[elr]|crontab\s+[^-]|systemctl\s+(enable|start|restart)\s+.*\.timer|(^|\s|;|&&|\|)at\s+)'; then
|
|
22
|
+
echo "BLOCKED: Cron/timer modification requires manual review." >&2
|
|
23
|
+
echo "" >&2
|
|
24
|
+
echo "Command: $COMMAND" >&2
|
|
25
|
+
echo "" >&2
|
|
26
|
+
echo "Cron jobs are persistent and run unattended. Before creating one:" >&2
|
|
27
|
+
echo " 1. Will this interfere with live services?" >&2
|
|
28
|
+
echo " 2. Does it access shared resources (DB, API, proxy)?" >&2
|
|
29
|
+
echo " 3. What happens if it fails silently?" >&2
|
|
30
|
+
exit 2
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
# Also catch writing to cron directories
|
|
34
|
+
if echo "$COMMAND" | grep -qE '>\s*/etc/cron\.|tee\s+/etc/cron\.'; then
|
|
35
|
+
echo "BLOCKED: Direct write to cron directory." >&2
|
|
36
|
+
echo "Command: $COMMAND" >&2
|
|
37
|
+
exit 2
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
exit 0
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# cwd-drift-detector.sh — Warn when destructive commands run outside project root
|
|
3
|
+
#
|
|
4
|
+
# Solves: Claude frequently loses track of which directory
|
|
5
|
+
# it is in, risking destructive commands in the wrong
|
|
6
|
+
# location (#1669). git reset --hard in the wrong
|
|
7
|
+
# directory can destroy unrelated work.
|
|
8
|
+
#
|
|
9
|
+
# How it works: For destructive commands (git reset, rm -rf,
|
|
10
|
+
# git clean, git checkout -- .), checks if the current
|
|
11
|
+
# directory looks like a project root (has .git, package.json,
|
|
12
|
+
# etc). Warns if it doesn't.
|
|
13
|
+
#
|
|
14
|
+
# TRIGGER: PreToolUse
|
|
15
|
+
# MATCHER: "Bash"
|
|
16
|
+
|
|
17
|
+
set -euo pipefail
|
|
18
|
+
INPUT=$(cat)
|
|
19
|
+
|
|
20
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
21
|
+
[ -z "$COMMAND" ] && exit 0
|
|
22
|
+
|
|
23
|
+
# Only check destructive commands
|
|
24
|
+
if ! echo "$COMMAND" | grep -qE '(git\s+(reset|clean|checkout\s+--|push\s+--force)|rm\s+-rf|DROP\s+TABLE|DROP\s+DATABASE)'; then
|
|
25
|
+
exit 0
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
# Check if we're in a project root
|
|
29
|
+
CWD=$(pwd)
|
|
30
|
+
IS_PROJECT=false
|
|
31
|
+
|
|
32
|
+
for marker in .git package.json Cargo.toml go.mod pyproject.toml Makefile; do
|
|
33
|
+
if [ -e "$CWD/$marker" ]; then
|
|
34
|
+
IS_PROJECT=true
|
|
35
|
+
break
|
|
36
|
+
fi
|
|
37
|
+
done
|
|
38
|
+
|
|
39
|
+
if [ "$IS_PROJECT" = false ]; then
|
|
40
|
+
echo "WARNING: Destructive command detected outside project root." >&2
|
|
41
|
+
echo " CWD: $CWD" >&2
|
|
42
|
+
echo " Command: $(echo "$COMMAND" | head -c 100)" >&2
|
|
43
|
+
echo " No project markers (.git, package.json, etc) found." >&2
|
|
44
|
+
echo " Verify you are in the correct directory." >&2
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
exit 0
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# cwd-project-boundary-guard.sh — Warn when cd leaves the project directory
|
|
3
|
+
#
|
|
4
|
+
# Solves: Claude navigating to system directories or other projects
|
|
5
|
+
# and making changes outside the intended scope. Especially
|
|
6
|
+
# dangerous with auto-approve, where writes to /etc or
|
|
7
|
+
# other projects go unchallenged.
|
|
8
|
+
#
|
|
9
|
+
# How it works: CwdChanged hook that tracks the project root and warns
|
|
10
|
+
# when the working directory moves outside it.
|
|
11
|
+
#
|
|
12
|
+
# CONFIG:
|
|
13
|
+
# CC_PROJECT_ROOT (auto-detected if not set)
|
|
14
|
+
#
|
|
15
|
+
# TRIGGER: CwdChanged
|
|
16
|
+
# MATCHER: "" (CwdChanged has no matcher support)
|
|
17
|
+
|
|
18
|
+
set -euo pipefail
|
|
19
|
+
|
|
20
|
+
INPUT=$(cat)
|
|
21
|
+
NEW_CWD=$(echo "$INPUT" | jq -r '.cwd // empty' 2>/dev/null)
|
|
22
|
+
[ -z "$NEW_CWD" ] && exit 0
|
|
23
|
+
|
|
24
|
+
# Determine project root
|
|
25
|
+
PROJECT_ROOT="${CC_PROJECT_ROOT:-}"
|
|
26
|
+
if [ -z "$PROJECT_ROOT" ]; then
|
|
27
|
+
# Auto-detect: find nearest .git directory
|
|
28
|
+
PROJECT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || echo "")
|
|
29
|
+
fi
|
|
30
|
+
[ -z "$PROJECT_ROOT" ] && exit 0
|
|
31
|
+
|
|
32
|
+
# Normalize paths
|
|
33
|
+
PROJECT_ROOT=$(realpath "$PROJECT_ROOT" 2>/dev/null || echo "$PROJECT_ROOT")
|
|
34
|
+
NEW_CWD=$(realpath "$NEW_CWD" 2>/dev/null || echo "$NEW_CWD")
|
|
35
|
+
|
|
36
|
+
# Check if new cwd is inside project
|
|
37
|
+
case "$NEW_CWD" in
|
|
38
|
+
"$PROJECT_ROOT"|"$PROJECT_ROOT"/*)
|
|
39
|
+
# Inside project — OK
|
|
40
|
+
exit 0
|
|
41
|
+
;;
|
|
42
|
+
*)
|
|
43
|
+
echo "WARNING: Working directory left project boundary." >&2
|
|
44
|
+
echo " Project: $PROJECT_ROOT" >&2
|
|
45
|
+
echo " New cwd: $NEW_CWD" >&2
|
|
46
|
+
echo " Changes outside the project may affect other systems." >&2
|
|
47
|
+
;;
|
|
48
|
+
esac
|
|
49
|
+
|
|
50
|
+
exit 0
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# denied-action-retry-guard.sh — Block re-attempts of denied tool calls
|
|
3
|
+
#
|
|
4
|
+
# Solves: Model retries the same operation after user explicitly denied it (#40156).
|
|
5
|
+
# Claude asks "shall I git push?", user says no, Claude tries git push anyway.
|
|
6
|
+
#
|
|
7
|
+
# How it works: PreToolUse hook that tracks denied operations via a state file.
|
|
8
|
+
# When a tool call is blocked (exit 2 from any hook), the command signature
|
|
9
|
+
# is recorded. If the same signature appears again, it's auto-blocked.
|
|
10
|
+
#
|
|
11
|
+
# TRIGGER: PreToolUse
|
|
12
|
+
# MATCHER: "Bash|Edit|Write"
|
|
13
|
+
|
|
14
|
+
set -euo pipefail
|
|
15
|
+
|
|
16
|
+
INPUT=$(cat)
|
|
17
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
18
|
+
COMMAND=""
|
|
19
|
+
|
|
20
|
+
case "$TOOL" in
|
|
21
|
+
Bash) COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null) ;;
|
|
22
|
+
Edit|Write) COMMAND=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null) ;;
|
|
23
|
+
*) exit 0 ;;
|
|
24
|
+
esac
|
|
25
|
+
|
|
26
|
+
[ -z "$COMMAND" ] && exit 0
|
|
27
|
+
|
|
28
|
+
DENY_FILE="/tmp/claude-denied-actions-${PPID:-0}"
|
|
29
|
+
|
|
30
|
+
# Check if this action was previously denied
|
|
31
|
+
if [ -f "$DENY_FILE" ]; then
|
|
32
|
+
# Normalize: take first 80 chars as signature
|
|
33
|
+
SIG=$(echo "$COMMAND" | head -c 80 | md5sum | cut -d' ' -f1)
|
|
34
|
+
if grep -q "$SIG" "$DENY_FILE" 2>/dev/null; then
|
|
35
|
+
echo "BLOCKED: This action was previously denied in this session." >&2
|
|
36
|
+
echo "Do not retry denied actions. Try a different approach." >&2
|
|
37
|
+
exit 2
|
|
38
|
+
fi
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
exit 0
|