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,54 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# self-modify-bypass-guard.sh — Auto-allow .claude/ writes in bypass mode
|
|
3
|
+
#
|
|
4
|
+
# Solves: Self-modification guard prompts for .claude/ writes even when
|
|
5
|
+
# bypassPermissions is active (#40463). The guard fires before
|
|
6
|
+
# hooks/permissions are evaluated.
|
|
7
|
+
#
|
|
8
|
+
# How it works: PermissionRequest hook that detects .claude/ write prompts
|
|
9
|
+
# and auto-allows them when the project uses bypassPermissions mode.
|
|
10
|
+
# Exempts security-sensitive paths (settings.json, CLAUDE.md).
|
|
11
|
+
#
|
|
12
|
+
# TRIGGER: PermissionRequest
|
|
13
|
+
# MATCHER: ""
|
|
14
|
+
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
|
|
17
|
+
INPUT=$(cat)
|
|
18
|
+
|
|
19
|
+
# Extract the permission request details
|
|
20
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
21
|
+
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
|
|
22
|
+
|
|
23
|
+
# Only handle Edit/Write to .claude/ paths
|
|
24
|
+
case "$TOOL" in
|
|
25
|
+
Edit|Write) ;;
|
|
26
|
+
*) exit 0 ;;
|
|
27
|
+
esac
|
|
28
|
+
|
|
29
|
+
case "$FILE" in
|
|
30
|
+
.claude/*|*/.claude/*) ;;
|
|
31
|
+
*) exit 0 ;;
|
|
32
|
+
esac
|
|
33
|
+
|
|
34
|
+
# Security-sensitive files: always prompt (don't auto-allow)
|
|
35
|
+
BASENAME=$(basename "$FILE")
|
|
36
|
+
case "$BASENAME" in
|
|
37
|
+
settings.json|settings.local.json|CLAUDE.md)
|
|
38
|
+
# Let the default prompt handle these
|
|
39
|
+
exit 0 ;;
|
|
40
|
+
esac
|
|
41
|
+
|
|
42
|
+
# Check if bypassPermissions is configured
|
|
43
|
+
SETTINGS=".claude/settings.json"
|
|
44
|
+
if [ -f "$SETTINGS" ]; then
|
|
45
|
+
MODE=$(jq -r '.defaultMode // empty' "$SETTINGS" 2>/dev/null)
|
|
46
|
+
if [ "$MODE" = "bypassPermissions" ]; then
|
|
47
|
+
# Auto-allow: output hookSpecificOutput to approve
|
|
48
|
+
echo '{"hookSpecificOutput":{"hookEventName":"PermissionRequest","allow":true}}'
|
|
49
|
+
exit 0
|
|
50
|
+
fi
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
# Not in bypass mode — let default prompt handle it
|
|
54
|
+
exit 0
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ================================================================
|
|
3
|
+
# session-duration-guard.sh — Warn on long-running sessions
|
|
4
|
+
# ================================================================
|
|
5
|
+
# PURPOSE:
|
|
6
|
+
# Model quality degrades in very long sessions due to context
|
|
7
|
+
# accumulation, compaction artifacts, and attention dilution.
|
|
8
|
+
# This hook warns at configurable thresholds and suggests
|
|
9
|
+
# saving state + starting fresh.
|
|
10
|
+
#
|
|
11
|
+
# Based on 700+ hours of autonomous operation experience.
|
|
12
|
+
#
|
|
13
|
+
# TRIGGER: PostToolUse
|
|
14
|
+
# MATCHER: "" (all tools)
|
|
15
|
+
#
|
|
16
|
+
# CONFIG:
|
|
17
|
+
# CC_SESSION_WARN_HOURS=2 (warn after 2 hours, default)
|
|
18
|
+
# CC_SESSION_CRITICAL_HOURS=4 (critical after 4 hours, default)
|
|
19
|
+
# ================================================================
|
|
20
|
+
|
|
21
|
+
MARKER="/tmp/cc-session-start-$$"
|
|
22
|
+
WARN_HOURS="${CC_SESSION_WARN_HOURS:-2}"
|
|
23
|
+
CRITICAL_HOURS="${CC_SESSION_CRITICAL_HOURS:-4}"
|
|
24
|
+
|
|
25
|
+
# Create marker on first run
|
|
26
|
+
if [ ! -f "$MARKER" ]; then
|
|
27
|
+
date +%s > "$MARKER"
|
|
28
|
+
exit 0
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
# Check every 50 tool calls (not every call)
|
|
32
|
+
COUNTER="/tmp/cc-duration-counter-$$"
|
|
33
|
+
COUNT=$(cat "$COUNTER" 2>/dev/null || echo 0)
|
|
34
|
+
COUNT=$((COUNT + 1))
|
|
35
|
+
echo "$COUNT" > "$COUNTER"
|
|
36
|
+
[ $((COUNT % 50)) -ne 0 ] && exit 0
|
|
37
|
+
|
|
38
|
+
START=$(cat "$MARKER" 2>/dev/null || echo 0)
|
|
39
|
+
NOW=$(date +%s)
|
|
40
|
+
ELAPSED=$(( (NOW - START) / 3600 ))
|
|
41
|
+
ELAPSED_MIN=$(( (NOW - START) / 60 ))
|
|
42
|
+
|
|
43
|
+
if [ "$ELAPSED" -ge "$CRITICAL_HOURS" ]; then
|
|
44
|
+
echo "⚠ CRITICAL: Session running for ${ELAPSED_MIN} minutes (${ELAPSED}+ hours)." >&2
|
|
45
|
+
echo " Model quality typically degrades after ${CRITICAL_HOURS} hours." >&2
|
|
46
|
+
echo " Save your state and start a new session: /compact then resume later." >&2
|
|
47
|
+
elif [ "$ELAPSED" -ge "$WARN_HOURS" ]; then
|
|
48
|
+
echo "NOTE: Session running for ${ELAPSED_MIN} minutes. Consider saving state." >&2
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
exit 0
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# session-end-logger.sh — Log session activity at exit
|
|
3
|
+
#
|
|
4
|
+
# Solves: No built-in session audit trail. When a session ends,
|
|
5
|
+
# there's no record of what was done unless you manually check
|
|
6
|
+
# git log or conversation history (#40010).
|
|
7
|
+
#
|
|
8
|
+
# How it works: SessionEnd hook that captures recent git commits,
|
|
9
|
+
# modified files, and session metadata into a structured log file.
|
|
10
|
+
#
|
|
11
|
+
# TRIGGER: SessionEnd
|
|
12
|
+
# MATCHER: ""
|
|
13
|
+
#
|
|
14
|
+
# Usage:
|
|
15
|
+
# {
|
|
16
|
+
# "hooks": {
|
|
17
|
+
# "SessionEnd": [{
|
|
18
|
+
# "hooks": [{ "type": "command", "command": "~/.claude/hooks/session-end-logger.sh" }]
|
|
19
|
+
# }]
|
|
20
|
+
# }
|
|
21
|
+
# }
|
|
22
|
+
|
|
23
|
+
INPUT=$(cat)
|
|
24
|
+
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // "unknown"' 2>/dev/null)
|
|
25
|
+
|
|
26
|
+
LOG_DIR=".claude/session-logs"
|
|
27
|
+
mkdir -p "$LOG_DIR"
|
|
28
|
+
LOG_FILE="$LOG_DIR/$(date '+%Y-%m-%d').md"
|
|
29
|
+
|
|
30
|
+
{
|
|
31
|
+
echo ""
|
|
32
|
+
echo "## Session $SESSION_ID — $(date '+%H:%M')"
|
|
33
|
+
echo ""
|
|
34
|
+
|
|
35
|
+
# Recent git activity
|
|
36
|
+
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
37
|
+
COMMITS=$(git log --oneline --since="1 hour ago" 2>/dev/null)
|
|
38
|
+
if [ -n "$COMMITS" ]; then
|
|
39
|
+
echo "### Commits"
|
|
40
|
+
echo '```'
|
|
41
|
+
echo "$COMMITS"
|
|
42
|
+
echo '```'
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
CHANGED=$(git diff --name-only HEAD~5..HEAD 2>/dev/null | head -20)
|
|
46
|
+
if [ -n "$CHANGED" ]; then
|
|
47
|
+
echo "### Changed files"
|
|
48
|
+
echo '```'
|
|
49
|
+
echo "$CHANGED"
|
|
50
|
+
echo '```'
|
|
51
|
+
fi
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
echo ""
|
|
55
|
+
} >> "$LOG_FILE"
|
|
56
|
+
|
|
57
|
+
exit 0
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ================================================================
|
|
3
|
+
# session-error-rate-monitor.sh — Detect session quality degradation
|
|
4
|
+
# ================================================================
|
|
5
|
+
# PURPOSE:
|
|
6
|
+
# Long sessions (6+ hours) show quality decay: more errors,
|
|
7
|
+
# ignored instructions, and destructive actions. This hook
|
|
8
|
+
# tracks the error rate over a rolling window and warns when
|
|
9
|
+
# it exceeds a threshold, suggesting a session restart.
|
|
10
|
+
#
|
|
11
|
+
# How it works:
|
|
12
|
+
# - Counts tool calls and errors (exit code != 0) in a state file
|
|
13
|
+
# - Calculates error rate over last N tool calls
|
|
14
|
+
# - Warns (stderr) when error rate exceeds threshold
|
|
15
|
+
# - Does NOT block — purely advisory (exit 0 always)
|
|
16
|
+
#
|
|
17
|
+
# TRIGGER: PostToolUse
|
|
18
|
+
# MATCHER: "Bash"
|
|
19
|
+
#
|
|
20
|
+
# CONFIG:
|
|
21
|
+
# CC_ERROR_RATE_WINDOW=20 (rolling window size)
|
|
22
|
+
# CC_ERROR_RATE_THRESHOLD=40 (% error rate to trigger warning)
|
|
23
|
+
#
|
|
24
|
+
# See: https://github.com/anthropics/claude-code/issues/32963
|
|
25
|
+
# ================================================================
|
|
26
|
+
|
|
27
|
+
INPUT=$(cat)
|
|
28
|
+
EXIT_CODE=$(echo "$INPUT" | jq -r '.tool_result.exit_code // "0"' 2>/dev/null)
|
|
29
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
30
|
+
|
|
31
|
+
# Only track Bash tool calls
|
|
32
|
+
[[ "$TOOL" != "Bash" ]] && exit 0
|
|
33
|
+
|
|
34
|
+
WINDOW=${CC_ERROR_RATE_WINDOW:-20}
|
|
35
|
+
THRESHOLD=${CC_ERROR_RATE_THRESHOLD:-40}
|
|
36
|
+
STATE_DIR="${HOME}/.claude/state"
|
|
37
|
+
mkdir -p "$STATE_DIR"
|
|
38
|
+
STATE_FILE="$STATE_DIR/error-rate-history.log"
|
|
39
|
+
|
|
40
|
+
# Record result: 0=success, 1=error
|
|
41
|
+
if [ "$EXIT_CODE" = "0" ]; then
|
|
42
|
+
echo "0" >> "$STATE_FILE"
|
|
43
|
+
else
|
|
44
|
+
echo "1" >> "$STATE_FILE"
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# Keep only last N entries
|
|
48
|
+
TOTAL=$(wc -l < "$STATE_FILE" 2>/dev/null || echo "0")
|
|
49
|
+
if [ "$TOTAL" -gt "$WINDOW" ]; then
|
|
50
|
+
tail -n "$WINDOW" "$STATE_FILE" > "$STATE_FILE.tmp"
|
|
51
|
+
mv "$STATE_FILE.tmp" "$STATE_FILE"
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
# Calculate error rate
|
|
55
|
+
if [ "$TOTAL" -ge "$WINDOW" ]; then
|
|
56
|
+
ERRORS=$(tail -n "$WINDOW" "$STATE_FILE" | grep -c "^1$" || echo "0")
|
|
57
|
+
RATE=$(( ERRORS * 100 / WINDOW ))
|
|
58
|
+
|
|
59
|
+
if [ "$RATE" -ge "$THRESHOLD" ]; then
|
|
60
|
+
echo "⚠ Session quality alert: ${RATE}% error rate over last ${WINDOW} commands (threshold: ${THRESHOLD}%)" >&2
|
|
61
|
+
echo " Consider: /compact or starting a fresh session" >&2
|
|
62
|
+
fi
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
exit 0
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# session-health-monitor.sh — Monitor session health metrics
|
|
3
|
+
#
|
|
4
|
+
# Solves: Long-running sessions degrade silently — context fills up,
|
|
5
|
+
# tool calls slow down, errors accumulate. No visibility into
|
|
6
|
+
# session health until it's too late.
|
|
7
|
+
#
|
|
8
|
+
# How it works: PreToolUse hook that tracks session metrics:
|
|
9
|
+
# - Tool call count (proxy for context usage)
|
|
10
|
+
# - Error rate (consecutive failures)
|
|
11
|
+
# - Session duration
|
|
12
|
+
# Warns at configurable thresholds.
|
|
13
|
+
#
|
|
14
|
+
# TRIGGER: PreToolUse
|
|
15
|
+
# MATCHER: ""
|
|
16
|
+
#
|
|
17
|
+
# Usage:
|
|
18
|
+
# {
|
|
19
|
+
# "hooks": {
|
|
20
|
+
# "PreToolUse": [{
|
|
21
|
+
# "hooks": [{ "type": "command", "command": "~/.claude/hooks/session-health-monitor.sh" }]
|
|
22
|
+
# }]
|
|
23
|
+
# }
|
|
24
|
+
# }
|
|
25
|
+
|
|
26
|
+
STATE="/tmp/cc-health-$$"
|
|
27
|
+
|
|
28
|
+
# Initialize on first call
|
|
29
|
+
if [ ! -f "$STATE" ]; then
|
|
30
|
+
echo "start=$(date +%s) calls=0 errors=0" > "$STATE"
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
# Read current state
|
|
34
|
+
eval $(cat "$STATE")
|
|
35
|
+
calls=$((calls + 1))
|
|
36
|
+
|
|
37
|
+
# Update state
|
|
38
|
+
echo "start=$start calls=$calls errors=$errors" > "$STATE"
|
|
39
|
+
|
|
40
|
+
# Check thresholds
|
|
41
|
+
DURATION=$(( ($(date +%s) - start) / 60 ))
|
|
42
|
+
|
|
43
|
+
if [ "$calls" -eq 50 ]; then
|
|
44
|
+
echo "ℹ Session health: 50 tool calls, ${DURATION}m elapsed." >&2
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
if [ "$calls" -eq 150 ]; then
|
|
48
|
+
echo "⚠ Session health: 150 tool calls, ${DURATION}m elapsed. Context may be getting full." >&2
|
|
49
|
+
echo "Consider saving state and starting a fresh session." >&2
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
if [ "$calls" -ge 250 ] && [ $((calls % 50)) -eq 0 ]; then
|
|
53
|
+
echo "🔴 Session health: $calls tool calls, ${DURATION}m elapsed. Performance may be degraded." >&2
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
# Duration warning
|
|
57
|
+
if [ "$DURATION" -ge 120 ] && [ $((calls % 100)) -eq 0 ]; then
|
|
58
|
+
echo "⏰ Session running for ${DURATION}m. Long sessions accumulate context drift." >&2
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
exit 0
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
MAX_RSS_MB="${CC_MAX_RSS_MB:-4096}"
|
|
2
|
+
CHECK_INTERVAL=300
|
|
3
|
+
(
|
|
4
|
+
while true; do
|
|
5
|
+
sleep "$CHECK_INTERVAL"
|
|
6
|
+
pgrep -f "claude" | while read pid; do
|
|
7
|
+
RSS_KB=$(ps -o rss= -p "$pid" 2>/dev/null | tr -d ' ')
|
|
8
|
+
[ -z "$RSS_KB" ] && continue
|
|
9
|
+
RSS_MB=$((RSS_KB / 1024))
|
|
10
|
+
if [ "$RSS_MB" -gt "$MAX_RSS_MB" ]; then
|
|
11
|
+
echo "[$(date -Iseconds)] PID $pid: ${RSS_MB}MB > ${MAX_RSS_MB}MB limit" >> ~/.claude/memory-watchdog.log
|
|
12
|
+
kill -15 "$pid" 2>/dev/null
|
|
13
|
+
fi
|
|
14
|
+
done
|
|
15
|
+
done
|
|
16
|
+
) &
|
|
17
|
+
exit 0
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# session-permission-reset-guard.sh — Override session-cached permissions
|
|
3
|
+
#
|
|
4
|
+
# Solves: Sandbox mode caching session-level permissions (#40384).
|
|
5
|
+
# After approving a command once, sandbox caches the approval
|
|
6
|
+
# for the entire session, bypassing the allow list.
|
|
7
|
+
#
|
|
8
|
+
# How it works: PreToolUse hook that enforces per-invocation checks
|
|
9
|
+
# for specified commands, regardless of session cache state.
|
|
10
|
+
# Acts as a secondary permission layer outside the caching system.
|
|
11
|
+
#
|
|
12
|
+
# CONFIG:
|
|
13
|
+
# CC_ALWAYS_CHECK_COMMANDS="git commit:git push:npm publish:cargo install"
|
|
14
|
+
#
|
|
15
|
+
# TRIGGER: PreToolUse
|
|
16
|
+
# MATCHER: "Bash"
|
|
17
|
+
|
|
18
|
+
INPUT=$(cat)
|
|
19
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
20
|
+
[ -z "$COMMAND" ] && exit 0
|
|
21
|
+
|
|
22
|
+
# Commands that should always require hook-level check
|
|
23
|
+
ALWAYS_CHECK="${CC_ALWAYS_CHECK_COMMANDS:-git commit:git push:npm publish:cargo install:pip install}"
|
|
24
|
+
|
|
25
|
+
IFS=':' read -ra PATTERNS <<< "$ALWAYS_CHECK"
|
|
26
|
+
for pattern in "${PATTERNS[@]}"; do
|
|
27
|
+
# Match the pattern anywhere in the command (including chained commands)
|
|
28
|
+
if echo "$COMMAND" | grep -qiF "$pattern"; then
|
|
29
|
+
echo "HOOK CHECK: '$pattern' detected — hook-level review." >&2
|
|
30
|
+
echo "Command: $COMMAND" >&2
|
|
31
|
+
echo "" >&2
|
|
32
|
+
echo "This hook enforces per-invocation checks regardless of" >&2
|
|
33
|
+
echo "session permission caching. To allow, remove this pattern" >&2
|
|
34
|
+
echo "from CC_ALWAYS_CHECK_COMMANDS." >&2
|
|
35
|
+
exit 2
|
|
36
|
+
fi
|
|
37
|
+
done
|
|
38
|
+
|
|
39
|
+
exit 0
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# session-resume-env-fix.sh — Fix CLAUDE_ENV_FILE path on session resume
|
|
3
|
+
#
|
|
4
|
+
# Solves: CLAUDE_ENV_FILE points to startup session directory, but Bash
|
|
5
|
+
# tool loads from resumed session directory (#40391, #24775).
|
|
6
|
+
# Environment variables written by SessionStart hooks are lost.
|
|
7
|
+
#
|
|
8
|
+
# How it works: SessionStart hook that detects resume (source="resume")
|
|
9
|
+
# and copies/symlinks env files from the startup session directory
|
|
10
|
+
# to the resumed session directory.
|
|
11
|
+
#
|
|
12
|
+
# TRIGGER: SessionStart
|
|
13
|
+
# MATCHER: ""
|
|
14
|
+
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
|
|
17
|
+
INPUT=$(cat)
|
|
18
|
+
SOURCE=$(echo "$INPUT" | jq -r '.source // empty' 2>/dev/null)
|
|
19
|
+
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // empty' 2>/dev/null)
|
|
20
|
+
|
|
21
|
+
# Only act on resume events
|
|
22
|
+
[ "$SOURCE" = "resume" ] || exit 0
|
|
23
|
+
[ -n "$SESSION_ID" ] || exit 0
|
|
24
|
+
[ -n "${CLAUDE_ENV_FILE:-}" ] || exit 0
|
|
25
|
+
|
|
26
|
+
# Extract the startup session directory from CLAUDE_ENV_FILE
|
|
27
|
+
STARTUP_DIR=$(dirname "$CLAUDE_ENV_FILE")
|
|
28
|
+
ENV_BASE=$(dirname "$STARTUP_DIR")
|
|
29
|
+
|
|
30
|
+
# Construct the resumed session directory
|
|
31
|
+
RESUME_DIR="${ENV_BASE}/${SESSION_ID}"
|
|
32
|
+
|
|
33
|
+
# If they're the same, no fix needed
|
|
34
|
+
[ "$STARTUP_DIR" != "$RESUME_DIR" ] || exit 0
|
|
35
|
+
|
|
36
|
+
# Create resumed session directory if missing
|
|
37
|
+
mkdir -p "$RESUME_DIR"
|
|
38
|
+
|
|
39
|
+
# Copy all env files from startup dir to resumed dir
|
|
40
|
+
for envfile in "$STARTUP_DIR"/*.sh; do
|
|
41
|
+
[ -f "$envfile" ] || continue
|
|
42
|
+
BASENAME=$(basename "$envfile")
|
|
43
|
+
if [ ! -f "$RESUME_DIR/$BASENAME" ]; then
|
|
44
|
+
cp "$envfile" "$RESUME_DIR/$BASENAME"
|
|
45
|
+
echo "Copied env file to resumed session: $BASENAME" >&2
|
|
46
|
+
fi
|
|
47
|
+
done
|
|
48
|
+
|
|
49
|
+
exit 0
|
|
@@ -24,6 +24,8 @@
|
|
|
24
24
|
# CC_TOOL_WARN_100 — threshold 1 (default: 100)
|
|
25
25
|
# CC_TOOL_WARN_200 — threshold 2 (default: 200)
|
|
26
26
|
# CC_TOOL_WARN_500 — threshold 3 (default: 500)
|
|
27
|
+
#
|
|
28
|
+
# TRIGGER: PreToolUse MATCHER: "Bash"
|
|
27
29
|
|
|
28
30
|
INPUT=$(cat)
|
|
29
31
|
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ================================================================
|
|
3
|
+
# settings-auto-backup.sh — Auto-backup settings on session start
|
|
4
|
+
# ================================================================
|
|
5
|
+
# PURPOSE:
|
|
6
|
+
# Claude Code auto-updates have been observed silently wiping
|
|
7
|
+
# settings.json, settings.local.json, and plugin state. (#40714)
|
|
8
|
+
# This hook creates rolling backups on every session start and
|
|
9
|
+
# warns if settings appear to have been reset.
|
|
10
|
+
#
|
|
11
|
+
# TRIGGER: Notification
|
|
12
|
+
# MATCHER: "SessionStart"
|
|
13
|
+
#
|
|
14
|
+
# BACKUPS: ~/.claude/settings-backups/
|
|
15
|
+
# ================================================================
|
|
16
|
+
|
|
17
|
+
BACKUP_DIR="$HOME/.claude/settings-backups"
|
|
18
|
+
mkdir -p "$BACKUP_DIR"
|
|
19
|
+
|
|
20
|
+
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
|
|
21
|
+
BACKED_UP=0
|
|
22
|
+
|
|
23
|
+
# Backup settings files
|
|
24
|
+
for f in settings.json settings.local.json; do
|
|
25
|
+
SRC="$HOME/.claude/$f"
|
|
26
|
+
if [ -f "$SRC" ] && [ -s "$SRC" ]; then
|
|
27
|
+
cp "$SRC" "$BACKUP_DIR/${f%.json}-${TIMESTAMP}.json"
|
|
28
|
+
BACKED_UP=$((BACKED_UP + 1))
|
|
29
|
+
fi
|
|
30
|
+
done
|
|
31
|
+
|
|
32
|
+
# Keep only last 10 backups per file type
|
|
33
|
+
for prefix in settings settings.local; do
|
|
34
|
+
ls -t "$BACKUP_DIR/${prefix}-"*.json 2>/dev/null | tail -n +11 | xargs rm -f 2>/dev/null
|
|
35
|
+
done
|
|
36
|
+
|
|
37
|
+
# Detect suspicious settings reset
|
|
38
|
+
SETTINGS="$HOME/.claude/settings.json"
|
|
39
|
+
if [ -f "$SETTINGS" ]; then
|
|
40
|
+
KEY_COUNT=$(jq 'keys | length' "$SETTINGS" 2>/dev/null || echo 0)
|
|
41
|
+
if [ "$KEY_COUNT" -le 1 ]; then
|
|
42
|
+
LATEST_BACKUP=$(ls -t "$BACKUP_DIR/settings-"*.json 2>/dev/null | head -2 | tail -1)
|
|
43
|
+
if [ -n "$LATEST_BACKUP" ]; then
|
|
44
|
+
BACKUP_KEYS=$(jq 'keys | length' "$LATEST_BACKUP" 2>/dev/null || echo 0)
|
|
45
|
+
if [ "$BACKUP_KEYS" -gt "$KEY_COUNT" ]; then
|
|
46
|
+
echo "⚠ Settings may have been reset ($KEY_COUNT keys vs $BACKUP_KEYS in backup)" >&2
|
|
47
|
+
echo " Restore: cp '$LATEST_BACKUP' '$SETTINGS'" >&2
|
|
48
|
+
fi
|
|
49
|
+
fi
|
|
50
|
+
fi
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
exit 0
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# settings-mutation-detector.sh — Detect unauthorized changes to Claude settings files
|
|
3
|
+
#
|
|
4
|
+
# Solves: Claude Code can modify its own settings files during
|
|
5
|
+
# a session, potentially disabling safety hooks or
|
|
6
|
+
# changing permissions without user awareness.
|
|
7
|
+
#
|
|
8
|
+
# How it works: On first run, takes a hash of key settings files.
|
|
9
|
+
# On subsequent runs, compares the current hash. If changed,
|
|
10
|
+
# warns the user. This catches silent permission escalation
|
|
11
|
+
# or hook removal.
|
|
12
|
+
#
|
|
13
|
+
# TRIGGER: PostToolUse
|
|
14
|
+
# MATCHER: ""
|
|
15
|
+
|
|
16
|
+
set -euo pipefail
|
|
17
|
+
|
|
18
|
+
HASH_FILE="/tmp/claude-settings-hash-$$"
|
|
19
|
+
|
|
20
|
+
# Files to monitor
|
|
21
|
+
SETTINGS_FILES=""
|
|
22
|
+
for f in \
|
|
23
|
+
".claude/settings.json" \
|
|
24
|
+
".claude/settings.local.json" \
|
|
25
|
+
"${HOME}/.claude/settings.json" \
|
|
26
|
+
"${HOME}/.claude/settings.local.json"; do
|
|
27
|
+
[ -f "$f" ] && SETTINGS_FILES="$SETTINGS_FILES $f"
|
|
28
|
+
done
|
|
29
|
+
|
|
30
|
+
[ -z "$SETTINGS_FILES" ] && exit 0
|
|
31
|
+
|
|
32
|
+
# Calculate current hash
|
|
33
|
+
CURRENT_HASH=$(cat $SETTINGS_FILES 2>/dev/null | md5sum | cut -d' ' -f1)
|
|
34
|
+
|
|
35
|
+
if [ -f "$HASH_FILE" ]; then
|
|
36
|
+
PREV_HASH=$(cat "$HASH_FILE")
|
|
37
|
+
if [ "$CURRENT_HASH" != "$PREV_HASH" ]; then
|
|
38
|
+
echo "WARNING: Claude settings files were modified during this session!" >&2
|
|
39
|
+
echo " Files monitored: $SETTINGS_FILES" >&2
|
|
40
|
+
echo " Review changes to ensure hooks and permissions are intact." >&2
|
|
41
|
+
fi
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
echo "$CURRENT_HASH" > "$HASH_FILE"
|
|
45
|
+
exit 0
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
# This hook unwraps interpreter one-liners and checks the inner command.
|
|
14
14
|
#
|
|
15
15
|
# Usage: PreToolUse hook on "Bash"
|
|
16
|
+
#
|
|
17
|
+
# TRIGGER: PreToolUse MATCHER: "Bash"
|
|
16
18
|
|
|
17
19
|
INPUT=$(cat)
|
|
18
20
|
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
package/examples/skill-gate.sh
CHANGED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# skill-injection-detector.sh — Detect silently injected skills/plugins
|
|
3
|
+
#
|
|
4
|
+
# Solves: Skills and plugins from claude.ai silently injected into Claude Code
|
|
5
|
+
# sessions (#39686). External tool definitions bloat context and
|
|
6
|
+
# can override local behavior without user awareness.
|
|
7
|
+
#
|
|
8
|
+
# How it works: Notification hook that monitors for unexpected skill/plugin
|
|
9
|
+
# loading messages. Warns when tools are loaded from external sources.
|
|
10
|
+
# Also checks MCP config for unexpected servers.
|
|
11
|
+
#
|
|
12
|
+
# TRIGGER: PreToolUse
|
|
13
|
+
# MATCHER: ""
|
|
14
|
+
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
|
|
17
|
+
INPUT=$(cat)
|
|
18
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
19
|
+
|
|
20
|
+
# Check MCP tool calls for unexpected servers
|
|
21
|
+
if [ "$TOOL" = "Bash" ]; then
|
|
22
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
23
|
+
# Detect MCP server addition
|
|
24
|
+
if echo "$COMMAND" | grep -qE 'claude\s+mcp\s+add'; then
|
|
25
|
+
echo "WARNING: MCP server addition detected." >&2
|
|
26
|
+
echo "Command: $COMMAND" >&2
|
|
27
|
+
echo "Verify this server is expected before proceeding." >&2
|
|
28
|
+
fi
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
# Check for skill/plugin invocation from unexpected sources
|
|
32
|
+
if echo "$INPUT" | jq -e '.tool_input.skill // empty' 2>/dev/null | grep -qv '^$'; then
|
|
33
|
+
SKILL=$(echo "$INPUT" | jq -r '.tool_input.skill' 2>/dev/null)
|
|
34
|
+
# Check if skill is local
|
|
35
|
+
if [ ! -f ".claude/skills/${SKILL}/SKILL.md" ] && [ ! -f ".claude/skills/${SKILL}.md" ]; then
|
|
36
|
+
echo "WARNING: Skill '$SKILL' invoked but not found locally." >&2
|
|
37
|
+
echo "This may be an externally injected skill." >&2
|
|
38
|
+
fi
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
exit 0
|