跳到主要内容
知仓学习社ZHICANG

cc-hooks

Configure Claude Code hooks and narrow enforcement guards. Use when: the caller requests hook installation, repair or policy changes; a hook is not …

读凭据改身份文件写文件联网严重 3 · 高危 42hashgraph-online/awesome-codex-plugins

它会碰到什么

扫了多少20 个文本文件,145 KB
它会碰到什么读凭据改身份文件写文件联网
命中总数48 处
命中统计严重 3 · 高 42 · 中 0 · 低 1
逐条看命中(30 条严重或高危)
  • 严重 references/DCG-RCH.md:171cred-paths
    4. .env / .rch.env files
  • 严重 references/PATTERNS.md:107cred-paths
    "command": "jq -r '.tool_input.file_path' | grep -qE '(\\.env|\\.git/|credentials|secrets|password)' && { echo 'Blocked: sensitive file' >&2; exit 2; } || exit 
  • 严重 references/PATTERNS.md:129cred-paths
    ".env.production"
  • hooks/read-budget-guard.sh:8identity-write
    # CLAUDE.md was advisory and ignored (the Spotify finding), so it lives here as
  • policies/policies.json:19identity-write
    "rationale": "CLAUDE.md footgun row + memory agentops-br-private-ledger. Only the explicit '_beads' path form is matched; 'git add -A' silently sweeping _beads/
  • policies/policies.json:111identity-write
    "rationale": "The first policy guarding the PRODUCT's invariant rather than this repo's own artifacts. CLAUDE.md 'Validate once, fresh' and docs/architecture/rp
  • references/DCG-RCH.md:9identity-config-write
    "hooks": {
  • references/DCG-RCH.md:13identity-config-write
    "hooks": [
  • references/DCG-RCH.md:318identity-config-write
    {"PreToolUse": {"tools": ["Bash"], "hooks": [...]}}
  • references/DCG-RCH.md:321identity-config-write
    {"PreToolUse": [{"matcher": "Bash", "hooks": [...]}]}
  • references/HOOK-EVENTS.md:219identity-config-write
    "hooks": {
  • references/HOOK-EVENTS.md:222identity-config-write
    "hooks": [
  • references/HOOK-EVENTS.md:350identity-config-write
    "hooks": {
  • references/HOOK-EVENTS.md:354identity-config-write
    "hooks": [
  • references/HOOK-EVENTS.md:360identity-config-write
    "hooks": [
  • references/HOOK-EVENTS.md:425identity-config-write
    "hooks": [{ "type": "command", "command": "log-memory-ops.sh" }]
  • references/INSTALLED-SKILL-EDIT-GUARD.md:74identity-config-write
    "hooks": {
  • references/INSTALLED-SKILL-EDIT-GUARD.md:78identity-config-write
    "hooks": [
  • references/PATTERNS.md:11identity-config-write
    "hooks": {
  • references/PATTERNS.md:15identity-config-write
    "hooks": [
  • references/PATTERNS.md:31identity-config-write
    "hooks": {
  • references/PATTERNS.md:35identity-config-write
    "hooks": [
  • references/PATTERNS.md:51identity-config-write
    "hooks": {
  • references/PATTERNS.md:55identity-config-write
    "hooks": [
  • references/PATTERNS.md:100identity-config-write
    "hooks": {
  • references/PATTERNS.md:104identity-config-write
    "hooks": [
  • references/PATTERNS.md:206identity-config-write
    "hooks": {
  • references/PATTERNS.md:210identity-config-write
    "hooks": [
  • references/PATTERNS.md:252identity-config-write
    "hooks": {
  • references/PATTERNS.md:256identity-config-write
    "hooks": [

这一栏是扫描器报的事实,不是结论。命中多不等于有毒(安全工具、规则库、示例脚本本来就会包含危险写法),命中少也不等于干净。它和你手上的凭据、文件、网络有什么关系,需要你自己看。

技能内容

Claude Code Hooks

Shell commands that fire at specific points in Claude Code's lifecycle.

Hooks enforce mechanically what prose cannot: a model can reason its way past

an instruction, but it cannot reason its way past an exit 2 — which is exactly

why every hook must be narrow, silent, and reversible.

Named failure mode — chatty happy path: a hook that emits stdout on exit 0

corrupts the tool call it was guarding; silence on success is part of the

contract, not a style preference.

Prompt

Add a PreToolUse hook to fleet-router/.claude/settings.json that blocks `git push --force` on the main branch. Keep it silent on exit 0, exit 2 with a message on block, and confirm it fires with a manual test invocation before committing the change.

It's working if

  • The hook script exits 2 with a stderr message when it blocks git push --force, and exit 0 with no stdout on the allowed path.
  • .claude/settings.json gains one matcher entry for the new hook, alongside the existing hooks list rather than replacing it.
  • A manual test invocation against the new matcher shows the block firing in the transcript, with exit 2 visible, before the change gets committed.
  • The hook inspects only the PreToolUse call it guards, keeping every other file untouched.

Constraints

  • Enforcement hooks (the PreToolUse policy dispatcher) ship by DEFAULT: plugin installs auto-wire hooks/hooks.json; skill copies and checkouts wire with one command (scripts/install-hooks.sh). Operators can disable per host (/plugin disable, or remove the settings matchers).
  • Injection hooks (SessionStart/UserPromptSubmit context stuffing) stay dead — the #511 teardown proved delta=0 at 10.35M resident tokens. Never ship one; the hookless-cold-start gate still enforces this.
  • Keep the happy path silent and block only with the event's documented exit/JSON contract because stray stdout can corrupt a tool call.
  • Bound Stop hooks with stop_hook_active and scope matchers narrowly to prevent recursion and unrelated-command interception.

<!-- TOC: Quick Start | Events | Blocking | Writing Hooks | Anti-Patterns | References -->

Quick Start

Add to ~/.claude/settings.json (user) or .claude/settings.json (project):

{"hooks":{"PreToolUse":[{"matcher":"Bash","hooks":[{"type":"command","command":"my-validator.sh"}]}]}}

Hook Events

| Event | When | Blocks? | Common Use |

|-------|------|---------|------------|

| PreToolUse | Before tool runs | Yes | Block/modify commands |

| PostToolUse | After tool succeeds | Feedback | Auto-format, lint |

| PermissionRequest | Permission dialog | Yes | Auto-approve/deny |

| UserPromptSubmit | Prompt submitted | Yes | Add context, validate |

| Stop | Claude finishes | Yes | Force continue |

| SessionStart | Session begins | No | Load context, set env |

| Notification | Notifications | No | Desktop alerts |

Full schemas: [HOOK-EVENTS.md](references/HOOK-EVENTS.md)

Matchers

"Bash"              → exact match
"Edit|Write"        → regex OR
"mcp__.*__write"    → MCP tools
"*" or ""           → all tools

Tools: Bash, Read, Write, Edit, Glob, Grep, Task, WebFetch, WebSearch

Exit Codes

| Code | Effect |

|------|--------|

| 0 | Success - JSON parsed from stdout |

| 2 | Block - stderr fed to Claude |

| Other | Non-blocking error |

Blocking a Tool

Simple (exit 2):

echo "Blocked: reason" >&2 && exit 2

JSON (exit 0):

{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"Blocked"}}

Decisions: "allow" (auto-approve), "deny" (block), "ask" (show dialog)

Modifying Input

{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow",
  "updatedInput":{"command":"modified-command"}}}

Real-World: DCG + RCH

{"hooks":{"PreToolUse":[{"matcher":"Bash","hooks":[
  {"type":"command","command":"dcg"},
  {"type":"command","command":"rch"}
]}]}}
  • DCG: Blocks git reset --hard, rm -rf, git push --force
  • RCH: Routes builds to remote workers

Details: [DCG-RCH.md](references/DCG-RCH.md)

Skill-First Coordination Guard (opt-in)

A copy-paste PreToolUse recipe that nudges agents to **load the coordination

skill before hand-rolling the am/atm/ntm/tmux send-keys CLI**. This

recipe auto-installs nothing; you opt in per host (unlike the policy

dispatcher, which ships by default).

Context-budget doctrine for hooks: hooks are the most powerful enforcement

(mechanical, can't be reasoned past) but they pollute context — use sparingly. A

hook must be SILENT on the happy path (exit 0, no stdout/stderr), fire ONLY on a

real violation (ideally once per session, sentinel-gated), prefer PreToolUse

violation-guards over UserPromptSubmit/SessionStart per-turn injectors, and

NEVER emit stray stdout on an exit-0 PreToolUse path (it is parsed as JSON and

breaks the tool call). Block via exit 2 + stderr.

The recipe ships both scripts verbatim, a precise head-only matcher (so a

br create --body "...am/atm/ntm..." never false-fires), the two-matcher

opt-in settings.json snippet, and a bats test proving every fire/silent case.

Recipe: [SKILL-FIRST-COORDINATION-GUARD.md](references/SKILL-FIRST-COORDINATION-GUARD.md)

Installed-Skill-Edit Guard (opt-in)

A PreToolUse Edit|Write guard that routes an edit of an installed skill copy

(/.claude/skills/*, .codex, .gemini) back to the repo source of truth

skills/<name>/. This is a TRUE mistake-token — editing an installed/symlinked

copy has no legitimate form (overwritten on install, or symlinks through to the

factory checkout). Zero false-positive surface: it matches tool_input.file_path

only, so a doc that merely mentions claude/skills in its body never fires.

Reversible → it ROUTES (exit 2 + one-line redirect), not hard-blocks. Silent on

every other path; fires once per session. Ships INERT — opt-in installer:

scripts/install-installed-skill-edit-guard.sh   # user scope; --project for project

Recipe: [INSTALLED-SKILL-EDIT-GUARD.md](references/INSTALLED-SKILL-EDIT-GUARD.md)

Value-proof (why this guard survives the hookless teardown)

The keystone guard ships gate-blind per-fire telemetry: on each fire it

appends exactly one JSONL line — {ts, session, token_class, path_sha256} — to

${AGENTOPS_HOME:-~/.agents/ao}/guardrail-telemetry.jsonl (override with

AGENTOPS_GUARDRAIL_TELEMETRY). The path is SHA-256 hashed, never raw

(privacy); nothing is written on the happy path; the sensor is inert until the

guard is installed and fires. The pre-registered methodology — metric =

declining fire-ATTEMPT rate over time (a signal the redirect cannot fake, NOT the

circular hand-roll rate), minimum N, noise floor, and **null-at-small-N is an

acceptable outcome** — satisfies ADR-0002 l.58 ("test or eval evidence showing

positive value"), the criterion whose absence killed 2.x hooks (#511).

Methodology: [GUARDRAIL-VALUE-PROOF.md](references/GUARDRAIL-VALUE-PROOF.md)

Read-Budget Guard (opt-in)

A PreToolUse Read|Bash guard that DENIES an **unbounded read over the line

budget** (AOP_READ_BUDGET_LINES, default 350): a Read with no limit, or a

cat/head/tail whose effective line count exceeds it. The Spotify finding:

the same rule in CLAUDE.md was advisory and ignored, and an over-budget read

re-sends its lines on every later turn. The predicate is a LOOKUP (wc -l on

the exact argument), so it is a standalone guard, never a registry policy. A

limit-bounded slice, a file at/below budget, a pipe, a redirect or quoted text

that merely mentions cat passes. Literal quoted/escaped paths are preserved;

unsupported shell syntax and directory-changing chains fail open. Negative

head counts use the actual number of retained lines for GNU head; rejected

Darwin system utility flags pass after checking executable identity. Positive

signed head counts are bounded by their numeric value. Nothing un-reads bytes

once in context → every attempt blocks (exit 2 + stderr): full message once per session naming the two

correct moves (slice it, or delegate to the plugin's agentops:bulk-reader

subagent / agentops:bulk-read workflow), one short line after. Use bare names

only for standalone definitions or links when the runtime lists them. Waive once with

AOP_WAIVE=core.context:unbounded-read; hashed telemetry adds tool, lines,

budget plus the dispatcher's mode/decision pair. Ships INERT — opt-in installer:

scripts/install-read-budget-guard.sh   # user scope; --project for project

Recipe: [READ-BUDGET-GUARD.md](references/READ-BUDGET-GUARD.md)

Policy Dispatch Engine (ships by default)

The admission-control layer (epic age-4qw1): one PreToolUse dispatcher —

[hooks/policy-dispatch.sh](hooks/policy-dispatch.sh) — evaluating a

policies-as-data registry

([policies/policies.json](policies/policies.json), contract

schemas/hooks-manifest.v2.schema.json) instead of N hand-wired settings

entries. This is the membrane at tool-call altitude: same vocabulary, lower

altitude than the pawl/gate at push time.

Per policy: dcg-style id (domain.object:token), mode: deny | route | audit,

matchers (tool + command/file_path regex), a route_message that names THE

correct tool, a rationale, and a pre-registered value_proof (the ADR-0002

lease-on-life: no proof accruing → retire the policy).

Predicate discipline, schema-enforced (the #511 anti-lesson): only

predicate_class: pure — syntactic mistake-tokens over the command or file

path — may deny/route. Lookup/stateful predicates ship audit-only until

promoted with reviewed fires.

[scripts/lint-policies.sh](scripts/lint-policies.sh) enforces this mechanically

(jq-only; runs in bats and CI).

Accepted false-positive surface: because a pure predicate matches its token

anywhere in the raw command string, a protected token quoted as data (a commit

message body, a dcg test "..." probe, a here-doc payload) can still fire even

though nothing harmful would run. This is the deliberate cost of the

pure-only-may-deny rule — the alternative (repo/context lookups) is exactly the

stateful predicate the discipline bars from deny. Every fire is reversible: a

one-shot AOP_WAIVE=<policy-id> or a policy-waivers line clears it.

Semantics: happy path = exit 0, zero output. deny = exit 2 + one stderr

route line (full message once per session, short line after — every attempt

still blocks). route = exit 0 + permissionDecision:"ask" JSON. audit =

allow + record. Every fire appends one hashed guardrail-telemetry line

(token_class = policy id, plus mode/decision). Waive once with

AOP_WAIVE=<policy-id>, or a policy-waivers file line

<policy-id> <expiry-epoch>. Missing registry or jq fails OPEN.

Enforce cohort (all pure-regex, high-pain). The first four are the day-1

maintainer cohort (age-wnyt) — they guard this repository's artifacts. The

fifth guards the product's own invariant and therefore fires on every

consumer repo, not just this one:

| Policy | Blocks | Routes to |

|---|---|---|

| core.git:add-beads-ledger | git add naming _beads/ (private ledger leak is one-way) | push the ledger repo itself — never git add _beads in the public tree |

| core.provenance:ledger-hand-append | redirect/tee/Edit/Write onto docs/provenance/ledger.jsonl (hash-chained, sealed) | ao provenance add |

| core.skills:copy-into-installed | cp/rsync/mv INTO ~/.claude|.codex|.gemini/skills (dest-position enforced) | ao skills link |

| core.skills:edit-installed-copy | Edit/Write of an installed skill copy (file_path only — prose can never fire it) | edit repo skills/<name>/ |

| core.verdicts:hand-edit | Edit/Write, or Bash >/>>/tee/cp/rsync/mv INTO .agents/ao/verdicts/ (dest-position enforced) — the filename IS the SHA-256 of the content, so a hand edit breaks digest identity | re-run validation and let it persist a fresh artifact (validate.py store-verdict) |

core.verdicts:hand-edit is the one policy whose subject is the promise

rather than the repo: a verdict that no longer hashes to its own filename is

forged evidence, and nothing above the tool-call altitude catches it. Reading

the store is untouched — cat/ls/jq/rg/diff over a verdict, and

copying one OUT for inspection, never fire; only writes landing IN the store

do — including in-place editors (sed -i, perl -pi/-ni) and deleters

(rm, unlink, shred), matched as flag-tokens so a read whose script text

merely contains -i stays silent (bats-proven both directions). Remaining

disclosed gap: the noclobber override redirect (>|).

How it reaches users — every install path delivers hooks:

| Install path | Delivery |

|---|---|

| Claude Code plugin (claude plugin install agentops@agentops-marketplace) | Automatic — the plugin bundles hooks/hooks.json (${CLAUDE_PLUGIN_ROOT} paths); hooks are active on install, no wiring step |

| npx skills@latest add boshu2/agentops / skills.sh copy | The skill package carries its own installer: ~/.claude/skills/cc-hooks/scripts/install-hooks.sh (one command; file copies cannot self-wire) |

| git clone / brew checkout | scripts/install-policy-dispatch.sh (delegates to the same skill-embedded installer) |

The installer lints the registry before wiring, backs up settings, and is

idempotent. Disable per host with /plugin disable agentops or by removing the

two PreToolUse matchers from settings.

Contract tests: tests/scripts/policy-dispatch.bats (block+message+telemetry

per policy, stray-stdout hazard, waivers, audit/route modes, fail-open).

Writing Your Own Hook

Minimal Python:

#!/usr/bin/env python3
import json, sys

data = json.load(sys.stdin)
cmd = data.get('tool_input', {}).get('command', '')

if 'dangerous' in cmd:
    print("Blocked: dangerous", file=sys.stderr)
    sys.exit(2)

sys.exit(0)  # Allow

Hook input (stdin):

{"tool_name":"Bash","tool_input":{"command":"npm test"},"session_id":"...","cwd":"..."}

Environment Variables

| Variable | Scope | Purpose |

|----------|-------|---------|

| CLAUDE_PROJECT_DIR | All | Project root |

| CLAUDE_ENV_FILE | SessionStart/Setup | Persist env vars |

Stop Hook (Force Continue)

{"decision":"block","reason":"Tests failing. Fix before stopping."}

Critical: Check stop_hook_active to prevent infinite loops.

Anti-Patterns

| Don't | Do |

|-------|-----|

| Old object format | Array format with matcher |

| Unquoted $VAR | "$VAR" |

| Exit 2 with JSON | Exit 2 uses stderr only |

| Skip stop_hook_active check | Always check in Stop hooks |

Debugging

claude --debug  # Hook execution details
/hooks          # View/edit in REPL

Output Specification

  • Path: user ~/.claude/settings.json or project .claude/settings.json, plus explicitly named hook scripts. The PreToolUse policy dispatcher ships by default (every install path wires it — see "Policy Dispatch Engine"); the additional guard recipes (skill-first coordination, standalone installed-skill-edit, read-budget) stay inert until opted in.
  • Filename: preserve settings.json; give scripts descriptive executable filenames rather than embedding large shell programs in JSON.
  • Format: valid Claude hook JSON using event arrays, matchers, and command objects; hook stdout/stderr and exit codes follow the selected event schema.
  • Exit code: validate with jq -e '.hooks | type=="object"' <settings.json> and a representative silent/fire test for each matcher; any parse error, noisy happy path, or recursion risk blocks activation.
  • Downstream handoff: consumed by the operator only after the exact scope, reversal command, test evidence, and opt-in location are reported.

Quality Checklist

  • The matcher fires on the intended event/input and stays silent on representative near misses.
  • Blocking and allow paths use the documented exit code and output channel without leaking context.
  • The hook is reversible, narrowly scoped, recursion-safe, and clearly labeled as opt-in host policy.

References

  • [HOOK-EVENTS.md](references/HOOK-EVENTS.md) - All events with full schemas
  • [DCG-RCH.md](references/DCG-RCH.md) - Production examples (dcg, rch)
  • [INSTALLED-SKILL-EDIT-GUARD.md](references/INSTALLED-SKILL-EDIT-GUARD.md) - Opt-in guard routing installed-skill edits to repo skills/ (keystone)
  • [READ-BUDGET-GUARD.md](references/READ-BUDGET-GUARD.md) - Opt-in guard denying unbounded reads over the line budget; pairs with bulk-read / code-write delegation
  • [GUARDRAIL-VALUE-PROOF.md](references/GUARDRAIL-VALUE-PROOF.md) - Pre-registered value-proof methodology + per-fire telemetry contract (ADR-0002 l.58)
  • [PATTERNS.md](references/PATTERNS.md) - Auto-format, logging, notifications
  • [JSON-OUTPUT.md](references/JSON-OUTPUT.md) - Response schemas

想直接用这个技能?

本站把开放许可(MIT / Apache 等)的技能按仓库打包整理到网盘,点一下转存到你自己的网盘,不用一个个从 GitHub 拉。许可未声明的技能只给原始仓库链接,不打包。