Curriculum › Security and Safety · 8.1% of the exam

Claude hooks

What you'll be able to do

  • Identify when a requirement demands a hook rather than a prompt
  • Place hooks at the right lifecycle event and scope
  • Explain why determinism is the property that matters
  • Combine hooks with the other guardrail layers

What you’ll be able to do

  • Identify when a requirement demands a hook rather than a prompt
  • Place hooks at the right lifecycle event and scope
  • Explain why determinism is the property that matters
  • Combine hooks with the other guardrail layers

What you need to know

The property that makes hooks different

Everything else in Domain 7 shapes probability. A hardened system prompt makes bad behaviour less likely. Input filtering catches most known patterns. Hooks are the only mechanism here that is deterministic — code runs at a fixed point in the loop and its decision does not depend on what the model concluded.

That's the whole exam signal. When a question says a rule must hold "no matter what the model decides," "regardless of the conversation," or "even if the agent is manipulated," every prompt-wording option is a distractor.

Where they sit

Hooks attach at fixed points in the loop — most usefully PreToolUse, which fires at the request-execute gap from lesson 1.2, between the model requesting a tool call and your code running it. They are configured in settings.json at user, project, local, or enterprise-managed scope, and a matcher targets them at specific tool names.

a PreToolUse hook, wired in settings.json
{ "hooks": { "PreToolUse": [ { "matcher": "Bash|Write|Edit", "hooks": [{ "type": "command", "command": ".claude/policy.py" }] } ] } }

Two ways for the handler to refuse. The blunt one is an exit code; the explicit one is structured JSON:

policy.py — structured decision on stdout
import json, sys event = json.load(sys.stdin) # tool_name, tool_input, session_id, cwd...

if event[“tool_name”] == “Bash” and “curl” in event[“tool_input”].get(“command”,""): print(json.dumps({ “hookSpecificOutput”: { “hookEventName”: “PreToolUse”, “permissionDecision”: “deny”, “permissionDecisionReason”: “External network calls require approval” }})) sys.exit(0)

sys.exit(0) # or sys.exit(2) to block outright, with the reason on stderr

Exit code 2 is the hard block: on an event that can block it stops the call whether or not you print JSON, and a permissionDecision of "allow" cannot override it. The event names are PascalCase — PreToolUse, PostToolUse, UserPromptSubmit, Stop, SessionStart, PreCompact and others — and matcher applies only to the tool events.

The model can request either of those calls all day. Neither executes.

What hooks are not

They're not a replacement for the other layers. A hook blocks a defined action category — it can't judge whether a summary leaked something it shouldn't have, which is output filtering's job, and it can't stop a model from being persuaded, which is what isolation and hardening reduce.

Hooks are the floor under the system: whatever else fails, these specific things cannot happen.

Choosing what to hook

Hook the actions where a single failure is unrecoverable or unbounded: external data transmission, destructive operations, writes to systems of record, anything with financial or clinical consequence. Reserve them for that class — hooking every call turns the agent into a workflow with extra steps and no benefit.

Key concept

Hooks are the only deterministic guardrail — code at a fixed point in the loop whose decision does not depend on the model. Any requirement phrased as “regardless of what the model decides” is asking for one.

Practice scenario

ScenarioA requirement reads: "under no circumstances, regardless of conversation state, may this agent transmit records off-network."
Work it through, then open this

“Regardless of” is the exam signalling a hook. Every prompt-wording option in that question is a distractor. Implement deterministic code at the pre-execution point that denies the call by name and arguments — and keep the other layers too, since a hook blocks defined actions but can’t evaluate output content.

Build exercise — Prove a hook holds where a prompt doesn’t

Intermediate · 20 min

What you’ll learn

  • Recognising requirements that demand determinism
  • Placing a hook at the request-execute gap
  1. Write a rule that must hold no matter what — something like ‘this agent never sends data to an external address without approval.’

    • Why: That phrasing is the exam’s own signal for a hook. Recognising it is half the skill.
    • You should see: A rule with no room for judgment in it.
  2. Implement it twice: once as a system prompt instruction, once as a hook that denies the tool call by name and arguments.

    • Why: Seeing both implementations side by side makes the difference concrete rather than theoretical.
    • You should see: One is a sentence; the other is a function that returns a denial.
  3. Attack both with the same injected instruction inside a document the agent processes.

    • Why: This is the realistic version of the threat, not a user typing something obvious.
    • You should see: The prompt version can be talked around under enough pressure. The hook returns the same denial regardless.

Exam traps

Answering a “regardless of what the model decides” question with prompt wording

That phrasing is the exam signalling a hook. Prompts shape probability; only code enforces.

Using hooks as a substitute for the other layers

They block defined action categories. They cannot evaluate output content or prevent persuasion.

Hooking every tool call

It reduces the agent to a fixed pipeline with added latency. Reserve hooks for unrecoverable or unbounded actions.

Assuming a hook can judge intent

It evaluates the call and its arguments deterministically. Nuanced judgment belongs in output filtering or human approval.

Sources

Quick check

A team argues broad agent credentials are acceptable because the agent is internal-only. Is that reasoning sound?