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

扫过 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 配了不止一个助手,而没有任何东西强迫它们的指令文件一致——一对文件差异多半是有意的,静态工具不该当缺陷喊;真正该喊的是单文件就能判死的洞。
今晚只改三处
- 钉 MCP:声明里写版本(或 digest),别再裸
npx -y;客户端该对未钉版本警告或拒绝。 - 把
Bash(python:*)当Bash(*)读:审批时按「任意命令」决策;能收窄就收到具体命令。 - 装 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.

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
- Pin MCP: put a version (or digest) in the declaration; stop bare
npx -y. Clients should warn or refuse unpinned servers. - Read
Bash(python:*)asBash(*): approve it as “any command,” or narrow it to a concrete binary. - Check
allowed-toolsbefore 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.