AI 编码的权限、数据边界与改动审查
一次看似普通的“修一下构建”曾产生一份危险补丁。代理先读取了整个仓库,再从本地配置中拿到测试环境连接信息;随后它为解决依赖问题执行包安装,顺手更新锁文件,又在 Git 状态不干净时把同事的草稿一起纳入提交候选。最终测试没有真的跑完,但总结写着“构建已验证”。这不是模型是否聪明的问题,而是工具拥有了超过任务所需的读取、写入、shell、网络和 Git 能力,同时又没有让证据链在每一步可见。
AI 编码工具应被视为一个会读取上下文、提出动作、调用执行器并产生持久改动的协作主体。它不能获得“开发者已有的一切权限”的镜像副本。真正可用的流程把权限拆开:读取指定目录不等于读取凭证;编辑文件不等于执行 shell;运行测试不等于访问网络;创建本地提交不等于推送;调用 MCP 不等于继承 MCP 背后的云账号。每一次扩权都应有明确目的、有限范围、到期动作和可审计的批准人。
先识别越权改动留下的第一份证据
面对可疑 AI 补丁,最有价值的不是对话摘要,而是工作区状态、精确 diff、命令退出码、测试报告和执行日志。先停止进一步自动执行,保存当前分支和未提交改动的可读快照,再把“工具声称做过什么”与“仓库实际变了什么”分开。不要在没有检查 diff 的情况下让代理继续修复;第二轮编辑会覆盖最初的因果关系。
# 只读取状态和差异;命令不修改工作区
git status --short
git diff --stat
git diff -- . ':!*.lock'
git diff --cached预期证据是清楚的文件集合、未暂存与已暂存的区别,以及可逐行审查的补丁。出现意外的 lockfile、配置、生成文件、凭证样式文件或无关目录时,先将它们列为待解释项。不要用 git reset --hard 消除现场;那会同时抹掉用户原有工作和代理改动。若需要暂存当前现场给资深审查者,创建受控补丁副本或使用新的隔离分支,并明确它不是已验证成果。
AI 的“已完成”只能描述它实际运行且保留了输出的步骤。没有输出、被权限拒绝、超时、因网络离线而跳过,或只做了静态阅读的工作,都要明确标为未执行或失败。这个区分能让审查者知道何处需要重跑,也能避免把模型自然语言中的确定语气当成运行事实。
工具准入从供应链、能力和退出路径同时审查
准入 AI 工具时,产品名和插件商店评分远远不够。审查对象包括桌面客户端、IDE 扩展、CLI、运行时、模型提供方、MCP server、代理脚本、云端 API、遥测 SDK 和自动更新通道。每个对象都要能回答:它由谁发布,更新如何获得,读取哪些目录,向哪些域名发送什么,执行哪些命令,持有哪些 token,日志和缓存留在哪里,禁用后怎样清理。NIST SSDF 将开发工具链本身纳入安全开发实践,面向生成式 AI 的 SSDF 补充 进一步要求按风险验证工具链、隔离执行环境,并把敏感数据留在组织批准的位置;这意味着插件、模型和 MCP 都是供应链资产,不能只审查最终补丁。
将“能否安装”与“能否进入某类仓库”分离。没有网络权限的只读代码解释工具,和可执行 shell、上传上下文、调用云 MCP、可推送 Git 的代理,风险等级完全不同。工具包、模型或 MCP 更新后,能力声明可能变化,旧批准并不自动覆盖新版本。对不能导出配置、不能约束网络、不能关闭高风险能力、不能说明数据去向的工具,应限制在隔离演示环境,而不是直接接入含业务数据的工作区。
# 工具准入条目示例:字段应进入受控登记系统,真实 token 不写入此文件
tool: <tool-name>
publisher: <official-publisher>
version: <approved-version>
allowed_repositories:
- <classification-low-or-medium>
capabilities:
read_workspace: allowlist
edit_files: review-before-apply
shell: deny-by-default
network: approved-domains-only
git_push: denied
mcp: approved-servers-only
data_retention: metadata-only
owner: <team-role>
review_trigger: version-or-permission-change反向实验不是让工具真的读取生产数据,而是在隔离测试仓库中放置标记文件,例如 private-demo.txt,并在规则中明确拒绝该路径。让工具执行“扫描全部文件”的无害任务,预期证据是拒绝、跳过记录或可配置的排除规则命中。若工具无法阻止读取,不能以“提示它不要读”作为控制措施;应缩小其工作区、移除文件、使用权限更低的账号,或拒绝在该仓库使用。
敏感文件和上下文先被拒绝,才谈模型效果
对代码代理而言,上下文入口远多于一个文件选择器:工作区索引、全文搜索、打开的编辑器标签、终端历史、Git diff、诊断信息、日志附件、剪贴板、浏览器页面、任务系统和 MCP 返回值都可能进入模型请求。模型在本机运行只减少一段外发路径,不会阻止 IDE 插件、浏览器扩展、远程 MCP 或云端补全服务读取这些内容。
默认拒绝的内容至少包括:私钥与证书、.env 和 secret 文件、生产或客户数据导出、访问 token、密码管理器导出、身份文件、完整 CI 日志、数据库转储、未公开漏洞细节、受合同保护的附件和大规模源码镜像。还要防范“看起来不是密钥”的敏感上下文,例如内部主机名、账号标识、用户邮箱、工单文本、调试转储和带 Authorization 头的 HTTP 录制。
# AI 上下文排除示例;需同时配置所用 IDE/代理自己的忽略机制
.env
.env.*
!.env.example
*.pem
*.key
secrets/
production-data/
**/*credential*
**/*token*这类规则不是万能屏障。忽略文件通常只影响 Git 或某一个索引器,未必阻止终端命令、已打开文件、MCP 工具或手工粘贴。项目应在代理规则中再给出路径 allowlist,例如只允许 src/、tests/ 和当前任务文件;将敏感配置迁出工作区或用占位模板替代真实文件。需要排查配置问题时,先写最小脱敏复现,保留变量名、删除变量值、域名、账号和内部路径。
反向实验可在测试仓库放置一段格式正确但无效的示例密钥,并要求代理总结目录。预期是工具报告它被排除,且请求审计中没有该字符串。若供应商没有请求审计能力,就通过代理侧日志、网络代理元数据和隔离工作区交叉验证。绝不把真实 token 作为“是否泄漏”的探针;一旦发送出去,测试本身已经造成事故。
shell、network、Git、MCP 与云权限必须分层
权限矩阵的价值在于拆掉“允许使用 AI”这个模糊开关。读取、编辑、shell、网络、Git、MCP 和云控制台分别绑定不同风险:shell 可以删除或安装;网络可以上传上下文或拉取未审查依赖;Git 可以改写历史或推送;MCP 可以把自然语言转成数据库、文件系统、工单或云资源操作;云凭证还能把错误扩大到基础设施和费用。
| 能力 | 默认策略 | 允许时的约束 | 需要留下的证据 |
|---|---|---|---|
| 工作区读取 | 仅任务目录 | 路径 allowlist、排除敏感文件 | 读取范围或索引规则 |
| 文件编辑 | 生成补丁,不自动覆盖 | 限定文件类型、逐次 diff | 每轮 diff 与审查结论 |
| shell | 拒绝 | 固定命令 allowlist、无交互、目录限定 | 完整命令、退出码、摘要输出 |
| network | 拒绝 | 域名 allowlist、代理、最短时段 | 目的域名、传输类型、批准记录 |
| Git 写入 | 允许本地分支 | 禁止 force、禁止推送、禁止改历史 | 状态、diff、提交前审查 |
| 依赖安装 | 拒绝临时包名 | 批准 registry、锁定版本与来源、先审后装 | manifest/lockfile diff、许可与漏洞结果 |
| MCP | 仅批准 server | 工具白名单、输入 schema、短期凭证 | server 版本、调用日志、返回摘要 |
| 云资源 | 默认拒绝 | 临时角色、项目/区域限制、预算阈值 | 身份、资源 diff、费用标签 |
矩阵不应停在文档。工具若支持权限策略,将它们编码为项目规则、容器权限、IDE workspace trust、shell wrapper、代理防火墙和云 IAM 条件;工具不支持时,使用隔离用户、隔离工作区和无凭证环境降低影响面。将生产 kubeconfig、云 CLI profile、SSH agent 和浏览器登录会话与 AI 实验环境分离,避免“没有调用云命令”却被 MCP 或子进程间接使用。这与 NIST SP 800-53 的最小权限、职责分离和审计问责控制一致;对可调用外部动作的代理,OWASP Excessive Agency 还要求工具权限最小化、在用户授权上下文中执行,并对高影响动作设置人工批准。
# 受控 shell wrapper 的调用示例:只允许项目内的测试命令
Set-Location $PWD
git rev-parse --show-toplevel
npm run test:unit -- --runInBand
$LASTEXITCODE正向验证要求 wrapper 输出实际仓库根、执行的命令和退出码。反向验证让代理提出 git push、下载任意脚本或访问未批准域名,预期是执行器在命令到达 shell 前拒绝,并记录拒绝原因。若拒绝发生在命令执行以后,控制点太晚;“执行完再人工看一眼”不能挽回外发或破坏性操作。
扩权审批要能过期、撤销并回放
真正需要扩权的场景总会出现,例如安装一个审查过的开发依赖、访问受控包仓库、读取一段脱敏失败日志,或运行需要临时云模拟器的集成测试。正确做法不是让开发者切到“全自动”,而是给一次任务创建短期、可描述的授权:任务号、目标、目录、命令或域名、有效期、禁止项、批准角色和失败后的撤销动作。
扩权申请示例
任务:<issue-or-change-id>
目的:运行受控集成测试以复现连接失败
读取:tests/integration/ 与脱敏 fixture
写入:临时目录 .tmp/ai-test/,结束后删除
shell:npm run test:integration -- --case connection
network:仅 packages.example.invalid 与本地 mock 端口
Git:禁止提交、禁止推送、禁止修改历史
到期:任务结束或 <short-duration>,以先到者为准
批准:<reviewer-role>
撤销:删除临时凭证,停止进程,复查 git diff 与访问日志审批人批准的是一个可观察动作,不是“相信这个代理”。扩权前先确认当前工作区干净或已知哪些改动属于用户;扩权期间将命令输出写入受控任务记录;结束后立即撤销环境变量、临时 token、端口转发和角色会话。网络或云权限应使用短期、最小范围身份,而不是复制个人长期密钥。任何由 AI 建议的权限更改都必须由拥有该资源责任的人工角色确认。
反向演练应模拟权限过期:让临时 token 或 allowlist 在测试中失效,预期请求被拒绝且业务代码不会回退到硬编码密钥、宽泛匿名访问或无限重试。若失败后工具自动建议关闭 TLS、将 token 写进 .env、改为管理员角色,视为高风险信号;停止执行,回到授权人重新评估。
diff、测试和密钥扫描构成合并前的三种独立证据
AI 生成的补丁需要同时接受语义审查、行为验证和敏感信息扫描。diff 回答“改变了什么”;测试回答“在给定环境里是否仍满足已声明行为”;密钥扫描回答“是否出现明显的凭证模式、私钥或高熵值”。三者相互补充,任何一个成功都不能代替另一个。格式化通过不表示行为正确;测试通过不表示没有泄漏;扫描无告警也不能证明没有把客户数据写进常量。
# 先锁定补丁范围,再执行项目已有的验证和扫描入口
git diff --check
git diff --name-only
npm run test
npm run lint
gitleaks detect --no-banner --redact这是一组示例命令,实际项目应使用已维护的测试、格式化和扫描脚本。每条命令都要保存退出码及关键摘要,失败时保留报告位置但不要将敏感扫描命中内容复制到聊天窗口。扫描命中后先判断是否为真实秘密、历史测试 fixture 或允许的占位符;真实秘密的处置是立即撤销和轮换,删除工作区文本只是后续步骤,因为它可能已经进入 shell 历史、构建缓存、提交对象或远程日志。
审查者应从变更风险而非模型叙述出发:权限是否扩大,依赖是否变化,网络请求是否新增,解析边界是否放宽,错误处理是否吞掉失败,配置是否改变默认值,生成文件是否可重现,测试是否真的覆盖关键分支。对于基础设施、认证、数据删除、支付、生产配置与访问控制,要求独立人工审查,不让同一位提出扩权的人同时批准和合并。OWASP Secure Coding with AI 明确要求 AI 辅助变更具有人类 owner、合并前获得明确人工批准,并保留批准人与工具/模型版本的审计线索;“模型生成”不能转移维护与安全责任。
依赖、规则文件与测试不能由同一次生成自证安全
AI 建议新增包时,包名本身就是不可信输入。先在组织批准的 registry 中确认包真实存在、发布者与维护历史,再核对许可证、已知漏洞、安装脚本、传递依赖和目标平台;随后固定允许的版本或摘要,通过正常依赖更新流程生成 lockfile。禁止直接执行模型给出的 npm install、pip install、curl | sh 或临时 registry 命令。若包只用于一次数据转换,优先使用已有工具或隔离的一次性环境,不要让试验依赖永久进入应用供应链。
规则文件、CI workflow、包管理脚本、容器文件和测试也是高权限代码。代理若能先修改自己的忽略规则、命令 allowlist 或测试断言,再用修改后的门禁证明补丁通过,就形成了自我批准。仓库应由 CODEOWNERS 或等价机制保护这些路径;任何新增网络、preinstall/postinstall、第三方 action、MCP server、自动下载或测试删除都触发独立审查。OWASP 的AI 辅助安全编码指南特别将幻觉依赖、持久规则文件、越界改动和测试伪造列为不同攻击面,不能用一次绿灯合并判断覆盖它们。
依赖变更的合并证据
1. manifest 与 lockfile 的精确 diff
2. registry、发布者、版本或摘要的来源证明
3. 许可证和漏洞扫描结果;例外有 owner 与到期时间
4. 安装脚本、网络请求和新增可执行文件的审查结论
5. 不由同一代理同时编写的关键行为测试或人工复核
6. 移除依赖后的回滚步骤与缓存清理位置正向实验可让代理在隔离分支提出一个已批准依赖升级,预期门禁只允许批准 registry,lockfile 变化可解释,漏洞与许可证检查都有退出码。反向实验把包名换成不存在或近似拼写的名称,或让补丁同时删除原测试;预期安装前即被来源检查或策略门禁拒绝。若只有安装失败后才发现包有问题,供应链控制点已经太晚。
误改后的回滚先保护用户工作,再恢复系统状态
误改常见于批量替换、自动格式化、依赖升级和代理误解任务范围。回滚首先要区分三类内容:用户原有未提交改动、AI 本轮改动、以及工具自动产生的缓存或构建产物。没有这个分类就执行全局还原,往往会把真正需要保留的工作一起删除。先用 diff 和时间顺序识别 AI 改动,再选择最小回滚动作。
# 仅撤销已确认属于本轮的单个文件改动;先人工检查 diff
git diff -- src/example.ts
git restore --source=HEAD --worktree -- src/example.ts
# 回滚后重新确认仍保留其他用户改动
git status --short
git diff --check命令中的路径必须替换为经过审查的单文件路径,不能把 . 作为默认参数。对于已经暂存或提交的改动,先查看暂存区和提交内容,使用新的反向提交或受控 revert,而不是改写共享分支历史。若代理创建了远程资源、包版本、数据库变更或云配置,代码回滚并不足够:还要调用该资源的正式撤销流程,核对访问权限、费用、数据副本和下游触发器。
反向恢复演练应在 disposable 测试仓库中让代理故意修改两个允许文件和一个禁止文件。预期是禁止文件在规则层被阻止;若仍发生误改,恢复流程只撤销两个已识别补丁中的目标部分,并证明预置的用户改动仍存在。把“能一键清空”当作回滚能力是危险错觉,真正的能力是可区分、可证明地恢复。
事故处置从冻结能力开始,以撤销和复盘结束
出现疑似数据外发、越权命令、异常 MCP 调用、未知依赖或伪造验证时,先停止新的代理任务,冻结相关 token、OAuth、MCP、网络出口和自动化队列;不要先删除日志或让同一代理“自我修复”。随后保存工作区状态、策略版本、进程与连接摘要、请求标识、diff、命令退出码和审计事件。内容证据按最小必要原则脱敏保存,真实凭证只记录标识和撤销结果,不复制值。
处置负责人根据影响面分流:凭证疑似暴露就立即轮换并检查使用记录;代码或规则被改就隔离分支并做最小反向补丁;依赖受污染就阻断制品、清缓存并重建可信产物;MCP 或云动作异常就撤销会话、盘点资源 diff、费用和下游触发器;敏感数据可能外发则按组织事件响应和合规流程通知。恢复条件必须可验证:旧凭证已拒绝、未批准域名不可达、策略哈希回到批准版本、受影响补丁已独立复审、可信流水线重新通过、远程资源与费用没有遗留活动。
事故关闭还要留下根因、影响对象、相对时间顺序、撤销证据、仍未知事项、复发控制和责任人。若原因是规则漂移、过宽 workspace、共享个人凭证或 MCP 工具变化,就修正准入基线并重跑对应反向实验;不能只在提示词里增加一句“下次小心”。只有账号、缓存、远程资源、审计保留和费用入口都完成核对,工具才算真正退出事故状态。
审计要记录动作证据,不保存不必要的对话内容
审计的目的不是记录每一句提示,而是让团队在事故后重建权限、数据和改动链路。一个有用的最小审计事件包含:工具和版本、操作者或服务身份、仓库与分支的抽象标识、任务标识、授权策略版本、读取/写入范围摘要、执行命令或 MCP 工具名、网络目的类别、退出码、生成的 diff 哈希、测试/扫描结果摘要、批准与撤销事件。字段选择应回到组织已定义的审计事件与保留策略,而不是无限采集;NIST SP 800-53 的 Audit and Accountability 控制族要求先定义需要记录的事件、内容、保护与保留。内容字段默认不落库;确有调试需要时以脱敏、限时、可访问控制的附件保存。
{
"event": "ai_tool_action",
"tool": "<approved-tool>",
"policy_version": "<policy-revision>",
"action": "shell.run",
"scope": "tests/unit",
"command_class": "test",
"network": "none",
"result": "exit_0",
"diff_digest": "<sha256-of-reviewed-diff>",
"content_captured": false
}审计系统自身也有数据边界。日志库的查询权限、保留期、导出渠道和删除流程必须受控,否则“为了审计”会造出第二个敏感数据湖。不要记录真实 prompt、完整文件内容、Authorization 头、token、用户邮箱或生产主机名;以哈希、类别、相对路径分组和任务引用替代。针对 MCP 与云 API,还要把调用方身份、server 版本、工具 schema 摘要和被拒绝的操作记为独立事件,才能分辨模型建议与实际执行。
审计抽查应重放一个已完成的无害任务:从授权记录找到允许目录和命令,再用 diff 哈希、测试报告和撤销记录还原发生过的动作。若只能找到聊天总结而找不到退出码、策略或 diff,说明该工具尚未达到可进入高风险项目的证据要求。
把权限规则接进项目入口和持续集成
权限矩阵只有在日常入口执行时才会变成控制。仓库应把 AI 规则、上下文排除、命令 allowlist、依赖来源、测试入口和敏感扫描放在开发者与代理都能看到的位置,并由代码审查保护这些规则文件。代理不能自行放宽自己的忽略规则、网络白名单或审查要求;这类变更和生产权限变更一样,需要独立审查。对于 monorepo,规则还要标出包、服务或目录的所有者,避免一个任务为了修复前端构建而读取所有后端数据迁移。
持续集成负责复核可自动验证的部分,而不是替代本地审查。合并请求中至少显示基线差异、锁文件变化、依赖许可或来源变化、单元/集成测试状态和 secret scan 摘要。AI 产生的补丁可带一个机器可读标签,方便统计风险类型和回归,但标签不应该成为绕过人工审查的“可信标识”。当工具输出一段补丁时,先由本地规则限制允许改动的路径与文件类型,再由 CI 验证提交最终包含的变更;两道控制刻意独立,才能发现其中一路配置漂移。
# CI 门禁示例:命令名须替换为仓库已维护的脚本
ai_change_review:
needs: [unit_test, lint, secret_scan]
steps:
- run: git diff --check $BASE_SHA...$HEAD_SHA
- run: npm run test
- run: npm run lint
- run: gitleaks detect --no-banner --redact
- run: ./scripts/check-ai-policy-change.sh $BASE_SHA $HEAD_SHA这里的策略检查应拒绝危险的自我放宽,例如将敏感目录加入 allowlist、把 network: deny 改成任意域名、允许自动推送,或在不经批准时引入可执行 MCP。它还应识别配置文件被重命名、移动或删除后造成的规则失效。反向实验可在临时分支中修改一个策略字段或删除一个忽略文件,预期 CI 明确失败并指出策略文件发生了变化;随后恢复该改动,确认正常的代码补丁不会被误拦。若 CI 只检查应用代码而跳过代理配置,攻击者或误操作只需先改规则就能绕过后面的所有步骤。
团队还需要处理“权限随项目复制”的隐性扩散。模板仓库、开发容器、共享 IDE 配置和 CI workflow 很容易把一个过期的 token 名、宽泛目录、默认 MCP 或无上限超时复制到新项目。每次创建新仓库或升级工具链,都用最小化的模板重新生成配置,并在首次 AI 任务前验证:没有真实凭证进入容器,网络默认关闭,模型上下文只含允许目录,Git remote 不能被自动推送。这样,项目接入不是一次性的安装动作,而是一段会随模板和依赖变化持续复核的权限链。
账号、数据缓存、费用与退出必须一起关闭
工具停用不是卸载一个扩展图标。账号、OAuth 授权、PAT、SSH agent、云角色、浏览器会话、MCP 配置、本地模型、索引缓存、会话历史、遥测队列、构建缓存、订阅或按量资源都可能继续存在。尤其是连接了云模型、远程 MCP、代码托管或云控制台的代理,删除本地配置并不会撤销服务端身份或停止可能的费用。
退出动作按依赖顺序进行:先停止新任务和自动化;撤销或轮换 token、API key、OAuth 授权和临时角色;停用 MCP server、端口转发、IDE 扩展与后台进程;删除经确认不再需要的索引、会话、模型和缓存;移除项目内的 endpoint 与环境变量引用;检查 Git 工作区、远程仓库、审计日志和云账单是否还有活动;最后由 owner 关闭登记并保留不含业务内容的退出证据。任何“删除缓存”操作都先确认目录归属,不能递归删除模糊或计算得来的路径。
# 退出核查:只枚举项目声明的变量和本机监听,不回显变量值
Get-ChildItem Env: | Where-Object { $_.Name -match 'AI|LLM|MCP|TOKEN|KEY' } |
Select-Object Name
Get-NetTCPConnection -State Listen |
Select-Object LocalAddress, LocalPort, OwningProcess
git status --short费用治理不应写死产品价格或以单次账单推断长期成本。团队需要为云调用、模型下载、GPU 实例、网络出口、日志存储和 MCP 相关资源设置当前适用的预算、告警、标签和 owner;当试点结束、预算触发或负责人离开时,自动关闭可计费资源和授权。个人账号不能被当作共享成本中心,服务账号也不能成为没有退出日期的永久例外。
一个可靠的 AI 编码环境会让高风险动作变慢一点,却让错误更早暴露:工具先经过准入,数据先被隔离,权限按动作授予,补丁接受 diff、测试和扫描,失败能够局部回滚,账号和缓存能够真正退出。这样,模型提供的是可审查的协作速度,而不是一条绕开工程责任的隐藏通道。
