# 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).