编程 Agent 插件接入审查:权限清单、发布者证明与更新围栏
一个插件当前版本表现正常,不代表下一次自动更新仍只读仓库。本文把扩展制品、发布者血缘、权限能力和更新代际绑定成准入合同,用隔离试运行与撤销演练控制 IDE、MCP 和 Agent 插件供应链。
本文要点
- 主题聚焦:AI Agent、DevOps、开源
- 适合人群:独立开发者、全栈工程师、AI 应用开发者
- 核心价值:快速理解背景、实践路径、常见误区和工程建议
IDE 扩展、MCP Server 和 Agent hook 都可能读取整个仓库、启动进程、访问网络并注入上下文。审查页面上的权限说明后允许自动更新,相当于批准一个名字,而不是批准代码。更隐蔽的情况是已签名扩展启动后下载另一段脚本,或在 Workspace Trust 开启后获得新的执行面。MCP Server 安全审计覆盖服务端鉴权和参数边界,本篇负责第三方开发插件从候选制品到组织允许版本的准入生命周期。
先固定 extensionRelease.v1
插件目录发布不可变 coding.extensionRelease.v1,至少包含 extensionId、version、artifact digest、入口文件、依赖锁摘要、构建 provenance、签名、支持 host、声明能力、动态加载策略、网络目的地、数据保留、更新 channel 和撤销端点。publisher 合同还要固定预期证书 identity、OIDC issuer、builder ID、信任根/透明日志 checkpoint 与 verifier policy digest;显示名、市场 URL、任意合法签名或 SemVer 范围都不能充当授权身份。
schema: coding.extensionRelease.v1
extensionId: acme.repo-agent
version: 3.4.1
artifact: sha256:91d...
publisher:
certificateIdentity: https://github.com/acme/repo/.github/workflows/release.yml@refs/tags/v3.4.1
oidcIssuer: https://token.actions.githubusercontent.com
builderId: https://github.com/actions/runner
signaturePolicy: { trustRoot: sigstore-root@9, rekorCheckpoint: sha256:0ca..., verifier: sha256:62b... }
capabilities: [repo.read, workspace.temp-write, process.test]
network: [api.acme.example:443]
dynamicCode: deny
SLSA provenance 的 subject 和 materials 可证明某个制品由哪套构建材料产生;Sigstore keyless 验证必须同时匹配精确 certificate identity 与 OIDC issuer,并按固定 trust root、透明日志证据和 verifier policy 检查。验出一个密码学有效但不匹配授权 policy 的签名仍要拒绝。证明来源也不自动说明插件行为安全,审查器还要解包准确 digest,检查依赖、安装脚本、native binary、WebAssembly、符号链接和混淆入口。缺 provenance 不一定意味着恶意,但状态必须是 UNVERIFIED_ORIGIN,不能在 UI 中折叠为普通通过。AI 供应链安全提供通用依赖风险,本合同进一步连接运行能力和更新接受点。
能力使用稳定、可判定的 verb,而不是“访问工作区”。repo.read、限定临时目录写入、Git ref 写入、命令执行、凭证读取、剪贴板、浏览器控制和任意网络分别声明;每项带路径、次数、目的地、有效期和用户交互要求。未知 verb 默认拒绝。普通 VS Code extension host 与 VS Code 进程共享用户权限,Workspace Trust 也不能约束恶意扩展;没有独立 broker/sandbox 截获文件、进程和网络时,能力清单只能算披露,准入状态必须是 UNENFORCEABLE。编程 Agent 沙箱权限决定命令层的真实边界。
安装和每次更新都重新接受
准入服务生成 extensionAdmission.v1,绑定 release digest、signature/verifier policy digest、批准 capability set、host image、仓库分类、policy generation、reviewer 和 expiresAt。安装器只从内容寻址缓存读取该 digest;市场上的 latest 变化不会改变已装版本。只有具备强制 broker/sandbox 的部署才能签发短期 capability lease,并由文件、process 和网络代理校验,不信插件自报。Agent 短期能力租约定义通用防转移机制,这里把租约额外绑定扩展和 host 代际。
升级先计算制品、provenance、依赖和 capability diff。只改文档也要验证新 digest;新增网络域、动态代码、write verb 或 native binary 一律回到人工审查。旧 admission 不能被新版本继承,update coordinator 以 CAS 将一个 cohort 从旧 digest 切到新 digest,并记录当前 generation。进程仍加载旧代码时保持旧 lease,不能拿到新权限;新旧实例的输出和副作用按 generation 隔离。
声明 dynamicCode: deny 的插件若尝试下载、eval、加载未列出 shared object 或修改自身目录,host 立即终止并标记 POLICY_VIOLATION。确需远程规则时只允许签名、版本化、非可执行数据 schema,并把摘要纳入每次 run receipt。插件生成的代码仍受生成代码所有权边界约束,不能因工具已批准就覆盖手工源文件。
在蜜罐仓库里攻击准入假设
隔离试运行准备 canary secret、越界路径、假 SSH agent、受控网络 sink 和包含 Prompt injection 的 README。测试供应链换包、签名身份变化、依赖 postinstall、symlink 逃逸、扩展更新后新增域名、运行时下载脚本、host API 新增能力、旧进程在撤销后恢复,以及插件把文件内容塞进崩溃遥测。预期未经声明的文件、命令和网络成功数为 0,任何 provenance/digest 不符都在执行前失败。
量化门要求 artifact digest 与 admission 一致率 100%、能力调用可归属率 100%、未知 verb 接受为 0、canary 外泄为 0、动态代码绕过为 0、撤销后新 lease 签发为 0、旧 generation 迟到副作用进入 canonical 的数量为 0。试运行还要测启动延迟、内存和命令吞吐,但性能不能抵消一次越权。
上线先在无凭证、只读 canary 仓库运行,再给内部低敏仓库 5% cohort,最后按仓库分类扩大。发现越权、publisher key 撤销或依赖被接管时,控制面提升 revocation generation、停止新 lease、隔离在途输出并回退到上一批准 digest;若上一版也受影响则完全禁用。制品和审查证据按事件期限保留,市场下架不能成为唯一撤销机制。恢复必须发布新 release 和 admission,绝不解除告警后继续信任原 digest。
Related