# Security Patterns for Autonomous Agents Reference for the agent-system-design skill. Covers permission modes, hook-based guardrails, settings.json configuration, and a checklist for hardening autonomous agents. --- ## 1. Permission Modes Four levels of approval, from most restrictive to least: ### Default (interactive approval) Claude asks the user before every tool call. Suitable for exploratory sessions. No configuration required. ### Auto-edit (AcceptEdits mode) File edits are auto-approved. Bash commands still require approval. Enables faster iteration on code without allowing arbitrary shell execution. ```json { "autoApprove": ["Edit", "Write"] } ``` ### Auto Mode (AI classifier) An AI classifier evaluates each tool call and approves or blocks it based on predicted risk. Reported metrics: 0.4% false positive rate (safe calls blocked), 5.7% false negative rate (unsafe calls approved). Suitable for: attended automation where the developer is nearby and can intervene. Not suitable for: fully unattended production agents. ```json { "autoApprove": ["auto"] } ``` ### Bypass (--dangerously-skip-permissions) All tool calls are auto-approved without review. Must only be used inside a sandbox (Docker, VM, or a throwaway environment with no access to production systems). Never use on the host machine with access to real credentials or filesystems. ```bash claude --dangerously-skip-permissions -p "run the pipeline" ``` **Decision rule:** Match the permission mode to the blast radius. The more access the agent has, the more restrictive the approval mode must be. --- ## 2. Hook-Based Guardrails Hooks run synchronously before and after tool calls. A non-zero exit from `PreToolUse` blocks the tool call entirely. Five standard patterns: ### Pattern 1: Destructive Command Blocking Block commands that cannot be undone. ```bash # hooks/pre-tool-use.sh BLOCKED_PATTERNS=( "rm -rf" "mkfs" "dd if=" ":(){ :|:& };:" # fork bomb "> /dev/" ) COMMAND="$CLAUDE_TOOL_INPUT_COMMAND" for pattern in "${BLOCKED_PATTERNS[@]}"; do if echo "$COMMAND" | grep -qF "$pattern"; then echo "BLOCKED: destructive pattern detected: $pattern" >&2 exit 1 fi done ``` ### Pattern 2: Piped Script Execution Blocking Block patterns that download and execute code in a single pipeline. ```bash PIPED_EXEC_PATTERNS=( "curl.*|.*bash" "curl.*|.*sh" "wget.*|.*bash" "wget.*|.*sh" ) for pattern in "${PIPED_EXEC_PATTERNS[@]}"; do if echo "$COMMAND" | grep -qE "$pattern"; then echo "BLOCKED: piped script execution detected" >&2 exit 1 fi done ``` ### Pattern 3: Privilege Escalation Blocking Block commands that elevate privileges or weaken file permissions. ```bash PRIV_PATTERNS=( "sudo" "chmod 777" "chmod a+x /etc" "shutdown" "reboot" "init 0" ) for pattern in "${PRIV_PATTERNS[@]}"; do if echo "$COMMAND" | grep -qF "$pattern"; then echo "BLOCKED: privilege escalation detected" >&2 exit 1 fi done ``` ### Pattern 4: Path Restriction Prevent writes outside the project directory. ```bash PROJECT_DIR="$(cd "$(dirname "$0")/.." && pwd)" TOOL_PATH="$CLAUDE_TOOL_INPUT_FILE_PATH" if [ -n "$TOOL_PATH" ]; then REAL_PATH="$(realpath "$TOOL_PATH" 2>/dev/null || echo "$TOOL_PATH")" if [[ "$REAL_PATH" != "$PROJECT_DIR"* ]]; then echo "BLOCKED: write outside project dir: $REAL_PATH" >&2 exit 1 fi fi ``` ### Pattern 5: Audit Logging Log every tool call with timestamp for post-hoc review. ```bash # hooks/post-tool-use.sh LOG_FILE="$PROJECT_DIR/logs/audit.log" mkdir -p "$(dirname "$LOG_FILE")" echo "$(date -u +"%Y-%m-%dT%H:%M:%SZ") TOOL=$CLAUDE_TOOL_NAME INPUT=$CLAUDE_TOOL_INPUT" >> "$LOG_FILE" ``` Combine all five patterns into a single `pre-tool-use.sh` and a separate `post-tool-use.sh`. Keep them under 80 lines each so they are auditable at a glance. --- ## 3. settings.json Security Configuration The full security configuration surface in `settings.json`: ```json { "permissions": { "allow": [ "Bash(git:*)", "Bash(npm run *)", "Read(**)", "Write(src/**)", "Edit(src/**)" ], "deny": [ "Bash(rm -rf *)", "Bash(sudo *)", "Bash(curl * | *)", "Bash(wget * | *)", "Write(/etc/*)", "Write(~/.ssh/*)", "Write(~/.zshenv)" ] }, "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": "./hooks/pre-tool-use.sh" } ] } ], "PostToolUse": [ { "matcher": "*", "hooks": [ { "type": "command", "command": "./hooks/post-tool-use.sh" } ] } ] } } ``` **Allow list principle:** Prefer an explicit allow list over a deny list alone. The deny list catches known-bad patterns; the allow list enforces least privilege. Both together provide defense in depth. **Glob patterns in deny list:** Use `*` conservatively. `Bash(sudo *)` blocks all sudo invocations. `Write(/etc/*)` blocks all writes to system config. --- ## 4. Security Checklist for Autonomous Agents Verify each item before declaring an agent production-ready: - [ ] `PreToolUse` hook is present and blocks destructive commands (Pattern 1-3 above) - [ ] `PostToolUse` audit log is enabled and written to a persistent location - [ ] `permissions.deny` list covers: `rm -rf *`, `sudo *`, `curl * | *`, `wget * | *` - [ ] `permissions.allow` list is as narrow as the agent's task requires - [ ] MEMORY.md does not contain API keys, tokens, or passwords - [ ] `.env` is in `.gitignore`; secrets are loaded from environment, not from files tracked by git - [ ] Deployment target matches the blast radius (see `deployment-targets.md`) - [ ] If always-on: phone approval via permission relay is configured (v2.1.81+) - [ ] Audit log is rotated (logrotate or launchd-managed) - [ ] Hook scripts are executable (`chmod +x hooks/*.sh`) and checked into version control ### Phone Approval (v2.1.81+) For unattended agents that must occasionally escalate to a human: ```json { "hooks": { "PreToolUse": [ { "matcher": "Bash(sudo *)", "hooks": [ { "type": "prompt", "prompt": "This command requires elevated privileges. Approve? (yes/no)", "channel": "telegram" } ] } ] } } ``` The agent pauses and sends the approval request to the configured channel. The operator approves or rejects from their phone. If no response within the timeout, the hook exits non-zero and the command is blocked. --- ## 5. OpenClaw vs Claude Code: Security Philosophy | Dimension | OpenClaw | Claude Code | |-----------|---------|-------------| | Primary mechanism | Docker sandbox (containment) | Hooks + deny list (prevention) | | Approach | Contain the damage after the fact | Prevent the action before it runs | | Escape risk | Container escape (low, not zero) | Hook bypass if hook is misconfigured | | Auditability | Container logs | Audit log hook (Pattern 5) | | Operator control | Docker network/volume flags | settings.json permissions | | Mobile escalation | Native | Permission relay via channel MCP (v2.1.81+) | **Containment vs Prevention:** OpenClaw runs the agent inside a Docker container with limited network and volume mounts. If the agent does something harmful, the damage is contained to the container. Claude Code instead prevents harmful actions from running at all, via hooks and the permissions deny list. **Tradeoff:** Containment is harder to escape but allows the harmful action to attempt execution. Prevention stops it earlier but requires the hook to be correctly configured and maintained. For production agents, combine both: run Claude Code inside Docker AND configure hooks and deny lists. **Combined approach (recommended for production):** 1. Run `claude` inside a Docker container with minimal volume mounts 2. Configure `PreToolUse` hooks with Patterns 1-4 3. Enable `PostToolUse` audit logging (Pattern 5) 4. Use an explicit `permissions.deny` list 5. Do not use `--dangerously-skip-permissions` outside the container This gives containment as a last resort and prevention as the primary defense.