Rubric
Contents — domains, guide and mocks

Hooks as safety controls

CCDV-F 7.312 min read · checked 21 September 2026

Task statementClaude Hooks (1.0%) — using hooks for guardrails and safety controls to prevent destructive actions

Where a hook sits in the tool loop

Claude
Harness
Hook
Tool
Step 1: Claude to Harness: Requests Bash(rm -rf …)
Step 2: Harness to Hook: PreToolUse with tool_input
Step 3: Hook to Harness: Exit 2 · deny + reason
Step 4: Harness to Claude: Blocked, with the reason
Step 5: Claude to Harness: Tries a safe alternative
Step 6: Harness to Tool: Allowed — tool runs
PreToolUse runs between the model asking and the tool running — the only point at which an action can still be stopped.

What a hook actually is

A hook is a handler you configure to run at a named point in the agent's lifecycle. It receives JSON describing what is about to happen — the session id, the working directory, the event name, and for tool events the tool_name and the tool_input — and it answers by exit code and by JSON on standard output. Handlers can be a shell command, an HTTP endpoint, a tool on an MCP server, or a prompt or subagent evaluated by a model.

The crucial property is that a hook is ordinary code. It runs whether or not the model is having a good day, its decision does not depend on how the request was phrased, and you can unit-test it. That is what makes it the right instrument for preventing destructive actions, and it is the comparison every exam item about hooks is drawing.

ControlRunsDecides byGood for
An instruction in the prompt or CLAUDE.mdWhen the model attends to itModel judgementStyle, conventions, preferences
A permission allow/deny ruleEvery matching tool callConfigured patternsBroad policy on named tools and commands
A hookEvery matching eventYour code, on the actual inputContext-dependent safety checks, audit, blocking
SandboxingEvery commandFilesystem and network isolationEnforcement that does not depend on command text

Blocking a destructive action

PreToolUse is the event that can stop something. A hook blocks by exiting with status 2, and it should also return JSON naming the decision and the reason so the model learns why and can try something else. Exit 0 means the hook has no objection and normal permission handling continues; any other exit status is a non-blocking error and the action proceeds.

A `PreToolUse` hook that refuses a destructive commandbash
#!/bin/bash
# .claude/hooks/block-destructive.sh — receives the event JSON on stdin.
COMMAND=$(jq -r '.tool_input.command')

case "$COMMAND" in
  *"rm -rf"*|*"DROP TABLE"*|*"git push --force"*)
    # Tell the model why, then block. Exit 2 is the blocking signal.
    jq -n '{hookSpecificOutput: {
              hookEventName: "PreToolUse",
              permissionDecision: "deny",
              permissionDecisionReason:
                "Destructive command blocked by policy hook."}}'
    exit 2 ;;
esac

exit 0   # No objection — normal permission flow applies.

Hooks are configured in settings, with a matcher selecting which events reach them. A matcher of Bash fires only for the Bash tool, Edit|Write for either of two tools, and anything containing regular-expression characters is treated as a pattern — mcp__.*__.* reaches every tool provided by an MCP server. All matching hooks run in parallel.

Wiring it up in settingsjson
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-destructive.sh",
            "timeout": 10,
            "statusMessage": "Checking command safety..."
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/audit.sh" }
        ]
      }
    ]
  }
}
Exit statusMeaningEffect on the action
0Success; JSON on stdout is readProceeds under normal permission handling
2Blocking errorBlocked on events that support blocking, with the reason passed back
Anything elseNon-blocking errorThe action proceeds — the hook failed open
Timed outCancelled, output discardedOn PreToolUse it does not block

The other events, and what they are for

Blocking is the headline, but the lifecycle has points for the rest of a safety story too. The documented set is large; a handful carry most of the safety work.

EventFiresTypical safety use
PreToolUseBefore a tool runsBlock destructive actions; require a condition to hold
PostToolUseAfter a tool succeedsAudit log, secret scanning, running a formatter or test
UserPromptSubmitBefore Claude processes a promptScreen input; add context; block a disallowed request
SessionStartWhen a session starts or resumesLoad policy or environment; record who started it
ConfigChangeWhen configuration changesAudit or block changes to settings during a session
StopWhen Claude finishes respondingCheck the turn's outcome before it is presented

Where the hook is configured decides who can change it. Project settings are shared through version control; a local settings file is personal and not committed; managed policy settings are administered centrally and cannot be overridden by a user. A safety hook that matters to the organisation belongs in managed settings — otherwise the control is only as strong as the most impatient engineer's local configuration.

Choosing the right control

How should this rule be enforced?
  • A preference or convention
    Instructionsguidance the model follows
  • A named tool or command
    Permission ruleallow, ask or deny
  • Needs your logic or data
    PreToolUse hookinspect, decide, exit 2
  • Must hold whatever the text says
    Sandboxfilesystem and network isolation
Hooks are not a replacement for permissions or sandboxing. They are the layer for checks that need your own logic or your own data.

That last branch matters. The security documentation notes that a deny rule matches a command as written, so text-based matching can be evaded by an equivalent command spelled differently; for enforcement that does not depend on the wording, network and filesystem isolation is the stronger instrument. A hook reading tool_input has the same exposure, so treat pattern matching as a filter over an already-restricted environment, not as the wall itself.

Traps the wrong answers are built from

Tempting but wrongDo this instead
Relying on CLAUDE.md or the system prompt to forbid destructive commandsEnforce it with a PreToolUse hook, a deny rule or a sandbox.
Returning a non-zero exit code other than 2 to block an actionExit 2; every other non-zero status is a non-blocking error and the action proceeds.
Letting a safety hook crash or time out unhandledFail closed: deny when the check cannot complete, and test that path.
Blocking silently with no reasonReturn permissionDecisionReason so the model can adapt rather than retry.
Keeping an organisation-wide safety hook in a personal settings fileShip it as managed policy settings that users cannot override.
Treating string matching on commands as complete protectionPair it with sandboxing, which does not depend on how the command is spelled.

You should now be able to

  • Explain what a hook is, what input it receives and how it returns a decision.
  • Write a PreToolUse hook that blocks a destructive action and explains why.
  • Choose the right lifecycle event for a given safety or audit requirement.
  • Configure matchers and pick the settings scope that makes a control enforceable.
  • Decide whether a hook should fail open or closed, and justify it.

Practice questions

Original questions written for this lesson, in the exam’s style. Answer first, then open the reasoning — every option is explained, including why the wrong ones are tempting.

  1. Question 1

    A team adds a rule to their project instructions: “Never run git push --force on a shared branch.” It is respected most of the time, but a force push reached the main branch twice in a quarter.

    What is the appropriate fix?

    1. ARestate the rule more emphatically and add an example of the forbidden command.
    2. BAdd a PreToolUse hook on the Bash tool that inspects the command and exits 2.
    3. CAdd a PostToolUse hook that alerts the team when a force push happens.
    4. DMove the rule into the system prompt rather than the project instructions.
    Show answer and reasoning
    1. AIncorrect. It remains guidance interpreted by a non-deterministic component; the failure mode is unchanged.
    2. BCorrect. A hook runs on every matching call and refuses deterministically, which is what the rule needed to be.
    3. CIncorrect. It reports the incident after the history has already been rewritten; detection is not prevention.
    4. DIncorrect. Relocating an instruction does not turn advice into enforcement.
  2. Question 2

    A safety hook is meant to block risky database commands. In testing, the command is blocked correctly. In production, the hook sometimes takes longer than its configured timeout because it queries an inventory service, and during those runs the command executes.

    What are the two correct conclusions? (Select 2.)

    1. AA PreToolUse hook that times out does not block the action.
    2. BThe hook should avoid slow external calls, or hold a local copy of what it needs.
    3. CRaising the timeout to several minutes makes the control reliable.
    4. DThe hook should return exit code 1 on timeout so the action is blocked.
    5. EBlocking should be moved to a PostToolUse hook instead.
    6. FThe behaviour is a bug in the harness and should be reported.
    Show answer and reasoning
    1. ACorrect. The documented behaviour is that the hook is cancelled and its output discarded; on this event the action proceeds.
    2. BCorrect. A safety check on the critical path must complete well inside its timeout to be reliable.
    3. CIncorrect. It narrows the window while making every risky command wait; it does not remove the fail-open behaviour.
    4. DIncorrect. A hook that has timed out returns nothing, and in any case only exit 2 blocks.
    5. EIncorrect. PostToolUse fires after the tool has already run; it cannot prevent anything.
    6. FIncorrect. It is documented behaviour for this event, so the design must account for it.
  3. Question 3

    A compliance team wants every file edit an agent makes recorded in an append-only log, for every engineer in the organisation, with no way for an individual to disable it.

    Which configuration meets the requirement?

    1. AA PreToolUse hook on Edit|Write in each project's committed settings file.
    2. BA PostToolUse hook on Edit|Write, shipped as managed policy settings.
    3. CA SessionEnd hook that writes a summary of the session's changes.
    4. DA PostToolUse hook in each engineer's personal settings file.
    Show answer and reasoning
    1. AIncorrect. It fires before the edit, so it cannot record the result, and a project file can be changed locally.
    2. BCorrect. It fires after a successful edit, and managed settings are administered centrally and cannot be overridden.
    3. CIncorrect. It captures far less detail and produces nothing at all if a session ends abnormally.
    4. DIncorrect. Personal settings are local and optional, which is precisely what the requirement excludes.

Sources

Drafted with AI assistance and checked against the sources above; expert review is in progress. Spotted an error? Tell us and it gets fixed, dated and listed on how this is written.