# Using Subagent Hooks for Validation in Claude Code: A Complete Security Guide

> Secure your Claude Code with subagent hooks. This guide explains how these scripts intercept prompts and tool use, blocking dangerous operations for enhanced security.

- Repository: [Luong NGUYEN/claude-howto](https://github.com/luongnv89/claude-howto)
- Tags: how-to-guide
- Published: 2026-03-30

---

**Subagent hooks in Claude Code are shell scripts that automatically intercept user prompts and tool executions, blocking dangerous operations by exiting with non-zero status while permitting safe workflows to proceed.**

Claude Code extends the standard Claude AI model with **subagents**—lightweight, isolated assistants that operate in dedicated context windows. A critical component of this ecosystem is the hook system located in the `06-hooks/` directory of the `luongnv89/claude-howto` repository. These hooks enable automated validation, security scanning, and policy enforcement before any subagent action is finalized.

## What Are Subagent Hooks?

Subagent hooks are **shell scripts** that Claude Code executes automatically when specific events occur during a subagent session. The system parses hook declarations from a subagent's YAML front-matter and registers them to run at precise lifecycle moments. If a hook exits with a non-zero status code, Claude Code blocks the associated operation entirely; if it exits with status 0, the workflow continues (potentially with logged warnings).

Hooks run in a **sandboxed Bash environment** and can read from stdin or process command-line arguments depending on the event type. This architecture allows you to implement validation logic without modifying the core Claude Code runtime.

## The Hook-Driven Validation Workflow

Claude Code supports several event types for hook registration, each targeting different stages of subagent execution:

### UserPromptSubmit Hooks

The `UserPromptSubmit` event fires immediately after a user submits a prompt to Claude Code. This hook receives the entire prompt via stdin, making it ideal for **dangerous command detection** and **production deployment guards**.

According to the source code in [`06-hooks/validate-prompt.sh`](https://github.com/luongnv89/claude-howto/blob/main/06-hooks/validate-prompt.sh), this hook implements three critical checks:
- **Dangerous pattern matching**—blocks strings like `rm -rf /`, `drop database`, or `dd if=` (case-insensitive)
- **Production deployment guard**—requires a `.deployment-approved` flag file when prompts mention both `deploy`/`push` and `production`
- **Refactor sanity check**—warns when refactoring is requested without an accompanying `tests/` directory

### PostToolUse:Write Hooks

The `PostToolUse:Write` event triggers after a subagent uses the `Write` tool to create or modify a file. The hook receives the file path as its first command-line argument (`$1`), enabling **post-write security scanning**.

As implemented in [`06-hooks/security-scan.sh`](https://github.com/luongnv89/claude-howto/blob/main/06-hooks/security-scan.sh), this hook:
- Greps for common secret patterns (passwords, API keys, AWS keys, private key delimiters)
- Optionally runs **semgrep** or **trufflehog** if installed for deeper static analysis
- Emits warnings for findings but exits with status 0 to avoid blocking legitimate file operations

### PreToolUse and PostToolUse Hooks

The `PreToolUse` event fires before any tool invocation (such as `Bash`), while `PostToolUse` runs after tool completion. These hooks support **matcher patterns** to target specific tools, enabling fine-grained policy enforcement like pre-flight Bash command validation or post-execution cleanup.

## How Validation Hooks Work Under the Hood

The [`validate-prompt.sh`](https://github.com/luongnv89/claude-howto/blob/main/validate-prompt.sh) script in the `06-hooks/` directory demonstrates the validation pattern. When Claude Code invokes this hook, it streams the user prompt to the script's stdin. The script then performs regex-based pattern matching against a blacklist of dangerous operations.

If any check fails, the script prints a descriptive error message to stderr and exits with status 1. Claude Code detects this non-zero exit code and aborts the operation, presenting the error to the user. If all checks pass, the script exits 0 and the subagent proceeds with the request.

This mechanism provides a **fail-closed security model**—any hook crash or explicit rejection prevents potentially harmful actions from executing.

## How Security Scan Hooks Work Under the Hood

The [`security-scan.sh`](https://github.com/luongnv89/claude-howto/blob/main/security-scan.sh) script operates differently because it runs after the operation completes. When a subagent writes a file, Claude Code executes this hook with the absolute path as argument `$1`.

The script first performs lightweight grepping for high-entropy strings and known secret patterns. If **semgrep** or **trufflehog** binaries are detected in the PATH, it launches these tools for comprehensive secrets detection. Rather than blocking writes (which would disrupt legitimate development), this hook logs warnings to stdout and always exits 0, providing a safety net without interrupting the development flow.

## Configuring Hooks in Subagent Front-Matter

Subagents declare hooks within their YAML front-matter under the `hooks` key. The configuration maps event names to command specifications using the `type: command` structure.

### Basic Prompt Validation Subagent

This minimal configuration blocks dangerous commands before any action occurs:

```yaml
---
name: prompt-validator
description: Blocks dangerous commands before any action.
tools: Read, Grep
hooks:
  UserPromptSubmit:
    - type: command
      command: "./06-hooks/validate-prompt.sh"
---
You are an assistant that only checks user prompts for safety. If the prompt passes,
proceed with the requested analysis; otherwise, refuse the request.

```

### Pre-Tool Execution Guard

To validate Bash commands before execution, use the `PreToolUse` event with a matcher:

```yaml
---
name: safe-bash-runner
description: Executes Bash scripts only after a custom safety scan.
tools: Bash
hooks:
  PreToolUse:
    - matcher: "Bash"
      hooks:
        - type: command
          command: "./scripts/my-bash-guard.sh"
---
You are a Bash executor. Before running any command, the guard script will verify
that the command does not contain forbidden patterns.

```

### Comprehensive Security Subagent

For production environments, combine multiple hooks to create a defense-in-depth strategy:

```yaml
---
name: security-assistant
description: Reviews changes, validates prompts, and scans written files.
tools: Read, Write, Grep, Bash
hooks:
  UserPromptSubmit:
    - type: command
      command: "./06-hooks/validate-prompt.sh"
  PostToolUse:Write:
    - type: command
      command: "./06-hooks/security-scan.sh"
---
You are a security specialist. Perform code reviews, ensure no secrets are leaked,
and only accept safe prompts. Use the provided tools to locate and fix issues.

```

## Summary

- **Subagent hooks** are shell scripts in `06-hooks/` that Claude Code executes at specific lifecycle events to enforce validation and security policies.
- **UserPromptSubmit hooks** like [`validate-prompt.sh`](https://github.com/luongnv89/claude-howto/blob/main/validate-prompt.sh) read from stdin and block dangerous operations by exiting with status 1.
- **PostToolUse:Write hooks** like [`security-scan.sh`](https://github.com/luongnv89/claude-howto/blob/main/security-scan.sh) receive file paths as arguments and scan for secrets while allowing operations to complete.
- **Hook declarations** reside in subagent YAML front-matter under the `hooks` key, supporting event-specific matchers for granular control.
- The system implements a **fail-closed model** where non-zero exit codes abort operations, ensuring unsafe prompts and unapproved deployments cannot proceed.

## Frequently Asked Questions

### How do I block specific dangerous commands in Claude Code subagents?

Create a `UserPromptSubmit` hook that reads from stdin and pattern-matches against forbidden strings. In [`06-hooks/validate-prompt.sh`](https://github.com/luongnv89/claude-howto/blob/main/06-hooks/validate-prompt.sh), the script checks for patterns like `rm -rf /` and `drop database`, then exits with status 1 if detected. Claude Code will abort the prompt execution and display your error message to the user.

### Can subagent hooks prevent accidental secret commits?

Yes. The [`security-scan.sh`](https://github.com/luongnv89/claude-howto/blob/main/security-scan.sh) hook in [`06-hooks/security-scan.sh`](https://github.com/luongnv89/claude-howto/blob/main/06-hooks/security-scan.sh) demonstrates this by scanning written files for API keys, passwords, and private keys. While it emits warnings for findings, you can modify the script to exit with status 1 when secrets are detected, causing Claude Code to reject the file write operation entirely.

### What happens if a hook script crashes or returns an error?

Claude Code treats any non-zero exit status as a blocking failure. If [`validate-prompt.sh`](https://github.com/luongnv89/claude-howto/blob/main/validate-prompt.sh) crashes or explicitly exits with status 1, the associated operation (prompt submission or tool use) is immediately aborted. This fail-closed behavior ensures that validation failures or script errors cannot inadvertently permit dangerous actions.

### Where should I store custom hook scripts for my subagents?

Store reusable hooks in the `06-hooks/` directory of your repository, as shown in the `luongnv89/claude-howto` project. Reference them from subagent front-matter using relative paths like [`./06-hooks/validate-prompt.sh`](https://github.com/luongnv89/claude-howto/blob/main/./06-hooks/validate-prompt.sh). For project-specific validation logic, create additional directories (e.g., `./scripts/`) and reference those paths in your hook configurations.