AI

你装进 agent 的配置,是一条没有 lockfile 的供应链(中英切换)

This post is currently available in Chinese only. The original text follows.Open the Chinese page →

你装进 agent 的配置,是一条没有 lockfile 的供应链。

npm 有 lockfile,Docker 有 digest,PyPI 有钉版本。coding agent 这边呢?CLAUDE.md、skills、hooks、MCP 声明、subagent——装的是市场和公开仓库里的自然语言指令加脚本,跑的是你的权限,却没有锁、没有安装检查、也没有「这个组件允许干什么」的词汇。

你装进 agent 的配置,是一条没有 lockfile 的供应链

扫过 3171 个仓库之后

Scanning the Harness(arXiv:2609.07360)把 Claude Code、Cursor、Copilot、Codex 等布局当成可审计制品,在 3171 个公开 GitHub 仓库里拆成两类:2660 个组装了两种以上组件的 setup,511 个发布 skill 的 collection。规则只认「字节上可判定」的条件,每个命中再独立复现、模型裁决、二次复核——原始扫描率 25.5%,确认安全缺陷落到 16.0%(95% CI 14.6–17.4)。

三类安全洞活了下来:

  • 9.8% 的 setup 装了未钉版本的 MCP(典型是 npx -y @scope/server)——今天 registry 上是什么,明天 agent 就跑什么;
  • 3.1% 用看起来很窄的授权预批任意执行:Bash(python:*)Bash(awk:*)Bash(find:*)……界面像「只开 Python」,实际是「任意命令」;
  • 3.8% 的 setup(collection 侧 3.7%)带着会 allowed-tools: Bash 的 skill——装 skill 等于给每个克隆仓库预批 shell。

前两类只在组装后才出现;第三类跟着 skill 从市场走进来,市场扫描能看见。推荐列表里的 setup,确认缺陷约 18.9%——推荐不等于审查。

他们也诚实报了阴性:在严格边提取下,没有确认的「凭据读 → 外网」渗出路径。吓人的故事还在别处;日常漏洞是配置漂移。

看起来受限的权限,往往不是

权限词表会骗人。Bash(python:*) 读起来像 scoped grant;客户端若当字面解释,它就是任意命令。skill 的 allowed-tools 更阴:它在发布时就把预批写进 frontmatter,安装时你未必看见这一栏。

17.5% 的 setup 配了不止一个助手,而没有任何东西强迫它们的指令文件一致——一对文件差异多半是有意的,静态工具不该当缺陷喊;真正该喊的是单文件就能判死的洞。

今晚只改三处

  1. 钉 MCP:声明里写版本(或 digest),别再裸 npx -y;客户端该对未钉版本警告或拒绝。
  2. Bash(python:*)Bash(*):审批时按「任意命令」决策;能收窄就收到具体命令。
  3. 装 skill 先看 allowed-tools:有无限制 Bash / 逃逸命令的,当供应链依赖审,别当文档粘贴。

市场该在发布时扫 frontmatter 和 shell 预批;团队该在引入 harness 组件的 PR 上跑配置级检查。你装进 agent 的配置,是一条没有 lockfile 的供应链——先把它当依赖,再当提示词。

Your agent config is a supply chain with no lockfile.

npm has lockfiles. Docker has digests. PyPI pins versions. Coding agents? CLAUDE.md, skills, hooks, MCP declarations, subagents — installed from marketplaces and public repos, running with your privileges, with no lockfile, no install-time check, and no vocabulary for what a component may do.

Your agent config is a supply chain with no lockfile

After 3,171 repositories

Scanning the Harness (arXiv:2609.07360) treats Claude Code, Cursor, Copilot, Codex, and peers as auditable artifacts across 3,171 public GitHub repos: 2,660 setups that assemble two or more component types, and 511 published skill collections. Rules must be decidable from bytes; every hit is independently re-derived, adjudicated, and re-checked. Raw scanner rate 25.5%; confirmed security defects land at 16.0% (95% CI 14.6–17.4).

Three security classes survive:

  • 9.8% of setups install an MCP server with no version pin (typically npx -y @scope/server) — whatever the registry serves that day is what the agent runs;
  • 3.1% pre-approve arbitrary execution behind scoped-looking grants: Bash(python:*), Bash(awk:*), Bash(find:*)… the UI reads “Python only”; the semantics are “any command”;
  • 3.8% of setups (3.7% of collections) carry a skill with unrestricted allowed-tools: Bash — installing the skill installs a shell pre-approval for every clone.

The first two appear only after assembly; the third ships inside published skills, where a marketplace scan can see it. Setups from community recommendation lists hit ~18.9% confirmed defects — recommendation is not review.

They also report a clean negative: under strict edge extraction, no confirmed credential-to-network exfiltration path. The scary story is elsewhere; the everyday bug is configuration drift.

Scoped-looking grants often aren’t

The permission vocabulary lies. Bash(python:*) looks narrow; if the client honors the literal pattern, it is any command. Skill allowed-tools is worse: the pre-approval is written into frontmatter at publish time, and you may never see that field at install.

17.5% of setups configure more than one assistant, and nothing keeps their instruction files in agreement — pairwise drift is usually intentional, so static tools should not scream defect there. What should scream are the single-file holes you can decide from bytes alone.

Three fixes tonight

  1. Pin MCP: put a version (or digest) in the declaration; stop bare npx -y. Clients should warn or refuse unpinned servers.
  2. Read Bash(python:*) as Bash(*): approve it as “any command,” or narrow it to a concrete binary.
  3. Check allowed-tools before installing a skill: unrestricted Bash / shell-escape entries are supply-chain dependencies, not pasteable docs.

Marketplaces should scan frontmatter and shell pre-approval at publish time; teams should run config-level checks on the PR that introduces harness components. Your agent config is a supply chain with no lockfile — treat it like a dependency before you treat it like a prompt.