基于 Cedar 策略的 Claude Code 工具调用治理:protect-mcp policy-enforcer 实战指南
【免费下载链接】agentsMulti-harness agentic plugin marketplace for Claude Code, Codex, Cursor, OpenCode, GitHub Copilot, and Google Antigravity项目地址: https://gitcode.com/GitHub_Trending/agents24/agents
本文以 plugins/protect-mcp/agents/policy-enforcer.md 为绝对主体,深入讲解如何在 Claude Code 项目中以 AWS 开源授权引擎 Cedar 编写、审计并验证 AI Agent 工具调用的访问控制策略。你将掌握 permit/forbid 的语义差异、context 属性的实战用法、三档风险画像的策略模板(只读研究项目 / 常规开发项目 / 生产部署项目),以及结合 protect-mcp 插件(PreToolUse 钩子 + Ed25519 签名回执)形成"策略执行 + 密码学审计"完整闭环的可落地方案。
一、Policy Enforcer 在 protect-mcp 中的定位
在 protect-mcp 插件体系中,policy-enforcer 是一个面向 Claude Code 的 Cedar 策略编写与审计 Agent。它与同目录下的 receipt-verifier(负责 Ed25519 签名回执与哈希链的离线验证)构成"先授权、后留痕"的两段式治理链路:
- policy-enforcer(本文主角):负责产出声明式、可形式化验证的 Cedar 策略,回答"这个 Agent 在项目里能做什么、不能做什么";
- receipt-verifier:负责验证每一次决策产生的签名回执,回答"当时到底发生了什么、有没有被篡改"。
protect-mcp 的核心机制是:每次工具调用前,PreToolUse 钩子调用 Cedar 求值器对调用做授权判定;每次工具调用后,PostToolUse 钩子为本次决策生成 Ed25519 签名回执,回执按parent_receipt_id形成哈希链,可离线验证。
二、Cedar 授权模型的核心知识
policy-enforcer 的"知识底座"是对 Cedar(AWS 开源授权引擎)的深入理解,这四块构成任何一条好策略的理论前提:
1. 语法核心:permit / forbid + 四元组
每条规则围绕四个维度展开:principal(主体,即 Agent)、action(动作,即工具)、resource(资源,即目标对象)、context(上下文,即求值时可用的动态属性)。
permit ( principal, action == Action::"Bash", resource ) when { context.command_pattern in ["git", "npm", "ls"] };2. 类型系统
Cedar 具备实体类型(entity types)、记录(records)、集合(sets)与扩展(extensions)。策略中通过Action::"..."、Tool::"..."等命名空间引用实体,通过context.xxx访问上下文属性。
3. 求值语义(最关键的一条)
- deny 是权威的:只要有任何一条
forbid规则匹配,无论存在多少条匹配的permit,最终结果都是拒绝; - permit 必须全部匹配:一个动作只有在某条 permit 规则的条件下匹配,且没有任何 forbid 规则拦截时,才被允许。
这意味着策略编写应以"最小权限"为默认值,显式放行,显式拦截高危操作。
4. Schema 定义与校验
如果项目配有 Cedar schema,策略应通过cedar validate做类型检查,确保context属性、实体引用与 schema 声明一致,再部署生效。
三、Claude Code 工具面与 protect-mcp 的集成方式
policy-enforcer 需要同时理解三层内容:工具本身、工具入参形状、以及求值期可用的上下文。
Claude Code 工具面
核心工具包括:Bash、Edit、Write、Read、Glob、Grep、WebFetch、WebSearch。每条工具的输入形状各不相同——命令字符串(Bash)、文件路径(Edit/Write/Read)、URL(WebFetch)、通配模式(Glob/Grep)。
求值期上下文
Cedar 策略通过context检查工具输入:Bash用context.command_pattern匹配命令族(git、npm、docker、rm 等);Edit/Write用context.path_starts_with限定文件系统范围。
protect-mcp 的运行时行为
从 hooks/hooks.json 可以看到插件的真实钩子配置:
{ "hooks": { "PreToolUse": [ { "matcher": ".*", "hooks": [ { "type": "command", "command": "npx protect-mcp@0.7.4 evaluate --policy \"${PROTECT_MCP_POLICY:-./protect.cedar}\" --tool \"$TOOL_NAME\" --input \"$TOOL_INPUT\" --fail-on-missing-policy false" } ] } ], "PostToolUse": [ { "matcher": ".*", "hooks": [ { "type": "command", "command": "npx protect-mcp@0.7.4 sign --tool \"$TOOL_NAME\" --input \"$TOOL_INPUT\" --output \"$TOOL_OUTPUT\" --receipts \"${PROTECT_MCP_RECEIPTS:-./receipts/}\" --key \"${PROTECT_MCP_KEY:-./protect-mcp.key}\"" } ] } ] } }三个关键机制:
- PreToolUse 钩子在每次工具调用前触发,对
$TOOL_NAME与$TOOL_INPUT执行 Cedar 求值。matcher: ".*"表示对所有工具生效; - Cedar
deny使钩子以退出码 2 结束,Claude Code 据此整体拦截该工具调用;permit则放行工具执行; - PostToolUse 钩子为每次决策生成回执,包含工具名、输入哈希、输出哈希、决策结果、策略 ID 与策略摘要、父回执 ID(链式链接)、公钥与签名,写入
./receipts/<timestamp>.json(路径可通过PROTECT_MCP_RECEIPTS环境变量覆盖,签名密钥通过PROTECT_MCP_KEY覆盖,策略文件通过PROTECT_MCP_POLICY覆盖,默认均为项目根目录下./开头的相对路径)。
四、策略编写工作流:六步法
policy-enforcer 在收到"编写一条 Cedar 策略"的请求时,遵循以下六步方法论,这也是任何项目落地策略时应复制的流程:
- 先问清项目的风险画像。这是只读即可安全的研究项目?还是命令会改动生产的部署流水线?还是存在审计要求的受监管环境(金融、医疗、受监管研究)?合适的策略完全取决于上下文;
- 从安全默认值出发。优先 allow-list(白名单)而非 deny-list(黑名单);先给出完成任务所需的最少工具集合,随着需求被证明再逐步增加;
- 善用 context 属性。策略通过
context检视工具输入——Bash用context.command_pattern匹配命令族,Edit/Write用context.path_starts_with限定文件系统范围; - 为高风险操作写配对规则。对危险动作同时写一条带具体条件的
permit和一条覆盖明显坏例的forbid——因为 Cedar 的forbid一旦匹配即为权威; - 逐条解释每条规则。Cedar 策略是安全关键资产,每条规则都需要注释说明其意图与所对抗的威胁模型;
- 对照 schema 校验。若项目有 Cedar schema,确保策略通过类型检查,部署前运行
cedar validate。
五、三档风险画像的完整策略模板
policy-enforcer 提供了三种场景的完整示例,下面全量继承并逐条批注,可直接复制改造。
5.1 研究项目(只读、安全)
// Allow all read-oriented tools permit ( principal, action in [Action::"Read", Action::"Glob", Action::"Grep"], resource ); // Web searches are fine, no fetch permit ( principal, action == Action::"WebSearch", resource ); // No writes, no shell forbid ( principal, action in [Action::"Write", Action::"Edit", Action::"Bash", Action::"WebFetch"], resource );设计要点:读类工具(Read/Glob/Grep)整体放行;WebSearch单独放行但WebFetch被禁用(搜索可以、抓取不行);末尾一条兜底forbid封死写入与 Shell,与文档开头"研究项目只读安全"的风险画像一致。
5.2 开发项目(限定写入、禁止破坏性命令)
// Reads are free permit ( principal, action in [Action::"Read", Action::"Glob", Action::"Grep"], resource ); // Writes only within the project directory permit ( principal, action in [Action::"Write", Action::"Edit"], resource ) when { context.path_starts_with == "./" }; // Safe shell commands only permit ( principal, action == Action::"Bash", resource ) when { context.command_pattern in [ "git", "npm", "pnpm", "yarn", "ls", "cat", "pwd", "echo", "test", "node", "python", "make" ] }; // Never destructive forbid ( principal, action == Action::"Bash", resource ) when { context.command_pattern in ["rm -rf", "dd", "mkfs", "shred"] };设计要点:写入被when { context.path_starts_with == "./" }限定在项目目录内;Shell 采用命令族白名单(git/npm/pnpm/yarn/ls/cat/pwd/echo/test/node/python/make);对rm -rf、dd、mkfs、shred等破坏性命令显式forbid。注意这里"配对规则"的体现:Bash既被 permit 白名单约束,又被 forbid 黑名单兜底,双重保险。
5.3 生产部署(严格、逐动作显式放行)
// Reads require evidenced trust tier permit ( principal, action in [Action::"Read", Action::"Grep"], resource ) when { context.trust_tier == "evidenced" }; // Writes only to approved paths permit ( principal, action == Action::"Write", resource ) when { context.trust_tier == "institutional" && context.path_starts_with in ["./deployments/", "./config/"] }; // Shell only for explicit deployment commands permit ( principal, action == Action::"Bash", resource ) when { context.trust_tier == "institutional" && context.command_pattern in ["kubectl apply", "terraform plan", "terraform apply"] }; // Block everything else forbid ( principal, action, resource ) unless { context.trust_tier in ["evidenced", "institutional"] };设计要点:引入context.trust_tier信任层级(evidenced/institutional)作为放行前提——读取需evidenced,写入仅限./deployments/、./config/且需institutional,Shell 仅放行kubectl apply、terraform plan、terraform apply等显式部署命令;最后一条forbid ... unless是"默认拒绝"的兜底闸门,凡 trust_tier 不在此列者一律拦截。这是三类模板中约束最严的一种,适合受监管环境。
六、从文档到仓库源码:策略的真实求值形状
文档中的示例为了可读性采用Action::"Read"这种抽象写法。而仓库测试夹具揭示了 protect-mcp(≥ 0.7.0)实际求值的实体形状,见 test/fixtures/test-policy.cedar 的头部注释:
protect-mcp >= 0.7.0 实际求值的形状为:principal
Agent::"<id>",actionAction::"MCP::Tool::call",resourceTool::"<toolName>",工具输入位于context.input。
对应的测试策略片段:
// Allow read-oriented tools. permit ( principal, action == Action::"MCP::Tool::call", resource ) when { resource == Tool::"Read" || resource == Tool::"Glob" || resource == Tool::"Grep" || resource == Tool::"WebSearch" }; // Allow Bash only for safe command prefixes. permit ( principal, action == Action::"MCP::Tool::call", resource == Tool::"Bash" ) when { context has input && context.input has command && (context.input.command like "git*" || context.input.command like "npm*" || context.input.command like "ls*" || context.input.command like "cat*" || context.input.command like "echo*" || context.input.command like "pwd*" || context.input.command like "node*") };从源码结构可以推断两条重要实现事实:
- 动作统一为
Action::"MCP::Tool::call",资源才是具体的工具名(Tool::"Bash"、Tool::"Read"等),这与文档示例的"动作即工具"抽象一一对应; - 上下文携带原始工具输入:
context.input.command是 Bash 的命令字符串,策略用like前缀匹配做安全命令白名单,用*rm -rf*、dd *、*mkfs*、*shred*等模式做破坏性命令拦截。
测试夹具同时给出了策略求值的端到端证据:
- pretool-allow-read.json:
Read读取./README.md,期望permit(退出码 0); - pretool-allow-bash-safe.json:
Bash执行git status,期望permit(退出码 0); - pretool-deny-bash-destructive.json:
Bash执行rm -rf /,期望forbid(退出码 2); - pretool-deny-write.json:
Write写入./secrets.env,期望forbid(退出码 2)。
七、审计既有策略:五步检查清单
policy-enforcer 在审核用户已写好的策略时,执行如下五步检查,这也是团队代码评审时可以套用的模板:
- 检查危险操作是否缺少
forbid规则——已知高危动作(递归删除、磁盘格式化、密码擦除等)必须有显式拦截; - 确认 context 属性已对照 schema 校验——
context.command_pattern、context.path_starts_with、context.trust_tier等字段必须在 schema 中有声明,否则求值行为不可预期; - 排查过宽的
permit规则——没有when子句的 permit 是典型反模式,等于无条件放行; - 检查逻辑缺口——例如
Edit被允许但Write被禁止,会导致绕过写入限制的路径; - 验证策略通过
cedar validate——部署前的最后一道类型与语法闸门。
八、策略执行的完整验证闭环
策略生效后的正确性验证,在仓库中由 test/README.md 描述的 8 项端到端测试兜底,覆盖"求值 → 签名 → 验证"全链路:
| # | 场景 | 期望退出码 |
|---|---|---|
| 1 | PreToolUse作用于Read | 0(permit) |
| 2 | PreToolUse作用于Bash git status | 0(permit) |
| 3 | PreToolUse作用于Bash rm -rf / | 2(forbid) |
| 4 | PreToolUse作用于Write | 2(forbid) |
| 5 | PostToolUse签名生成回执文件 | 0(成功) |
| 6 | 生成的回执符合 schema | 0(有效) |
| 7 | @veritasacta/verify接受该回执 | 0(有效) |
| 8 | 被篡改的回执被拒绝 | 1(tampered) |
第 8 项是关键的回归防线:翻转签名回执中的decision字段必须使 Ed25519 签名失效,验证器必须返回 1 而非 0。而 expected/receipt-schema.json 定义了回执的 JSON Schema,每个回执必须包含receipt_id、receipt_version、issuer_id、event_time、tool_name、input_hash、decision、policy_id、policy_digest、parent_receipt_id、public_key、signature等字段。
离线验证命令(由@veritasacta/verify提供):
# 验证单条回执:退出码 0=有效,1=被篡改,2=格式错误 npx @veritasacta/verify receipts/2026-04-15T10-30-00Z.json # 验证整条哈希链 npx @veritasacta/verify receipts/*.json在 Claude Code 内还可使用插件自带的斜杠命令:/verify-receipt <path>验证单条回执,/audit-chain [--last N]回溯验证./receipts/中的回执链。完整安装与配置流程(claude plugin install wshobson/agents/protect-mcp、npx protect-mcp@latest serve --enforce启动本地签名服务、.claude/settings.json挂载钩子)见 skills/protect-mcp-setup/SKILL.md。
九、总结:声明式治理 + 密码学留痕
policy-enforcer 的价值在于把"Agent 能做什么"从隐式的模型行为,转变为显式、声明式、可形式化验证的 Cedar 策略,并与 protect-mcp 的 Ed25519 签名回执体系结合,形成"事前授权、事后留痕、随时可证"的完整闭环:
- 事前:PreToolUse 钩子按 Cedar 策略逐次求值,
deny即拦截(退出码 2); - 事后:PostToolUse 钩子为每次决策签发 Ed25519 回执(RFC 8032 签名 + RFC 8785 JCS 规范化),经
parent_receipt_id哈希链接续成链; - 随时:任何第三方可用
@veritasacta/verify离线验证单条回执或整条链,无需信任运营商、无需联网。
编写策略时,记住 policy-enforcer 的核心纪律:先问风险画像、从白名单默认值起步、用 context 属性收窄作用域、为高危操作写 permit/forbid 配对规则、逐条注释威胁模型、部署前过cedar validate。这样产出的策略,既是开发期的安全护栏,也是合规审计期可直接呈堂的正式证据。
【免费下载链接】agentsMulti-harness agentic plugin marketplace for Claude Code, Codex, Cursor, OpenCode, GitHub Copilot, and Google Antigravity项目地址: https://gitcode.com/GitHub_Trending/agents24/agents
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考