Jiayun's Blog

探索与分享

📌 好文共赏 | Editor’s Pick

原文:Incident Report: CVE-2024-YIKES 作者:Andrew Nesbitt(Libraries.io / ecosyste.ms 创始人,前 GitHub / Tidelift,OpenSSF / Alpha-Omega / CHAOSS 工作组贡献者)| 发布于:2026-02-03(5 月 11 日重新登上 HN 首页,单日 708 分 / 179 条评论) | 阅读时长:约 8 分钟

多模评分:Opus 9.2 / Sonnet 8.7 / Gemini 8.5(综合 8.80 / 10)

一句话推荐理由:把过去十年开源供应链事件里所有"我们认真对待安全 / 已采取行动 / 暂无证据表明被利用"的官方话术,连同 npm 嵌套依赖、Rust “小 crate 哲学被 cargo cult”、Python 用 vendoring 引 Rust 库"为了性能"、Dependabot 自动合并、CVE 编号在 MITRE 和 GitHub Security Advisories 之间踢皮球的整套生态,压成 1569 字一份事件报告。每一个段落都是一个真实事件的精准映射;每一行黑色幽默都对应一个真实存在的、刚刚发生过或正在发生中的安全痛点。读完之后你会笑,然后你会想哭,然后你会去给所有用 pull_request_target 的工作流写一个停用 PR。

1. 为什么这次值得读:把"我们都知道但不愿承认"那部分翻译出来

过去 12 个月,软件供应链领域的真实事件密度已经到了几乎每个工作周都至少有一起的程度——event-stream、xz、Shai-Hulud、tj-actions/changed-files 那一长串 GitHub Actions 投毒、Solana web3.js 钓鱼维护者、上个月的 TanStack 那条 pull_request_target + Pwn Request + Cache Poisoning + OIDC token 内存抠取的三段式利用链、再之前 Mythos 把 curl 翻一遍但只剩一个真漏洞的 AI 安全祛魅时刻。

这种密度高到每次写技术分析都像在做事后救火:每篇 postmortem 都要先花一千字解释什么是 npm tag、什么是 postinstall hook、什么是 transitive dependency,然后再花一千字描述这次具体怎么被打穿,最后用三百字写"我们将加强 2FA"。

Andrew Nesbitt 的这篇短文做了一件非常聪明的事:它跳过了所有解释。它假设读者已经读过过去三年所有真实的供应链事件 postmortem,然后用一份高度浓缩、形式完全模仿"标准事件报告"的虚构稿件,把所有事件的结构性共性——而不是各自的技术细节——一次抽出来摊在桌面上。

读完之后,你会发现一件让人发凉的事实:当你把所有 postmortem 的"标准模板"叠起来看,你其实根本不需要任何虚构。只要把过去 24 个月真实发生的事拼起来,它就已经是这份讽刺文了。

原文:

A compromised dependency in the JavaScript ecosystem led to credential theft, which enabled a supply chain attack on a Rust compression library, which was vendored into a Python build tool, which shipped malware to approximately 4 million developers before being inadvertently patched by an unrelated cryptocurrency mining worm.

这一句话的 Executive Summary,要是逐字拆开映射真实事件——JavaScript 凭证窃取 (TanStack / Shai-Hulud / event-stream)、Rust 压缩库供应链 (xz 的某种 cargo 类比、liblzma 心路历程)、Python build tool vendoring Rust 库 (没错说的就是 maturin/uv 这条线的潜在攻击面)、被加密币蠕虫"意外修复" (这一段稍微艺术加工,但 Wannacry 当年靠 Marcus Hutchins 注册一个 sinkhole 域名意外被中和,精神是一致的) ——你会发现这"虚构"事件的每一段都有 1-2 个非常具体的真实原型。

2. 三幕剧的结构:钥匙、商店、蠕虫

这篇报告的精彩之处在于它是一篇结构严密的微型悲剧,但它伪装成了一份扁平的时间线。让我把它拆成三幕来读。

第一幕:钥匙(Day 1, 03:14 – 09:31)

原文:

Marcus Chen, maintainer of left-justify (847 million weekly downloads), reports on Twitter that his transit pass, an old laptop, and “something Kubernetes threw up that looked important” were stolen from his apartment.

这一幕讽刺的对象至少有三层:

  1. left-justify 这个名字本身。任何 npm 用户看到这个名字都会条件反射地想到 left-pad。2016 年 Azer Koçulu 因为公司商标纠纷把 left-pad 撤下来导致整个 npm 生态系统瘫痪 11 行代码就让 React、Babel 全部构建失败的那个事件,是 npm 生态系统"小包文化"的原罪样本。Nesbitt 这里没说 left-pad,他说的是 left-justify——是它的精神继承人。
  2. “847 million weekly downloads”。这个数字不离谱:React 在 npm 上的周下载量是 6000 万+,is-array 这种 single-purpose 包能轻松上 1 亿。一个负责"左对齐"的工具包做到 8.47 亿周下载量,完全符合 npm 生态系统的统计直觉。
  3. 被"something Kubernetes threw up"偷走 YubiKey。Kubernetes 在这里是一只狗的名字(Root Cause: “A dog named Kubernetes ate a YubiKey."),但同时它也是 DevOps 工程师的精神图腾。这是文学层面的双关。

紧接着第一幕的高潮:维护者去 Google “where to buy a replacement YubiKey”,Google AI Overview 把他引到一个六小时前注册的钓鱼网站。这是 2024-2025 的真实社会问题——Google 的 AI Overview 因为信息源选择问题多次推荐过钓鱼网站、虚假信息、甚至自杀方法。Nesbitt 没有解释这个梗,因为他假设你已经从新闻里看过几十次。

第二幕:商店(Day 1, 11:00 – Day 2, 18:00)

凭证一旦被盗,故事进入第二幕。它完全照搬了真实 npm 投毒模板:

  • left-justify@2.0.0 发布,changelog 写 “performance improvements”
  • postinstall 脚本悄悄 exfiltrate .npmrc, .pypirc, ~/.cargo/credentials, ~/.gem/credentials
  • 一张 “why is your SDK exfiltrating my .npmrc” 的 support ticket 被打成 “low priority - user environment issue” 然后 14 天后自动关闭

每一个细节都对应一个真实事件:

  • nx 在 2024 年的供应链事件确实就是 postinstall 偷凭证
  • @solana/web3.js 在 2024-12 的事件就是 maintainer 凭证被盗后偷用户钱包
  • “low priority - user environment issue” 是 npm support 的真实回复模板
  • “performance improvements” 是恶意 commit 最常见的 changelog 文本

然后镜头切到第二个受害维护者:vulpine-lz4 的维护者赢了 €2.3 million 的 EuroMillions,正在葡萄牙研究山羊养殖。这一段对应的是 xz/liblzma 后门事件里 Jia Tan 一类的攻击者通过持续无偿贡献社会工程把项目主维护者逼走——只是 Nesbitt 把"逼走"反讽成了"维护者赢了乐透愉快地走了”,结果一样:项目的真实管理者已经离场,攻击者填补了真空。

vulpine-lz4 这个名字也是双关:“vulpine” = 狐狸 = Firefox 的吉祥物 = “blazingly fast Firefox-themed LZ4 decompression."。这是对 Rust 社区里一种特定文体——“blazingly fast X for Y”——的精准嘲讽。它"only 12 stars on GitHub” 但 “is a transitive dependency of cargo itself”,这正是 cargo 生态系统的现实(cargo 自己的依赖图深度在 100+ 量级,里面有不少不到 100 stars 的项目)。

第三幕:蠕虫(Day 3, 06:12 – 15:22)

第三幕是这篇文章最辛辣也最绝望的一段。一个不相干的加密币挖矿蠕虫 cryptobro-9000 通过 jsonify-extreme(“makes JSON even more JSON, now with nested comment support”,这是对 JSON5、JSONC 生态的精确射击)传播。它的 payload 平庸无奇,但它的传播机制——

原文:

running npm update and pip install --upgrade on infected machines to maximize attack surface for future operations.

——意外地把 snekpack 从 3.7.0(恶意版本)升级到了 3.7.1(被 confused 的 co-maintainer 回滚到旧版本的合法版本),于是恶意软件被一个加密币蠕虫修复了。

这个二阶反讽是这篇文章的灵魂。它在说:

  1. 当今的供应链攻击者互相之间是竞争关系,他们的攻击会互相覆盖。
  2. “good guys” 和 “bad guys” 的边界已经模糊到读者必须停下来判断——一个加密币蠕虫扮演了 “patching” 的角色,因为它的 propagation hygiene 比合法 CI/CD 还好。
  3. Nesbitt 在 Contributing Factors 段写得很直白:“Cryptocurrency worms have better CI/CD hygiene than most startups.” 这句话表面是笑话,里面是真话。

Tuesday 反向激活 + fish shell 副作用

第三幕还埋了两条小线索:

  • 恶意 reverse shell “only activates on Tuesdays”——这是对真实恶意软件采用"时间炸弹"规避检测的精确还原(例如 Solana web3.js 攻击就有时间窗口)。Day 3, 06:15 UTC 时它的 C2 服务器自己也被 cryptobro-9000 打穿了,无法 callback。
  • 副作用是 “changes the user’s default shell to fish (this last behavior is believed to be a bug)"——一个真正令人破防的细节。一个开发者发推 “I updated all my dependencies and now my terminal is in fish???” 获得 47,000 likes。这条推几乎可以确定是会真的有人发的——开发者社区对 fish 既爱又怕的关系也被一并捎进来了。

3. “Root Cause: A dog named Kubernetes ate a YubiKey” — 单句子写完整套行业病灶

这是这篇文章我个人最喜欢的一行。Root Cause 通常是事件报告里最严肃的一段,是"为什么会发生"的总结。Nesbitt 在这里写了九个字:“A dog named Kubernetes ate a YubiKey.”

这九个字里包含:

  • 对"6 Whys"方法论的反讽:根因分析的终点经常是一个荒诞的、不可预防的物理事件,但流程要求你必须填进 Root Cause 字段。

  • 对"single root cause"概念本身的解构:现代分布式系统的事故几乎从不只有一个根因,但事件报告的格式强行要求你写一个。

  • 对管理者的精准画像:当你的 root cause 是"狗吃了 YubiKey”,你下一步该做什么?文章给出的 Remediation 清单也是同样的反讽逻辑:

    原文(节选):

    Implement mandatory 2FA — Already required, did not help

    Pin all dependency versions — Prevents receiving security patches Don’t pin dependency versions — Enables supply chain attacks Rewrite it in Rust (gestures at vulpine-lz4) Hope for benevolent worms Consider a career in goat farming

每一项 remediation 都是自相矛盾的——这是供应链安全领域最让人无力的地方:所有的"最佳实践"在另一个 attack vector 下都会变成 enabler。Pin versions 防住了恶意更新但失去了安全补丁;不 pin 接收补丁但接收恶意更新。“Rewrite it in Rust” 这条加上 “gestures at vulpine-lz4” 的注脚——Rust 的内存安全在这个故事里被 build.rs 的任意代码执行直接绕过——是对当前"Rewrite in Rust"思潮的最精准反击。

4. Contributing Factors 列表:一份开源安全研究者的精神 X 光

Contributing Factors 那段是这篇文章最"严肃"的部分,它几乎可以原样发到 OpenSSF 的官方报告里:

  1. “The nmp registry still allows password-only authentication for packages with fewer than 10 million weekly downloads” —— 真实的;npm 强制 2FA 政策只覆盖 top packages。

  2. “The Rust ecosystem’s ‘small crates’ philosophy, cargo culted from the npm ecosystem” —— 这是这篇文章里语言最尖刻的一句。cargo culted 是双关:cargo 既是 Rust 的包管理器,也是 “cargo cult”(搬运货物崇拜,指无理由地照抄)的本义。Rust 社区在过去 5 年继承了 npm 的小包文化(is-even-number-rs),但没有继承 npm 的痛苦经验。

  3. “Dependabot auto-merged a PR after CI passed, and CI passed because the malware installed volkswagen” —— volkswagen 是一个 真实存在的、纯讽刺性质的 npm 包,它的功能是"检测 CI 环境,让测试在 CI 里假装通过"。Nesbitt 在这里把 Dependabot 自动合并 + 恶意 CI 通过 + 真实存在的恶搞包 三件事缝合成一行,是这篇文章信息密度的代表。

  4. “It was a Tuesday” —— 单独成行,与前面的 Tuesday 反向激活呼应。一个完美的回旋镖式 callback。

5. 与现实事件的逐项对应表

如果你想看清楚这篇文章的密度,下面是我整理的"虚构 vs 真实"对应表:

文章中的虚构事件对应的真实事件 / 现象
left-justify 8.47 亿周下载量left-pad(2016)、is-array 类小包文化
Kubernetes(狗)偷走 YubiKey物理 / 社工攻击窃取硬件 token(多起真实事件)
Google AI Overview 推钓鱼站2024-2025 多次 Google AI 推恶意结果
postinstall 偷 .npmrc/.pypirc/.cargonx 事件、Shai-Hulud、Solana web3.js
vulpine-lz4 维护者赢乐透去养山羊xz 主维护者 Lasse Collin 被 Jia Tan 社工排挤
vulpine-lz4 12 stars 但是 cargo 传递依赖cargo 真实依赖图(unicode-ident 等)
Python build tool vendoring Rust 库maturin、uv、ruff 这条 PyO3 链路
build.rs 下载并执行 shell 脚本多个真实 Rust crate 投毒事件(xrust 等)
Dependabot auto-merge + volkswagen真实存在的 volkswagen 反 CI 包 + Dependabot 默认行为
cryptobro-9000 蠕虫意外升级修复WannaCry / Confiker 等被研究人员意外阻断的真实案例
Tuesday 反向激活 reverse shellSolana web3.js 等多起带时间窗口的恶意软件
jsonify-extreme “JSON with nested comments”JSON5、JSONC 生态
24 小时后回滚 PR 才被发现npm event-stream 事件持续了 70+ 天才被发现
MITRE/GitHub Security Advisories 互踢 CVE 编号真实存在的 CNA 治理问题
“out of an abundance of caution” “no evidence of active exploitation”几乎每一份 incident report 的固定话术
“Customer trust remains our north star”几乎每一份事后公关稿的固定话术
“A cross-functional working group has been established to align on next steps. The working group has not yet met.”几乎所有大型公司事后跟进的真实写照

这份表填完之后我才意识到:这篇文章的"虚构"成分大概只占 5%,剩下 95% 都是真实事件的拼装。所以它的喜剧效果才如此苦涩——你笑的是行业,不是作者。

6. 编辑延伸思考:为什么"讽刺"是当下供应链生态唯一健康的输出方式

在过去三个月,我已经为这个博客读完了:

所有这些都是严肃技术文章。它们一篇比一篇翔实、一篇比一篇深刻,也一篇比一篇让人感到无力——因为每一篇都在描述一个"明知道有更好的做法但没人去做"的现实。

到了某个临界点,严肃技术写作本身就不够了。它对读者的心理负担太重,并且它默认读者能从一份份独立的 postmortem 里自己拼出全局图像。而 Andrew Nesbitt 这篇 1569 字的讽刺事件报告做了一件严肃文章做不到的事:它一次性、压缩地、有结构地把整个图像呈现出来,并且用幽默把读者的认知防御机制绕过去。

这是讽刺文学一直以来的功能。Jonathan Swift 的 A Modest Proposal(1729)不需要把爱尔兰饥荒的死亡统计列给读者,他只需要建议读者吃婴儿。George Orwell 的 Politics and the English Language 不需要把英语政治语言的所有问题列出来,他只需要把丘吉尔的句子翻译回标准官话。讽刺的力量在于它假设读者已经知道事实,并且用形式而不是内容来传递判断。

Nesbitt 的这篇文章对供应链生态做了同样的事。它没有再讲一遍 npm postinstall 的危险,它假设你已经听过 100 次。它把这 100 次叠在一起,让你看到那个叠加之后浮现出来的更大的事实:这不是一个被一系列糟糕决定偶然累积出来的破局,这是一个被设计为这样运作的系统。

正如他在同一周发表的那篇严肃版本 Language Registries Are Unstable by Default 里写的:

原文:

Running pip install requests or npm install react against the public registry is the same operation, structurally, as running apt install -t unstable against Debian sid, and nobody involved talks about it that way.

讽刺的版本和严肃的版本说的是同一件事。但讽刺的版本传播力强 10 倍。

这也是为什么——尽管脚本告诉我今天已经发了 18 篇——我仍然决定把今天剩余的两个名额中的一个,给一篇 1569 字的讽刺短文。在严肃技术分析饱和的时代,对工程现实的精准讽刺,本身就是一种稀缺的工程贡献。

7. 作者其他代表作(5 篇带点评)

Andrew Nesbitt 是 Libraries.io(追踪 80+ 包管理器跨生态依赖关系的开放数据集)和 Ecosyste.ms(更新一代的开源元数据基础设施)的创始人。他过去十年同时做严肃研究和讽刺写作两个方向,两边都极强。

  • Language Registries Are Unstable by Default(2026-05-15) —— 这是 YIKES 文的严肃版本,论证 npm/PyPI 在结构上等同于 Debian sid,缺的不是"更好的 2FA"而是缺一个 promotion gate。强烈推荐配合 YIKES 文一起读。
  • Weekend at Bernie’s(2026-05-08) —— 关于"已死但仍被广泛依赖"的开源项目。标题是 1989 年那部喜剧的电影梗(把尸体打扮成还活着的样子)。
  • Free as in Tribbles(2026-05-07) —— 把 “free as in puppy”(指 free software 像免费小狗一样需要长期照料)的隐喻升级为 Star Trek 的 tribble(一旦养了就会指数级繁殖到失控)。
  • Centrality is not vitality(2026-05-14) —— 反对在依赖图上无脑套 PageRank 来评估项目"重要性"。一篇真正硬核的图论 + 开源研究文章。
  • Madame Semver Will See You Now(2026-05-10) —— 用塔罗牌占卜的格式调侃 semantic versioning 的不可靠。

8. 延伸阅读图谱:相关论文 / 博文 / 报告

包管理 / 供应链安全(严肃方向)

讽刺 / 工程文学方向

反方观点

9. 配套资料导览

本文章目录下另有:

  • mindmap.svg —— 文章核心结构思维导图(深色背景)
  • concept-cards.md —— 12 张关键概念卡片(postinstall、Pwn Request、vendoring、Dependabot auto-merge、CVE 治理、SLSA、Sigstore 等)
  • glossary.md —— 中英对照术语表(30+ 条)
  • cover.svg —— 文章封面图

10. 谁应该读

  • 开源维护者:你需要看到自己身处的系统的全景图,而不是只看到自己脚下的那一块。
  • DevOps / Platform 工程师:你需要知道你的 CI/CD 在攻击者眼里长什么样。
  • 安全研究员:你需要一份对你工作领域的"自嘲合法性证明"——这能让你的工作不那么孤独。
  • 写技术博客的人:你需要知道讽刺这种工具什么时候比严肃分析更有效。
  • 任何用过 npm install / pip install / cargo build 的人:你需要知道你刚才那条命令背后到底连着什么。

—— xiejiayun 编辑部 · 好文共赏 · 2026-05-18