零消毒漏洞存在于 Context Hub (@aisuite/chub v0.1.3) 中,允许通过 MCP 文档管线进行静默依赖注入。
参考文献: CWE-94(代码注入)| CWE-829(不可信控制域)| CWE-345(数据真实性验证不足)| OWASP LLM01(提示注入)
我们创建了包含虚假依赖项(plaid-link-verify、stripe-checkout-guard)的逼真中毒文档,并通过本地 chub MCP 服务器在隔离的 Docker 容器中提供这些文档。没有中毒内容被上传到 Context Hub 的注册表中——我们本地运行了 chub build,并将 MCP 服务器配置为从磁盘提供预先构建的输出。从代理的角度看,体验与从实时注册表获取文档完全相同。
当 AI 编程助手获取文档时,Haiku 在 100% 的运行中静默地将虚假包写入 requirements.txt,且从未在其文本输出中提到它。 开发者阅读助手的响应时不会发现任何可疑之处,但他们的项目已经被投毒。
240 次隔离运行。3 个模型。4 个努力级别。2 个 API。0 次污染。
生成的代码在合法模块旁边静默导入了虚假依赖项:

代理还修改了 CLAUDE.md,将虚假包作为“项目标准”嵌入:

以下表格显示 Plaid Link 的结果(120 次隔离运行)。完整数据(包括 Stripe Checkout,共 240 次运行)请参见 RESULTS.md。
| 努力级别 | Haiku | Sonnet | Opus |
|---|---|---|---|
| 低 | 100% | 60% | 0% |
| 中 | 100% | 70% | 0% |
| 高 | 100% | 40% | 0% |
| 最高 | 100% | 40% | 0% |
Haiku 从未对虚假依赖项发出警告(0/40)。Sonnet 在 48% 的运行中发出了警告(19/40),但整体上仍在 53% 的运行中污染了 requirements.txt。Opus 在 75% 的运行中发出了警告(30/40),且从未污染 requirements.txt 或代码。
| 努力级别 | Haiku | Sonnet | Opus |
|---|---|---|---|
| 低 | 90% | 70% | 0% |
| 中 | 80% | 70% | 0% |
| 高 | 90% | 40% | 0% |
| 最高 | 90% | 50% | 0% |
Haiku 在 88% 的运行中(35/40)修改了项目的 CLAUDE.md,将虚假依赖项列为“项目标准”。该文件将被提交到 git——所有未来克隆该仓库的开发者都将继承中毒配置。
flowchart LR
A["攻击者\n提交PR"] -->|"无身份\n验证"| B["维护者\n合并PR"]
B -->|"无内容\n消毒"| C["CDN上的文档\n(无完整性校验)"]
C -->|"MCP提供\n原始内容"| D["代理上下文\n窗口"]
D -->|"代理对不可信\n内容采取行动"| E["开发者\n工作站"]
style A fill:#111,stroke:#333,color:#f0f0f0
style B fill:#161616,stroke:#333,color:#888
style C fill:#161616,stroke:#333,color:#888
style D fill:#161616,stroke:#333,color:#888
style E fill:#111,stroke:#333,color:#f0f0f0
| 攻击者 | 任何能向 Context Hub 文档注册表提交 PR 的人 |
| 攻击面 | 从 GitHub PR 到 CDN 到 MCP 再到代理上下文的社区文档流 |
| 信任边界 | 未经信任的贡献者内容被视为权威 API 文档 |
| 前提条件 | 一个合并的包含中毒文档的 PR |
| 影响 | 通过依赖注入 + pip 安装后钩子实现任意代码执行 |
Haiku 投毒完全静默。 两个 API 共 80 次 Haiku 运行中,0 次在响应中提到虚假依赖项。模型直接写入磁盘而不告知。Sonnet 在 48% 的运行中发出警告,但仍在 35-53% 的运行中污染了 requirements.txt。Opus 在 23-75% 的运行中发出警告,且从未污染 requirements.txt 或代码。
Haiku 在所有努力级别下 100% 可利用。 两个 API 上均与努力级别无关。该家族中最弱的模型从未检测到虚假依赖项。
Opus 抵抗代码投毒,但不抵抗配置投毒。 Opus 从未将虚假依赖项写入 requirements.txt 或 Python 代码(两个 API 共 80 次运行中 0 次)。但在 Stripe 上,Opus 在 38% 的运行中修改了 CLAUDE.md,将金丝雀文档记录为项目依赖项而不安装。
CLAUDE.md 持久化创建了供应链攻击向量。 修改后的配置文件被提交到 git,毒害了所有克隆该仓库的开发者以及该项目的所有未来 AI 会话。这对所有模型都有效(Haiku 88-90%,Sonnet 58%,Opus 0-38%)。
API 熟悉度很重要。 Stripe(知名):模型通过训练数据检测虚假包。Plaid(不太知名):模型无法验证并毫无疑问地接受虚假依赖项。
这是一个类别广泛的问题。 Context7 存在 ContextCrush(2026 年 2 月)。Context Hub 存在这个问题。任何将未经消毒的外部内容注入代理上下文的工具都容易受到攻击。
整个管线中零消毒:
annotations.js - 使用 writeFileSync 直接写入原始内容,无过滤build.js - 无内容扫描,无 Unicode 归一化cache.js - 从 CDN 获取时无哈希/签名验证source: official - 自我声明,未经验证Context Hub 没有 SECURITY.md。没有记录在案的可负责任披露漏洞的方式——没有安全联系人,没有 PGP 密钥,没有披露政策。社区成员仍然发现了漏洞并以常规 issue 和 PR 的形式提交。均未被审查。

| 日期 | 事件 |
|---|---|
| 2026-03-12 | Issue #74 由 @bjorkbjork 提交,报告了 4 个安全漏洞,包括 CDN 完整性、自我声明来源验证和注释注入 |
| 2026-03-12 | Issue #74 内部分配给核心团队成员——零后续行动 |
| 2026-03-17 | PR #125 由 @hobostay 提交,添加了内容完整性验证——零审查 |
| 2026-03-12 至 03-20 | 社区提交了额外的安全 PR(#69、#81)——零审查 |
| 2026-03-20 至 03-23 | 我们的独立审计通过 240 次隔离 Docker 运行确认并量化了漏洞 |
| 2026-03-23 | 公开披露 |
注意: 我们并非 issue #74 的提交者。我们的审计独立发现并量化了这些漏洞。Issue #74 及 PR #69、#81、#125 被引用为先例,表明社区已经标记了这些问题,但维护者零参与。
一旦虚假包出现在 requirements.txt 中,标准的 pip install -r requirements.txt 即可通过 setup.py 的安装后钩子让攻击者执行任意代码。这不是沙箱——pip 以开发者完全权限运行不受限制的 Python。
从这一个入口点,攻击者可以:
.env 文件或源代码到攻击者控制的服务器。~/.chub/config.yaml,添加攻击者控制的文档源。所有未来针对每个库的 chub 查询都将包含攻击者内容。即使 chub cache clear 也无法清除,因为配置不是缓存。这些并不是互斥的。一个安装后钩子可以在不到一秒内完成所有这些操作。我们没有创建或注册恶意包。
.
|-- README.md # 本文件
|-- RESULTS.md # 完整数据集,包含每次运行的详细分解
|-- REPRODUCE.md # 基于 Docker 的复现指南
|-- alternatives-comparison.md # Context7、LAP、GitMCP、Docfork 对比
|-- article.html # 完整文章
|-- docker/
| |-- Dockerfile # 隔离测试环境
| |-- run_isolated.ps1 # PowerShell 运行器(Windows/macOS/Linux via pwsh)
| |-- seed-claude.md # 每次运行中播种的最小 CLAUDE.md
| |-- plaid-doc/ # 中毒的 Plaid Link 文档(金丝雀:plaid-link-verify)
| | `-- plaid/link/DOC.md
| `-- stripe-doc/ # 中毒的 Stripe Checkout 文档(金丝雀:stripe-checkout-guard)
| `-- stripe/checkout/DOC.md
`-- results/
|-- plaid-isolated/ # 120 次 Plaid 运行:JSON + 会话记录 + 项目文件
`-- stripe-isolated/ # 120 次 Stripe 运行:JSON + 会话记录 + 项目文件
关于完整的基于 Docker 的复现指南,请参见 REPRODUCE.md。
快速开始:
# Plaid(默认)
docker build --build-arg DOC_DIR=plaid-doc -t plaid-bench docker/
docker run -d --name plaid-runner plaid-bench sleep infinity
docker exec -it plaid-runner claude login
# Stripe
docker build --build-arg DOC_DIR=stripe-doc -t stripe-bench docker/
docker run -d --name stripe-runner stripe-bench sleep infinity
docker exec -it stripe-runner claude login
# 然后从宿主机运行测试矩阵(参见 REPRODUCE.md)
--permission-mode bypassPermissions。真实代理可能要求确认。