1
0
Fork 0
claude-code-hooks/exercises/01-block-danger.md
2026-03-30 10:38:57 +02:00

158 lines
3.8 KiB
Markdown

# Exercise 01: Block Dangerous Commands
**Concept:** Hooks (CC-024)
**Level:** Basic
**Time:** ~10 minutes
---
## Objective
Set up a pre-tool-use hook that blocks dangerous shell commands before
Claude executes them. You will configure the hook, test it by asking
Claude to run a blocked command, and confirm the block worked.
---
## Before You Start
Confirm you have:
- [ ] Claude Code installed (`claude --version` prints a version)
- [ ] This repo cloned and open in Claude Code
- [ ] Python 3 available (`python3 --version`)
---
## Instructions
**Step 1:** Read the hook script.
Open `hooks/pre-tool-use.sh` and read through it. Pay attention to:
- How it reads input from stdin using `cat`
- How it extracts `tool_name` and `command` from the JSON
- What the `BLOCKED` array contains
- What exit code 2 means vs exit code 0
You do not need to modify the script. Understanding it is the goal of this step.
**Step 2:** Make the script executable.
```bash
chmod +x hooks/pre-tool-use.sh
```
This is required before Claude Code can run it as a hook.
**Step 3:** Configure the hook in `.claude/settings.json`.
Create the file `.claude/settings.json` in this repo with this content:
```json
{
"permissions": {
"allow": [
"Bash(ls:*)",
"Bash(cat:*)",
"Bash(echo:*)",
"Bash(pwd)",
"Bash(date)",
"Read",
"Write",
"Edit",
"Glob",
"Grep"
],
"deny": [
"Bash(rm -rf *)",
"Bash(sudo *)"
]
},
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash hooks/pre-tool-use.sh"
}
]
}
]
}
}
```
The `matcher` field is a regex pattern matched against the tool name.
`"Bash"` matches the Bash tool. `".*"` would match all tools.
The `command` field is the shell command Claude Code runs before
executing the tool. It must exit 0 to allow, 2 to block.
**Step 4:** Start Claude Code in this directory.
```bash
cd claude-code-hooks
claude
```
Claude Code reads `.claude/settings.json` on startup and registers the hooks.
**Step 5:** Test the block by asking Claude to run a dangerous command.
Paste this prompt into Claude Code:
```
Run this shell command: sudo ls /root
```
Watch what happens. Claude will attempt the Bash tool call. The
pre-tool-use hook will run first, match the `sudo` pattern, and exit 2.
**Step 6:** Verify the result.
After the hook blocks the command, paste this prompt:
```
Did the command succeed? Why or why not?
```
Claude should explain that the command was blocked by a hook and show
the reason returned by the script.
---
## Expected Output
After completing the steps, you should see:
- Claude attempting the `sudo ls /root` command
- An error message from the hook: `Commands with sudo are not allowed`
- Claude reporting that the command was blocked and could not proceed
- No output from `sudo ls /root` because it never ran
If Claude executes the command anyway, check that `pre-tool-use.sh` is
executable and that `.claude/settings.json` has the correct hook config.
---
## What You Learned
- **PreToolUse hooks intercept before execution.** The command never runs
if the hook exits 2. This is different from permission deny lists, which
are pattern-matched by Claude Code before the tool call. Hooks let you
run arbitrary logic.
- **Exit code 2 means block.** Exit 0 means allow. Any other exit code is
treated as an error and may allow or block depending on Claude Code's
configuration.
- **The reason field reaches Claude.** Whatever you put in the `reason`
field of the JSON output, Claude receives it as the error message. This
lets you explain why a command was blocked in human-readable terms.
---
## Next
Move to [Exercise 02: Build an Audit Trail](./02-audit-trail.md).