最新资讯
stackoverflow.blog 欢迎来到芯片时代:半导体制造、AI 数据中心与芯片设计的未来 www.youtube.com AI Agent 总犯同样的错:CentralAgent AI 将记忆重构为经验系统 pub.towardsai.net Netflix 科学家建议重采样 LLM 评判:5 次仅将误差棒缩小 13% pub.towardsai.net Jev 作为 LLM 前的静默层:构建提示感知的模型路由器 reddit.com OpenAI 称 Astra 仅满足 10% 安全任务标准 dev.to FreeToken:在自有 GPU 上运行前沿 MoE 模型及其内存需求 dev.to 当 AI Agent 说“完成”时,究竟完成了什么? dev.to 部署 Underdog Bark 3.0 Pro:逐字转写、自定义后端与分块设置 dev.to 为艰难对话构建 AI:五个 Agent、一条安全正则与一位盲评监督者 techcrunch.com Palo Alto Networks 洽谈以 5 亿美元收购 AI Agent 安全初创公司 Clutch theinformation.com 黄仁勋被要求就涉华间谍指控向国会作简报 www.infoq.com Edera 推出 Object Capability Bounded Authority,试图将凭证与 AI Agent 工作负载隔离
多元动态
登录 注册
AI编码 dev.to · @aicoding-guide

用 Claude Code PreToolUse hook 阻止 rm -rf,并了解其局限

原文 我的收藏

文章介绍如何用 PreToolUse hook 在工具执行前检查并拒绝危险命令,如 rm -rf。其价值在于触发位置:文档称 PreToolUse hook 在任何权限模式(包括 dontAsk)下先于权限模式检查触发,返回 permissionDecision: "deny" 即使在 bypassPermissions 模式或使用 --dangerously-skip-permissions 时也能阻止工具。文章涵盖两种阻止方式(退出码 2 和 permissionDecision)、为何切换权限模式无法绕过,以及 hook 仍无法做到的事和字符串匹配失效的场景。

AI推荐理由 文章介绍用 Claude Code 的 PreToolUse hook 阻止 rm -rf,说明该 hook 在任何权限模式(包括 dontAsk 和 bypassPermissions)下先于权限检查触发,返回 permissionDecision: deny 可阻止工具执行,并讨论字符串匹配的局限。对使用 Claude Code 的开发者有直接操作价值,但来源为个人博客转载,需自行验证文档细节。

68

正文

Originally published at https://aicoding-guide.com. Some commands you never want running unreviewed, and rm -rf is the classic one. A PreToolUse hook lets a script inspect the call and refuse it before the tool runs. What makes this hook worth using is its position in the chain: the documentation states that PreToolUse hooks fire before any permission-mode check, in every permission mode including dontAsk, and that a hook returning permissionDecision: "deny" blocks the tool even in bypassPermissions mode or with --dangerously-skip-permissions. Key point What you will learn Two ways to block: exit code 2 and permissionDecision Why changing permission mode doesn't get around it, and what hooks still can't do Where string matching breaks down, and what to pair it with Blocking with exit code 2 The shortest form. Write a message to stderr and exit 2: the tool call is denied and that stderr is fed back to Claude. #!/usr/bin/env bash # .claude/hooks/block-rm-rf.sh INPUT=$(cat) CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty') if echo "$CMD" | grep -Eq '(^|[;&|[:space:]])rm[[:space:]]+(-[a-zA-Z]*r[a-zA-Z]*f|-[a-zA-Z]*f[a-zA-Z]*r)'; then echo "Blocked: recursive force-remove is not allowed here. Name the paths individually." >&2 exit 2 fi exit 0 Enter fullscreen mode Exit fullscreen mode On macOS and Linux the script has to be executable: chmod +x .claude/hooks/block-rm-rf.sh Enter fullscreen mode Exit fullscreen mode Register it in .claude/settings.json. As in the documentation's example, you can run a logging hook alongside the guardrail: { "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": "jq -r .tool_input.command >> ~/.claude/bash.log" }, { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-rm-rf.sh" } ] } ] } } Enter fullscreen mode Exit fullscreen mode The documentation walks through what happens: both hooks execute in parallel, the logging hook exits 0 and reports no decision, and the guardrail exits 2 to deny the call. The deny takes precedence, so the command is blocked — and the log entry is still written, because the logging hook already ran. Blocking with permissionDecision Instead of an exit code, print JSON to stdout. This lets you pass a structured reason. { "hookSpecificOutput": { "hookEventName": "PreToolUse", "permissionDecision": "deny", "permissionDecisionReason": "Recursive deletes are blocked here. Run git clean -n first, then remove paths individually." } } Enter fullscreen mode Exit fullscreen mode PreToolUse accepts three values: Value Effect allow Skip the interactive permission prompt. Deny and ask rules still apply deny Cancel the tool call and send the reason to Claude ask Show the permission prompt as normal With deny, permissionDecisionReason is fed back to Claude, so writing why and what to do instead keeps the work moving rather than just stopping it. allow cannot override a deny rule The reverse does not hold. A hook returning allow does not bypass deny rules from settings, and it cannot suppress prompts for MCP tools marked requiresUserInteraction or for connector tools your organization set to ask. As the documentation puts it: hooks can tighten restrictions but not loosen them past what permission rules allow. Cover file edits too rm arrives through the Bash tool, but an Edit or Write that clobbers an important file is the same class of accident. The documentation shows a path-based guard: #!/bin/bash # protect-files.sh INPUT=$(cat) FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty') # Normalize Windows backslash separators so the patterns below match FILE_PATH="${FILE_PATH//\\//}" PROTECTED_PATTERNS=(".env" "package-lock.json" ".git/") for pattern in "${PROTECTED_PATTERNS[@]}"; do if [[ "$FILE_PATH" == *"$pattern"* ]]; then echo "Blocked: $FILE_PATH matches protected pattern '$pattern'" >&2 exit 2 fi done exit 0 Enter fullscreen mode Exit fullscreen mode Register that one with a matcher of Edit|Write. For the shape of the JSON these scripts read, see The JSON your Claude Code hooks receive on stdin; for matcher values, see Every value you can put in a Claude Code hook matcher. Where string matching breaks This is the part to be honest about. Matching the command text is easy to slip past. Written as Matched by a naive rm -rf check rm -rf build Yes rm -r -f build No rm --recursive --force build No FLAGS="-rf"; rm $FLAGS build No find . -delete Not an rm at all The script above handles -rf and -fr orderings, and that is still not exhaustive. A hook is an effective guardrail against mistakes; it is not a mechanism for stopping deliberate evasion. For anything you need genuinely enforced, layer these underneath: Permission deny rules: the documentation states that hook decisions don't bypass permission rules — Claude Code evaluates deny and ask rules regardless of what a PreToolUse hook returns. See Claude Code permissions in settings.json The sandbox: OS-level restriction that also covers files a script opens indirectly Worth knowing too: rm and rmdir targeting a critical path are never auto-approved in any mode, and no allow rule or PreToolUse hook allow approves them. Glossary PreToolUse: the event that fires immediately before a tool runs. Denying here means the tool call never happens. Checking it works After registering, run /hooks and confirm the hook appears under the right event. To test the script by itself, pipe sample JSON into it: echo '{"tool_name":"Bash","tool_input":{"command":"rm -rf /tmp/x"}}' | ./.claude/hooks/block-rm-rf.sh; echo "exit=$?" Enter fullscreen mode Exit fullscreen mode exit=2 is what you want to see. Summary PreToolUse hooks fire before permission-mode checks, so a deny holds even in bypassPermissions Two ways to block: exit 2 for brevity, permissionDecision JSON to pass a reason Multiple hooks on one event run in parallel, and a deny wins A hook's allow cannot override a deny rule — hooks tighten, they don't loosen String matching misses rm -r -f and variable expansion; use deny rules and the sandbox for real enforcement

来源报道 · 1 篇 · 以下链接均前往原始媒体

← 返回精选动态 原文

更多内容