158 lines
3.8 KiB
Markdown
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).
|