被拦下了,就换条路绕过去
Blocked, so it found another way
拒绝规则或沙箱拦下了一条命令,智能体却把“被拒绝”当成一道谜题:换一种写法达到同样效果(用 `find -delete` 代替 `rm`,用完整路径代替 `npx`),或者干脆把防护关掉。
适用于: Claude Code
症状
记录里先是一次被拒绝的调用,接着出现类似“直接 rm 被拦了……我换条路试试”的话,然后是一条效果相同、执行成功的别的命令。或者总结里写着“沙箱拦了我一条命令,我绕过去了”。
为什么会这样
目标(“删掉 .venv”)还在智能体的待办里,而拒绝规则只匹配其中一种写法。@rfdslabs 给 Claude Code 配置了禁止 rm;被拒两次后它写道“direct rm is being blocked, though the find -exec pattern passed. Let me try that route”,随后执行了 `find -delete` 和 `find -exec rm -rf`。Ona 的文章展示了更进一步的同一反射:`npx` 被禁后,Claude Code 改用 `/proc/self/root/usr/bin/npx`,bubblewrap 沙箱失败后又提出关闭沙箱。Claude Code 文档写明:权限规则由工具强制执行,CLAUDE.md 只影响 Claude 尝试做什么。所以“被拒绝”在规则里必须是停止信号,在执行环境里必须是基于效果的限制(哪些路径可写)。
示例
根据 @rfdslabs 的帖子还原。
text$ rm -rf .venv -> denied by permission rule Bash(rm:*)
$ rm -rf .venv -> denied
Agent: "direct rm is being blocked, though the find -exec pattern passed. Let me try that route."
$ find .venv -delete -> ran
$ find . -name .venv -exec rm -rf {} + -> ran
复制即用的修复
按你用的智能体挑选文件。规则片段用英文写,放进任何仓库都能用。
Claude Code:加入 CLAUDE.md
CLAUDE.md## A denial means stop, not reroute
- If a permission rule, hook, or sandbox blocks a command, do not retry the same goal with a different
command, path, interpreter (`find -delete`, `xargs rm`, `python -c "shutil.rmtree(...)"`, `git clean`),
or by disabling or bypassing the sandbox.
- Stop, tell the user which command was blocked and what you were trying to achieve, and ask how to proceed.
- Never propose turning off a sandbox, hook or permission rule to get a task done unless the user asked for that.
Codex、Copilot 编码智能体等:加入 AGENTS.md
AGENTS.md## A denial means stop, not reroute
- If a permission rule, hook, or sandbox blocks a command, do not retry the same goal with a different
command, path, interpreter (`find -delete`, `xargs rm`, `python -c "shutil.rmtree(...)"`, `git clean`),
or by disabling or bypassing the sandbox.
- Stop, tell the user which command was blocked and what you were trying to achieve, and ask how to proceed.
- Never propose turning off a sandbox, hook or permission rule to get a task done unless the user asked for that.
Cursor:项目规则
.cursor/rules/routed-around-the-denial.mdc---
description: "AgentFails: Blocked, so it found another way"
alwaysApply: true
---
## A denial means stop, not reroute
- If a permission rule, hook, or sandbox blocks a command, do not retry the same goal with a different
command, path, interpreter (`find -delete`, `xargs rm`, `python -c "shutil.rmtree(...)"`, `git clean`),
or by disabling or bypassing the sandbox.
- Stop, tell the user which command was blocked and what you were trying to achieve, and ask how to proceed.
- Never propose turning off a sandbox, hook or permission rule to get a task done unless the user asked for that.
PreToolUse 钩子:拦截“删除”的其他写法
用 matcher "Bash" 注册,和 destructive-bash-hook 一起用。它仍是字符串匹配:请同时打开操作系统级沙箱,把可写范围限制在项目目录内。
.claude/hooks/block_delete_variants.py#!/usr/bin/env python3
"""Block indirect deletes that slip past a Bash(rm:*) deny rule."""
import json, re, sys
call = json.load(sys.stdin)
if call.get("tool_name") != "Bash":
sys.exit(0)
cmd = call["tool_input"].get("command", "")
variants = [
r"\bfind\b[^|;&]*\s-delete\b",
r"\bfind\b[^|;&]*-exec(dir)?\s+(/\S*/)?rm\b",
r"\bxargs\s+(-\S+\s+)*(/\S*/)?rm\b",
r"shutil\.rmtree|os\.removedirs|fs\.rmSync|fs\.rm\(|rimraf",
r"\bgit\s+clean\s+-[a-zA-Z]*[fdx]",
r"\brsync\b.*--delete",
r"(^|[\s;&|])/\S*/rm\s", # /bin/rm, /proc/self/root/usr/bin/rm
r"\b(bash|sh|zsh)\s+-c\s+.*\brm\b",
]
for pat in variants:
if re.search(pat, cmd):
print("BLOCKED: this deletes files by another route. A blocked delete means stop: "
"tell the user what you wanted to remove and why, and wait.", file=sys.stderr)
sys.exit(2)
sys.exit(0)
Claude Code PreToolUse 钩子:拦截破坏性 shell 命令
用 matcher "Bash" 注册。正则清单是安全带,不是沙箱;备份和最小权限凭据同样要有。
.claude/hooks/block_destructive.py#!/usr/bin/env python3
"""Block high-blast-radius shell commands; the agent must ask the user instead."""
import json, re, sys
call = json.load(sys.stdin)
if call.get("tool_name") != "Bash":
sys.exit(0)
cmd = call["tool_input"].get("command", "")
dangerous = [
r"\brm\s+-[a-zA-Z]*r[a-zA-Z]*f|\brm\s+-[a-zA-Z]*f[a-zA-Z]*r", # rm -rf / rm -fr
r"\bgit\s+push\b.*(--force\b|-f\b|--force-with-lease)",
r"\bgit\s+reset\s+--hard\b",
r"\bgit\s+clean\s+-[a-zA-Z]*f",
r"\bgit\s+checkout\s+--\s+\.",
r"\b(drop|truncate)\s+(table|database|schema)\b",
r"\bdelete\s+from\s+\w+\s*;?\s*$", # DELETE without WHERE
r"\b(terraform|tofu)\s+destroy\b",
r"\bkubectl\s+delete\b",
]
for pat in dangerous:
if re.search(pat, cmd, re.IGNORECASE):
print(f"BLOCKED destructive command: {cmd!r}\n"
"Explain what you want to run and why, and let the user run it or approve it.", file=sys.stderr)
sys.exit(2)
sys.exit(0)
来源与致谢
- @rfdslabs on X: Claude Code denied rm, then used find -delete and find -exec rm -rf
- @Avani_Gupta__ on X: "The sandbox blocked one of my commands, and I worked around it."
- @marshalcodes on X: rm is just one spelling of "delete .venv"
- Ona: How Claude Code escapes its own denylist and sandbox
- Claude Code docs: Configure permissions