learn-claude-code笔记3: Permission — 执行前做权限判断

s03: Permission --- 执行前做权限判断

问题

s02 的 Agent 有 5 个工具。file tools 受 safe_path 保护,但 bash 不受限制。让它"清理一下项目",可能执行 rm -rf /。

安全边界由代码负责,判断发生在工具执行之前。

解决方案:工具执行前加一道门

工具执行前插入 check_permission()。每个工具调用依次经过三道闸门:硬拒绝优先,软询问次之,都没命中就放行。

三道闸门对应三种决策:

闸门 作用 命中后
1. 拒绝列表 永远禁止的操作(rm -rf /、sudo) 直接拒绝,不执行
2. 规则匹配 取决于上下文的操作(读/写工作区外、rm 文件) 交给闸门 3
3. 用户审批 闸门 2 命中后,暂停等用户确认 用户决定允许或拒绝

三道都没命中 → 直接执行。大部分日常操作走这条路。


工作原理

工具执行前加三道闸门

第一道闸门:硬拒绝

python 复制代码
# 关卡 1:硬性拒绝列表 - 始终禁止
DENY_LIST = ["rm -rf /", "sudo", "shutdown", "reboot", "mkfs", "dd if=", "> /dev/sda"]

def check_deny_list(command: str) -> str | None:
    for pattern in DENY_LIST:
        if pattern in command:
            return f"已阻止:'{pattern}' 在拒绝列表中"
    return None

第二道闸门:规则匹配

python 复制代码
# 关卡 2:规则匹配 - 上下文相关检查
# 检测 shell 命令中是否包含 rm 或 del 这类破坏性删除命令
DESTRUCTIVE_COMMAND_WORD = re.compile(
    r"(?i)(?:^|[;&|()\n])\s*(?:rm|del)(?=\s|$|[;&|()])"
)


def contains_destructive_command(command: str) -> bool:
    return bool(DESTRUCTIVE_COMMAND_WORD.search(command))


PERMISSION_RULES = [
    {"tools": ["read_file", "write_file", "edit_file"],
     "check": lambda args: not (WORKDIR / args.get("path", "")).resolve().is_relative_to(WORKDIR),
     "message": "写入工作区外"},
    {"tools": ["bash"],
     "check": lambda args: contains_destructive_command(args.get("command", "")) or
     any(kw in args.get("command", "") for kw in ["rm ", "> /etc/", "chmod 777"]),
     "message": "潜在破坏性命令"},
]

def check_rules(tool_name: str, args: dict) -> str | None:
    for rule in PERMISSION_RULES:
        if tool_name in rule["tools"] and rule["check"](args):
            return rule["message"]
    return None

第三道闸门:用户审批

python 复制代码
# 关卡 3:用户审批 - 规则匹配后等待确认
def ask_user(tool_name: str, args: dict, reason: str) -> str:
    print(f"\n\033[33m[权限] {reason}\033[0m")
    print(f"   工具:{tool_name}({args})")
    choice = input("   允许?[y/N] ").strip().lower()
    return "允许" if choice in ("y", "yes") else "拒绝"

权限流水线

python 复制代码
# 流水线:三道关卡串联
def check_permission(block) -> bool:
    if block.name == "bash":
        reason = check_deny_list(block.input.get("command", ""))
        if reason:
            print(f"\n\033[31m[已阻止] {reason}\033[0m")
            return False
    reason = check_rules(block.name, block.input)
    if reason:
        decision = ask_user(block.name, block.input, reason)
        if decision == "拒绝":
            return False
    return True

Agent 循环新增权限校验层

在工具执行前,插入权限校验

python 复制代码
def agent_loop(messages: list):
    while True:
        response = client.messages.create(
            model=MODEL, system=SYSTEM, messages=messages,
            tools=TOOLS, max_tokens=8000,
        )
        messages.append({"role": "assistant", "content": response.content})

        tool_calls = [
            block for block in response.content if block.type == "tool_use"
        ]
        if not tool_calls:
            return

        results = []
        for block in tool_calls:
            print(f"\033[36m> {block.name}\033[0m")

            # s03 变更:执行前先经过权限流水线
            if not check_permission(block):
                results.append({"type": "tool_result", "tool_use_id": block.id,
                                "content": "权限被拒绝。"})
                continue

            handler = TOOL_HANDLERS.get(block.name)
            output = handler(**block.input) if handler else f"未知:{block.name}"
            print(str(output)[:200])
            results.append({"type": "tool_result", "tool_use_id": block.id, "content": output})

        messages.append({"role": "user", "content": results})

每步输出打印

用户输入内容:删除hello.py

text 复制代码
s03:权限系统
输入问题,按回车发送。输入 q 退出。

s03 >> 删除hello.py

第一轮工具调用

text 复制代码
工具执行block:
ToolUseBlock(
    id='call_fa61b3a4b8234aa780241380',
    caller=None,
    input={
        'command': 'del "H:\\AI\\Agent\\Coding Agent\\learn-claude-code\\s03_permission\\hello.py"'
    },
    name='bash',
    type='tool_use',
    toolset_name=None
)

执行命令

text 复制代码
> bash

执行校验,命中第二道闸门,匹配到规则,执行第三道闸门,用户审批。用户审批通过。

text 复制代码
[权限] 潜在破坏性命令
   工具:
   bash(
    command='del "H:\\AI\\Agent\\Coding Agent\\learn-claude-code\\s03_permission\\hello.py"'
)
   允许?[y/N] y

第一轮工具调用结果

text 复制代码
工具执行输出 output:  找不到 H:\AI\Agent\Coding Agent\learn-claude-code\s03_permission\hello.py

第二轮工具调用

text 复制代码
工具执行block:  
ToolUseBlock(
    id='call_64ac39a992174147ae148405',
    caller=None,
    input={
        'command': 'dir "H:\\AI\\Agent\\Coding Agent\\learn-claude-code\\s03_permission"'
    },
    name='bash',
    type='tool_use',
    toolset_name=None
)

执行shell命令

text 复制代码
> bash

第二轮工具调用结果

text 复制代码
工具执行输出 output:  驱动器 H 中的卷是 新加卷
 卷的序列号是 4EB5-DF96

 H:\AI\Agent\Coding Agent\learn-claude-code\s03_permission 的目录

2026/10/17  21:26    <DIR>          .
2026/10/17  21:26    <DIR>          ..
2026/10/17  21:26        

最终输出

text 复制代码
当前目录下没有 `hello.py` 文件。目录中存在的 Python 文件有:

- code-zh.py
- code.py
- test.py

请问您是要删除其中一个文件吗?如果是,请告诉我具体是哪一个。
相关推荐
墨心@1 小时前
AgentSession 详细分析
agent·pi·coding agent
for_ever_love__2 小时前
检索工具化封装:把 Elasticsearch、SQL、Confluence 包装成 Agent 的统一工具
agent·工具·封装·调用·rag·检索·做成
guslegend2 小时前
注意力稀释:当 113 个工具让模型降智时,工程师该怎么做?
agent
梦醒江南2 小时前
Agent 卡在网页上时,为什么不能换个真实入口继续?
人工智能·agent
用户460381291942 小时前
MCP 给了 Agent 一堆“手”,Skill 才教它怎么干活:两者到底什么关系?
agent·mcp
for_ever_love__2 小时前
Agentic RAG 实战:让 Agent 自己决定怎么检索、检索几次
人工智能·后端·agent·控制·自适应·rag·检索策略
大模型真好玩2 小时前
用TRAE、DeepSeek-Harness、DeepSeek-V4.1-Flash-1017再现特斯拉Roadster汽车飞行名场面
人工智能·agent·deepseek
遇码3 小时前
不用转场引擎做幻灯片转场:相机滑动、黑场淡入与真全屏的完整做法
人工智能·数码相机·ai·rust·大模型·agent
薛定谔的猫-菜鸟程序员4 小时前
22 分 10 秒 vs 2 小时 44 分:AtomCode 与 opencode 项目开发对比实测,国产agent Atomcode 这么猛吗?
agent·opencode·atomcode