agent-access-review
面向 AI agent 的用户访问审查。它会找出组织运行的所有 agent 和 MCP server,梳理出每个能读取、写入、执行和发送什么,用 12 项控制措施检验这些访问权限,并生成审计师期望的交付包:审查人签核表、带哈希的证据,以及映射到 NIST 800-53、SOC 2、NIST AI RMF 和 OWASP Top 10 for LLM Applications 的 OSCAL 评估结果。
一句话概括:企业原本每季度对员工执行的“谁有权访问什么?”审查,如今套用到 AI agent 上。

真实案例研究:从官方 README 中照抄 MCP 配置会得到什么:9 个里有 8 个未锁定版本,9 个里有 7 个带有未经审查的写入/发送/执行权限,而且一旦组合起来就形成一条数据外泄路径。

快速开始(2 分钟)
需要 Python 3.11+。无需云账户、API 密钥或网络访问。
pip install agent-access-review
agent-access-review demo
demo 会审查五个示例 agent,并把交付包写入 review-demo/。打开 review-demo/access-review.md,或者直接阅读此处的示例报告,无需安装任何东西。
然后审查你自己的环境:
| 工具 | MCP 配置所在位置 | 命令 |
|---|---|---|
| Claude Desktop (macOS) | ~/Library/Application Support/Claude/claude_desktop_config.json | agent-access-review scan ~/Library/Application\ Support/Claude/claude_desktop_config.json |
| Claude Desktop (Windows) | %APPDATA%\Claude\claude_desktop_config.json | PowerShell: agent-access-review scan "$env:APPDATA\Claude\claude_desktop_config.json" |
| Cursor | ~/.cursor/mcp.json(全局)或 .cursor/mcp.json(项目) | agent-access-review scan ~/.cursor/mcp.json |
| VS Code | 项目中的 .vscode/mcp.json | agent-access-review scan .vscode/mcp.json |
| 你自己的 agent | 你维护的 agents.yaml | agent-access-review scan agents.yaml |
配置中的密钥会在写入任何内容之前被屏蔽,因此输出可以安全分享。
| 术语 | 含义 |
|---|---|
| AI agent | 一种能够通过工具采取行动的 AI 模型:读取文件、查询数据库、发送邮件、运行命令。 |
| MCP (Model Context Protocol) | Claude Desktop、Cursor 和 VS Code 等应用将工具接入 AI 的标准方式。配置中的每个“MCP server”就是 agent 可以使用的一个工具。 |
| User access review (UAR) | 周期性检查:列出每个人的访问权限,由负责人确认是否仍然需要,其余权限予以撤销。审计师在 SOX 和 SOC 2 下对其进行测试。 |
| ITGC | IT 一般控制:审计师为财务报告测试的基线控制(访问、变更管理、运营)。 |
| Non-human identity | 由软件而非人员使用的账户:服务账户、API 密钥,以及如今的 AI agent。 |
| Exfiltration path | 数据离开组织的路径。此处指:一个 agent 既能读取敏感数据,又能读取攻击者可写入的内容,还能把数据发送出去。 |
| Prompt injection | 将指令隐藏在 AI 读取的内容(邮件、网页或工单)中,使其做出用户从未要求的事情。 |
| OSCAL | NIST 用于安全控制措施和评估结果的机器可读格式,使 GRC 工具能够自动导入发现结果。 |
问题
每个 SOX 和 SOC 2 项目都会执行季度用户访问审查:列出谁有权访问什么,由负责人确认是否仍然需要,撤销其余权限,保留证据。AI agent 如今持有同类的访问权限(API token、数据库连接、shell 访问、邮箱),而几乎没有人对它们进行审查:
- 没有负责人,没有审批。开发者往自己的 Claude Desktop 或 Cursor 配置里加一个 MCP server,agent 立刻就拿到了他的 GitHub token 和主目录。没有工单,没有审批人,也永远不会出现在访问审查里。
- 默认权限过宽。配置指南叫人授予整个主目录、仓库的 full-control 权限范围,或者租户级的 *.All Microsoft Graph 权限。
- 明文存放的密钥。MCP 配置里经常直接放着原始 token 和数据库密码。
- 一条新的攻击路径。一个能读敏感数据、能读攻击者写的内容(GitHub issue、邮件、网页)、又能把数据发出去的 agent,可能被 prompt injection 诱导泄露出这些数据。传统访问控制不会去看这种组合。
Agent 是非人类身份。它们需要和人一样的 joiner/mover/leaver 与复核(recertification)控制,外加一些 AI 特有的控制。
检查项
| ID | 控制项 | 严重性 | 框架 |
|---|---|---|---|
| AAR-01 | 每个 agent 都有一个可问责的人类负责人 | 高 | NIST 800-53 AC-2 · SOC 2 CC6.2 · AI RMF GOVERN 2.1 |
| AAR-02 | 访问经过审批,并在 90 天内完成复核 | 高 / 中 | AC-2 · CC6.2, CC6.3 |
| AAR-03 | 特权操作(写入、删除、发送)需要人工审批 | 高 / 中 | AC-6 · CC6.3 · AI RMF MAP 3.5 · OWASP LLM06 |
| AAR-04 | 最小权限:不得使用主目录、通配符、管理员或租户级范围 | 高 | AC-6 · CC6.3 · LLM06 |
| AAR-05 | 代码和 shell 执行在沙箱中进行 | 高 | CM-7, AC-6 · LLM05, LLM06 |
| AAR-06 | 无硬编码凭据 | 高 | IA-5 · CC6.1 |
| AAR-07 | 凭据在 90 天内轮换 | 中 / 低 | IA-5 · CC6.1 |
| AAR-08 | 90 天后移除闲置访问 | 中 / 低 | AC-2(3) · CC6.2 |
| AAR-09 | 不存在外泄路径(敏感数据 + 不可信输入 + 出境通道,且无人工把关) | 高 | AC-4 · AI RMF MEASURE 2.7 · LLM01, LLM02 |
| AAR-10 | 工具包固定到某个版本或镜像 digest | 中 | SR-3 · LLM03 |
| AAR-11 | 远程 MCP server 使用 HTTPS | 高 | SC-8 · CC6.7 |
| AAR-12 | 每个工具的能力都有清单记录 | 中 | CM-8 · AI RMF GOVERN 1.6 |
| AAR-13 | 不使用已弃用或已归档的 MCP server(须指明仍在维护的替代品) | 高 | SA-22, SI-2 · CC7.1 · LLM03 |
| AAR-14 | 不使用存在已知漏洞的 MCP server 版本,也不使用被发布为恶意软件的包(来自 OSV 快照) | 高 / 中 / 低 | RA-5, SI-2 · CC7.1 · LLM03 |
每个控制项在 controls.yaml 中只定义一次,并交叉映射到各个框架,因此一次测试就能为所有框架产出证据。这些映射是我个人的判断,在真实审计中依赖之前应先对照官方的 crosswalk 检查。
AI 特有的风险:外泄路径(AAR-09)
传统访问审查一次只看一个权限。对 AI agent 来说,危险在于组合:一个 agent 能读敏感数据、会读外部人员写的内容,并且能把数据发出去。单独看每一环都无害。合在一起,藏在邮件、工单、issue 或网页里的一次 prompt injection 就能指示 agent 泄露数据。在任一环上加人工审批步骤,就能打断这条链。
flowchart TB
subgraph BAD["❌ finance-ops-agent: flagged AAR-09"]
direction LR
U1["📨 Untrusted input<br/>anyone can email the AP inbox"] -->|"hidden instruction:<br/>forward vendor bank details"| A1(("🤖 agent"))
D1[("🔒 Restricted data<br/>NetSuite vendor bills")] -->|reads| A1
A1 -->|"sends email<br/>no human approval"| X1["🌐 Egress<br/>any recipient"]
end
subgraph GOOD["✅ support-copilot: passes"]
direction LR
U2["📨 Untrusted input<br/>customer tickets"] --> A2(("🤖 agent"))
D2[("🔒 Confidential data<br/>ticket history")] --> A2
A2 -->|"drafts reply"| H{{"👤 Human approves"}}
H -->|sends| X2["Customer"]
end
BAD ~~~ GOOD
Agent-access-review – 面向 AI agent 的 MCP 配置的访问审查
classDef risk fill:#fee2e2,stroke:#dc2626,color:#7f1d1d
classDef safe fill:#dcfce7,stroke:#16a34a,color:#14532d
classDef gate fill:#fef3c7,stroke:#d97706,color:#78350f
class U1,D1,X1 risk
class U2,D2,X2 safe
class H gate
这对应 OWASP LLM01(提示注入)和 LLM02(敏感信息泄露)、NIST 800-53 AC-4(信息流强制)以及 NIST AI RMF MEASURE 2.7(AI 安全性与韧性)。
工作原理
flowchart LR
subgraph IN["📥 Inputs"]
A["Claude Desktop / Cursor<br/>MCP configs"]
B["VS Code<br/>.vscode/mcp.json"]
C["agents.yaml<br/>LangChain · CrewAI · custom"]
end
subgraph CORE["⚙️ agent_access_review"]
P["Parsers<br/>secrets redacted here"]
K[("Capability catalog<br/>catalog.yaml")]
I["Normalized inventory<br/>agent → grants + credentials"]
X["14 checks<br/>AAR-01 … AAR-14"]
M[("Control catalog<br/>controls.yaml")]
end
subgraph OUT["📤 Audit outputs"]
R["access-review.md<br/>reviewer packet + sign-off"]
S["access-review.csv<br/>Keep / Modify / Revoke"]
E["evidence/*.json<br/>SHA-256 hashed"]
O["OSCAL 1.1.2<br/>assessment results"]
end
A & B & C --> P --> I --> X
K -. "what each server can do" .-> I
M -. "NIST · SOC 2 · AI RMF · OWASP" .-> X
X --> R & S & E & O
classDef input fill:#e0f2fe,stroke:#0284c7,color:#0c4a6e
classDef core fill:#ede9fe,stroke:#7c3aed,color:#3b0764
classDef data fill:#fef3c7,stroke:#d97706,color:#78350f
classDef output fill:#dcfce7,stroke:#16a34a,color:#14532d
class A,B,C input
class P,I,X core
class K,M data
class R,S,E,O output
- 发现。把它指向文件或文件夹。它读取两种格式:MCP 配置(Claude Desktop、Cursor、VS Code 的 .vscode/mcp.json):本地的 npx / uvx / docker server 和远程 HTTP server。这些配置既没有归属人也没有审批,这正是它们构成影子 AI 问题的原因。Agent 清单(agents.yaml):面向 LangChain、CrewAI 或自定义 agent 的受管记录,包含归属人、审批、工具、权限范围、凭据和最后使用日期。
- MCP 配置(Claude Desktop、Cursor、VS Code 的 .vscode/mcp.json):本地的 npx / uvx / docker server 和远程 HTTP server。这些配置既没有归属人也没有审批,这正是它们构成影子 AI 问题的原因。
- Agent 清单(agents.yaml):面向 LangChain、CrewAI 或自定义 agent 的受管记录,包含归属人、审批、工具、权限范围、凭据和最后使用日期。
- 分类。MCP 配置只说明运行哪个 server,而不说明它能做什么。能力目录把 server 名称转换为一项授权:动作、数据分级、是否接收不受信任的内容,以及能否把数据发出去。未知 server 会被标记(AAR-12),绝不会被默认为安全。
- 先脱敏,再哈希。密钥在解析阶段、在任何内容被存储之前就被掩盖,因此证据可以与审计人员共享。每个 agent 的清单和脱敏后的源记录都以规范化 JSON 做 SHA-256 哈希,因此此后对证据的任何改动都可被检测到。
- 审查。每一项工具授权都会得到一个建议决定(Keep / Modify / Revoke / Investigate),另有一列留空供审查人填写,形态与用户访问审查表格一致。
- 导出。OSCAL 1.1.2 评估结果:每个 agent 一条 observation,指向其证据文件和哈希;每个 NIST 800-53 控制目标一条 finding。
审查周期
这就是 SOX 和 SOC 2 审计人员早已针对人员执行的季度用户访问审查,只不过对象换成了 agent:
flowchart LR
D["1 · Discover<br/>configs + manifests"] --> C["2 · Classify<br/>capability catalog"]
C --> T["3 · Test<br/>12 controls"]
T --> R{"4 · Owner review<br/>per tool grant"}
R -->|Keep| E["5 · Evidence<br/>hashed JSON + OSCAL"]
R -->|"Modify / Revoke"| F["Remediate<br/>ticket + fix"]
F -->|re-scan| T
E --> Q["6 · Recertify<br/>every 90 days"]
Q -.-> D
classDef step fill:#e0f2fe,stroke:#0284c7,color:#0c4a6e
classDef decision fill:#fef3c7,stroke:#d97706,color:#78350f
classDef fix fill:#fee2e2,stroke:#dc2626,color:#7f1d1d
classDef done fill:#dcfce7,stroke:#16a34a,color:#14532d
class D,C,T,Q step
class R decision
class F fix
class E done
| 用户访问复核(人员) | Agent 访问复核(本工具) |
|---|---|
| HR 数据源列出每位员工 | 配置文件和清单列出每个 agent 与 MCP server |
| 管理者确认每项权限 | Agent 负责人为每项工具授权标记 Keep / Modify / Revoke |
| 已离职用户被移除 | 无主 agent 被标记(AAR-01),未使用的工具被撤销(AAR-08) |
| 特权访问接受额外审查 | 写入、发送、删除和 shell 访问需要人工把关(AAR-03、AAR-05) |
| 以截图和电子表格作为证据 | 哈希化的 JSON 证据和 OSCAL 输出 |
示例运行
随附的示例包括一个开发者的 Claude Desktop 配置、一个 VS Code MCP 配置,以及三个受治理的 agent。完整输出已提交在 sample-output/ 中:reviewer packet、决策表、哈希化证据和 OSCAL。
$ agent-access-review demo
AGENT TOOLS HIGH MED LOW
support-copilot 2 0 0 0
finance-ops-agent 4 4 3 0
devops-agent 2 6 1 1
claude-desktop-config 6 12 5 1
vscode-mcp 3 6 2 3
5 个 agent · 17 项工具授权 · 44 项发现
%%{init: {"themeVariables": {"pie1": "#dc2626", "pie2": "#f59e0b", "pie3": "#16a34a", "pieSectionTextColor": "#ffffff", "pieStrokeColor": "#ffffff"}}}%%
pie showData title Findings by severity
"High" : 28
"Medium" : 11
"Low" : 5
| 控制项 | 未通过的 agent 数 |
|---|---|
| AAR-02 · 在 90 天内获批并重新认证 | 4 / 5 |
| AAR-03 · 特权操作需人工批准 | 4 / 5 |
| AAR-08 · 移除休眠访问权限 | 4 / 5 |
| AAR-01 · 明确责任人 | 3 / 5 |
| AAR-04 · 最小权限 | 3 / 5 |
| AAR-09 · 无数据外泄路径 | 3 / 5 |
| AAR-05、06、07、10、12 | 各 2 / 5 |
| AAR-11、13、14 | 各 1 / 5 |
| 工具 | 操作 | 数据 | 建议 | 复核人决定 |
|---|---|---|---|---|
| netsuite | read, write | restricted | Modify | ☐ Keep ☐ Modify ☐ Revoke |
| outlook | read, send | confidential | Modify | ☐ Keep ☐ Modify ☐ Revoke |
| sharepoint | read | confidential | Modify | ☐ Keep ☐ Modify ☐ Revoke |
| expense-ocr | read | confidential | Revoke | ☐ Keep ☐ Modify ☐ Revoke |
| 检查项 | 严重程度 | 发现 |
|---|---|---|
| AAR-09 | high | 外泄路径:读取敏感数据,接收不可信内容(outlook),并能向外发送数据(outlook),且无人工批准。 |
| AAR-03 | high | netsuite 可以写入受限数据,而无需人工逐次批准操作。 |
| AAR-03 | high | outlook 可以发送机密数据,而无需人工逐次批准操作。 |
| AAR-04 | high | sharepoint 上的权限过宽:Sites.Read.All 。 |
| AAR-02 | medium | 上次批准时间为 216 天前,批准人 cfo-office(上限 90 天)。 |
| AAR-07 | medium | netsuite.NETSUITE_TOKEN 上次轮换为 315 天前(上限 90 天)。 |
| AAR-08 | medium | expense-ocr 已 164 天未被使用。 |
它发现的部分问题:
- finance-ops-agent 在 NetSuite 中读取供应商账单(restricted 数据),读取供应商可发信的收件箱(不可信输入),并能发送邮件(egress),且无人工批准。这构成一条 AAR-09 外泄路径:一封精心构造的发票邮件就可能让该 agent 把供应商银行信息寄出去。它的批准也已是 216 天前,其 OCR 工具已有 164 天未被使用,因此被标记为 Revoke。
- claude-desktop-config 把整个 /Users/dev 主目录、一个未沙箱化的终端、一个 GitHub token 和明文形式的数据库密码交给某个 agent,并运行四个未固定版本(unpinned)的包。
- support-copilot 通过了所有检查:它会写入工单,但任何内容发出前都有人工批准。
文章标题:Agent-access-review – 针对 AI 代理 MCP 配置的访问审查
正文: 每次推送时,CI 都会重新生成 sample-output/,如果它与已提交的内容不一致就会失败,因此示例始终与当前代码一致。
如何解读结果
建议决策(每个工具授权一项,在审查包和 CSV 中):
| 决策 | 含义 | 所有者做什么 |
|---|---|---|
| 保留 | 该工具没有发现问题 | 确认它仍然需要并签字确认 |
| 修改 | 访问是需要的,但当前配置不安全 | 缩小范围、增加人工审批、将密钥迁移到保险库、固定版本 |
| 撤销 | 90 多天未使用 | 移除它;如果再次需要则重新申请 |
| 调查 | 该工具不在能力目录中,因此无法判断其访问权限 | 查明它能做什么,然后将其添加到 catalog.yaml |
严重程度:高表示在代理下次使用前修复(无主代理、明文密钥、数据外泄路径)。中表示在审查周期内修复(过期的审批、未固定版本的包)。低表示证据缺口而非风险(无使用日志、无轮换日期)。
审查者填写空的审查者决策列和签字表。完整的审查包加上经过哈希处理的证据文件就是本次审查的审计证据。
运行它
从发布版本安装(如快速开始中所示),或从源码运行(依赖在 requirements.txt 中精确固定):
git clone https://github.com/Mihirajmera/agent-access-review.git
cd agent-access-review
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
python -m agent_access_review demo # writes review-demo/
pytest -q
扫描选项:--out DIR(默认 review/)、--as-of YYYY-MM-DD(用于重新认证、轮换和休眠的日期)、--fail-on high|medium|low(如果发现的问题达到或超过该严重程度,则退出码为 1)。agent-access-review --help 列出示例。
作为 CI 门禁
将你的 MCP 配置和 agents.yaml 保留在仓库中,并阻止那些给代理授予未固定版本、已归档、存在漏洞或过度权限的拉取请求:
# .github/workflows/agent-access.yml
on: pull_request
permissions:
contents: read
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: Mihirajmera/[email protected] # or a commit SHA
with:
paths: .mcp.json .vscode/mcp.json agents.yaml # files or folders; default "."
fail-on: high # high | medium | low | never
审查包会进入作业摘要,并且当发现问题达到或超过 fail-on 时该步骤会失败。没有配置的仓库会收到通知而不是失败。报告输出包含审查包的路径,因此你可以将该文件夹(默认 agent-access-review/)作为产物上传。
固定到发布标签或提交 SHA,而不是 main,这样新版本就无法在未经审查的情况下改变你的门禁。这就是将控制项 AAR-10 应用于此工具本身。不使用该 action 时,pip install agent-access-review 和 agent-access-review scan … --fail-on high 也能实现相同效果。
设计决策
- 代理是身份,不是应用。本审查参照 ITGC 用户访问审查(所有者、审批、重新认证、休眠、凭据卫生)建模,因为这是审计人员已经测试的控制框架。AI 特有的风险是在此基础上追加的,而不是替代。
- AAR-09 关注的是组合,而不是单个权限。数据外泄路径的每一段单独来看都是无害的。只有当同一个代理同时持有全部三段时风险才会出现,这就是传统权限审查会漏掉它的原因。在不可信输入或出口环节增加人工审批步骤可以打破这条链。
- 对未知工具采取失败关闭策略。未分类的 MCP 服务器是一个发现项,而不是通过。目录是数据,因此 GRC 团队可以在不修改代码的情况下扩展它。
- 先脱敏再哈希。包含密钥的证据无法共享,而事后脱敏会破坏哈希。脱敏在解析器中完成。
- Docker 标签被视为未固定。只有镜像摘要才是不可变的。:v1.0 可以被重新推送。
局限性
- 它审查配置,而非运行时行为。它能看出代理可以发送电子邮件,但不知道实际发送了什么。使用日期来自清单,在生产环境中这些日期会由工具调用日志提供。
- 能力目录是近似的:它描述服务器默认能做什么,而令牌的实际范围(例如细粒度的 GitHub PAT)可能更窄。清单格式允许你记录确切的范围。
- 带注释的 JSON 配置会被跳过并给出警告。
在企业规模下我会怎么做
- 从端点发现,而不是从文件。通过 MDM 或 EDR 从受管理的笔记本电脑收集 MCP 配置,并从 IdP(服务主体、OAuth 应用)收集代理身份,以便影子代理自动浮现。
- 从日志提供使用情况。从 MCP 网关或代理日志填充 last_used,使 AAR-08 基于证据而非自我报告。
- 在 GRC 平台中运行审查。将决策表推送到 ServiceNow GRC 或 SailPoint 作为认证活动,包含所有者、截止日期和升级,并自动撤销未认证的授权。
- 闭环。将未满足的 OSCAL 发现转化为 POA&M 事项,并附上所有者和截止日期。
贡献
最简单的帮助方式是将 MCP 服务器添加到能力目录:参见 CONTRIBUTING.md。变更列在 CHANGELOG 中。