上个月TanStack Router被供应链蠕虫攻击的事,做前端的应该都听说了。攻击者通过GitHub Actions缓存投毒,拿到OIDC token后直接在官方CI里发布了带后门的npm包。更恶心的是,因为发布流程走的是合法的GitHub Actions runner,npm provenance验证也是通过的------你用npm audit根本查不出问题。
那几天我一直在想一个事:我本地装了多少个被波及的包?node_modules里藏了多少颗定时炸弹?
传统工具帮不上忙。npm ls只能查项目级依赖,查不了全局缓存和pnpm store。EDR监控的是运行中进程,管不了躺在磁盘上的恶意包。SBOM记录的是构建产物,跟开发机上的实际状态是两回事。
Bumblebee是什么
5月22日,Perplexity开源了一个叫Bumblebee的工具。一句话概括:它扫描开发机上所有包管理器的本地文件,告诉你哪些包命中了已知威胁。
跟别的安全工具最大的区别------它什么都不执行。不调npm,不跑pip,不触发任何postinstall脚本。纯读文件。
这个设计不是偶然的。TanStack蠕虫(代号Mini Shai-Hulud)的传播方式就是利用npm lifecycle hooks------你只要npm install一个被感染的包,恶意payload就会在postinstall阶段执行,偷走你的GitHub token、npm token、AWS凭证,然后用这些凭证去感染你有发布权限的其他包。一个扫描工具如果还要调npm来检查,等于自己踩进雷区。
Bumblebee用Go 1.25写的,零第三方依赖,只用标准库。编译出来一个10MB的静态二进制文件,macOS和Linux都能跑。
装起来
bash
# 需要Go 1.25+
go install github.com/perplexityai/bumblebee/cmd/bumblebee@latest
# 验证
bumblebee --version
没装Go的话,去GitHub Releases页面直接下编译好的二进制:
bash
# macOS ARM64
curl -L https://github.com/perplexityai/bumblebee/releases/latest/download/bumblebee-darwin-arm64 -o /usr/local/bin/bumblebee
chmod +x /usr/local/bin/bumblebee
三种扫描模式
Bumblebee有三个profile,对应不同场景。
baseline:日常巡检
扫描全局包路径、IDE插件、浏览器扩展、MCP配置。跑一次5到15秒。
bash
bumblebee scan --profile baseline > ~/security/daily-inventory.ndjson
输出是NDJSON格式,每行一个JSON对象,长这样:
json
{"hostname":"macbook-pro","os":"darwin","arch":"arm64","ecosystem":"npm","package":"axios","version":"1.14.1","source":"/Users/dev/.pnpm-store/v3/files/axios/1.14.1/package.json","confidence":"high"}
可以直接丢进Splunk、Elastic、Datadog,不用写解析器。
project:扫项目目录
扫你指定的代码目录,遍历里面所有lockfile和node_modules。
bash
bumblebee scan --profile project \
--root "$HOME/code" \
--root "$HOME/work" > project-inventory.ndjson
跑一次30到60秒,取决于你有多少项目。
deep:应急响应
出了安全事件,要全盘扫的时候用。必须显式指定--root,防止误操作。
bash
bumblebee scan --profile deep \
--root "$HOME" \
--exposure-catalog ./threat_intel/tanstack-2026-05.json \
--findings-only \
--max-duration 10m > incident-findings.ndjson
--findings-only参数只输出命中威胁的记录,不输出全量清单。应急的时候你不想在几万条记录里翻找。
exposure catalog:威胁情报匹配
Bumblebee最实用的功能是exposure catalog。你定义一个JSON文件,列出已知的恶意包名和版本,扫描时自动匹配。
Perplexity在threat_intel/目录维护了一批catalog,覆盖最近的几个大事件:
- TanStack/Mini Shai-Hulud蠕虫(2026年5月)
- Axios被投毒(2026年3月)
- LiteLLM PyPI后门(2026年3月)
- XZ Utils后门(2024年3月)
catalog格式很简单:
json
{
"schema_version": "0.1.0",
"entries": [
{
"id": "tanstack-shai-hulud-2026-05",
"name": "TanStack Router compromised versions",
"ecosystem": "npm",
"package": "@tanstack/react-router",
"versions": ["1.120.2", "1.120.3"],
"severity": "critical"
},
{
"id": "axios-rat-2026-03",
"name": "Axios RAT dropper",
"ecosystem": "npm",
"package": "axios",
"versions": ["1.14.1", "0.30.4"],
"severity": "critical"
}
]
}
你也可以写自己的catalog。比如公司内部发现了可疑包,5分钟就能配好推全团队扫。
我实际跑了一遍
装好Bumblebee后,我先跑了个baseline看看自己机器上有什么:
bash
bumblebee scan --profile baseline | wc -l
# 输出:4,287
# 按生态系统统计
bumblebee scan --profile baseline | \
jq -r '.ecosystem' | sort | uniq -c | sort -rn
# 3,412 npm
# 389 pypi
# 201 go
# 156 vscode_extension
# 87 chrome_extension
# 42 mcp
4287个包,npm占了80%。意料之中。
然后挂上TanStack的exposure catalog扫一遍:
bash
bumblebee scan --profile deep \
--root "$HOME" \
--exposure-catalog threat_intel/tanstack-2026-05.json \
--findings-only
输出了3条命中:
json
{"ecosystem":"npm","package":"@tanstack/react-query","version":"5.72.1","severity":"high","source":"/Users/dev/projects/dashboard/node_modules/@tanstack/react-query/package.json"}
{"ecosystem":"npm","package":"@tanstack/react-table","version":"8.21.3","severity":"medium","source":"/Users/dev/.pnpm-store/v3/files/@tanstack/react-table/8.21.3/package.json"}
{"ecosystem":"npm","package":"plain-crypto-js","version":"1.0.2","severity":"critical","source":"/Users/dev/projects/api-service/node_modules/plain-crypto-js/package.json"}
第三个plain-crypto-js是Axios投毒事件里那个恶意依赖。它是被axios@1.14.1拉进来的,之前没注意这个项目的axios版本有问题。
接到CI/CD里
手动跑一次有用,但长期靠人记着跑不现实。我写了个GitHub Actions workflow,每天跑一次baseline扫描,有命中就发Slack告警。
yaml
# .github/workflows/supply-chain-scan.yml
name: Supply Chain Scan
on:
schedule:
- cron: '0 9 * * *' # 每天早上9点
workflow_dispatch:
jobs:
scan:
runs-on: ubuntu-latest
steps:
- name: Install Bumblebee
run: |
curl -L https://github.com/perplexityai/bumblebee/releases/latest/download/bumblebee-linux-amd64 \
-o /usr/local/bin/bumblebee
chmod +x /usr/local/bin/bumblebee
- name: Checkout threat catalogs
uses: actions/checkout@v4
with:
sparse-checkout: threat_intel
- name: Run scan
run: |
bumblebee scan --profile baseline \
--exposure-catalog threat_intel/*.json \
--findings-only > findings.ndjson
FINDING_COUNT=$(wc -l < findings.ndjson)
echo "findings=$FINDING_COUNT" >> $GITHUB_OUTPUT
if [ "$FINDING_COUNT" -gt 0 ]; then
echo "::warning::Found $FINDING_COUNT supply chain risks"
cat findings.ndjson | jq .
fi
id: scan
- name: Alert on findings
if: steps.scan.outputs.findings > 0
run: |
curl -X POST "${{ secrets.SLACK_WEBHOOK }}" \
-H 'Content-Type: application/json' \
-d "{\"text\":\"⚠️ Supply chain scan found $(cat findings.ndjson | wc -l) risks. Check workflow run for details.\"}"
本地开发机上也可以用cron或launchd定时跑:
bash
# macOS launchd(每天10点扫一次)
cat > ~/Library/LaunchAgents/com.bumblebee.daily.plist << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.bumblebee.daily</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/bumblebee</string>
<string>scan</string>
<string>--profile</string>
<string>baseline</string>
</array>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>10</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
<key>StandardOutPath</key>
<string>/tmp/bumblebee-daily.ndjson</string>
</dict>
</plist>
EOF
launchctl load ~/Library/LaunchAgents/com.bumblebee.daily.plist
它也扫MCP配置
这个功能我没预料到。Bumblebee会读取Claude Desktop、Cline、Gemini CLI等AI工具的MCP配置文件,检查里面引用的MCP server是否命中威胁列表。
MCP server本质上是LLM能调用的外部工具。如果你装了一个恶意的MCP server,AI助手每次调用它都可能泄露上下文里的敏感信息------API key、数据库连接串、甚至你代码里的业务逻辑。
bash
# 看看你的MCP配置里有哪些server
bumblebee scan --profile baseline | jq 'select(.ecosystem == "mcp")'
我扫出来42个MCP相关条目。大部分是正常的(filesystem、fetch这些官方server),但确实有两个第三方server我装了之后就没管过,也不知道上游有没有更新。
踩过的坑
1. bun.lockb不支持。 Bumblebee当前版本(v0.1.1)只能解析文本格式的lockfile。Bun的二进制lockfile读不了。如果你用Bun,得等后续版本。临时方案是同时生成一份bun.lock(文本格式):bun install --save-text-lockfile。
2. Python虚拟环境路径。 baseline profile默认不扫项目里的.venv目录。如果你的Python虚拟环境在非标准位置,需要用project或deep profile手动指定--root。
3. catalog匹配是精确匹配。 版本号必须完全一致,不支持范围匹配。如果恶意版本是1.14.1,你装的是1.14.0,不会告警。这个设计是有意的------避免误报------但也意味着你的catalog要尽量列全受影响的版本。
4. NDJSON输出量很大。 一台正常开发机扫出来几千条记录很正常。如果你不用SIEM,直接看原始输出会很痛苦。我的做法是写个简单的jq过滤脚本:
bash
#!/bin/bash
# scan-report.sh - 生成可读的扫描报告
bumblebee scan --profile baseline | jq -r '
[.ecosystem, .package, .version] | @tsv
' | sort | uniq -c | sort -rn | head -20
2026年供应链攻击时间线
写到这整理一下今年的供应链攻击事件,你就知道为什么需要这类工具了:
- 3月26日:LiteLLM(AI基础设施库,PyPI日下载340万次)两个包被投毒
- 3月30日:Axios被接管,1.14.1和0.30.4版本植入跨平台RAT
- 5月11日:Shai-Hulud蠕虫首次出现,利用npm postinstall脚本自我传播
- 5月12日:TanStack、Mistral AI、UiPath等160+个npm/PyPI包被Mini Shai-Hulud蠕虫感染
平均每个月都有一次大规模供应链攻击。而且攻击手法在进化------从简单的typosquatting升级到了CI/CD缓存投毒+OIDC token窃取+蠕虫自传播。
适合什么团队用
Bumblebee不是银弹。它不做漏洞扫描(那是Snyk、Dependabot干的事),不做运行时防护(那是EDR干的事),不生成SBOM(那是Syft干的事)。
它做的是一件很具体的事:在安全公告发出后的几分钟内,告诉你团队里哪些机器上有这个包。
适合的场景:
- 安全团队需要快速评估新威胁的影响面
- 团队超过10人,手动检查每台机器不现实
- 用了多种语言和包管理器,没有统一的依赖管理
- 用了AI coding工具(Cursor、Copilot、Claude等),需要审计MCP配置
不适合的场景:
- 小团队1到3人,直接
grep搜一下lockfile就够了 - 只需要项目级依赖检查,
npm audit或pip-audit就行 - 需要持续监控,Bumblebee是点扫描不是守护进程
我在一台装了26个项目的MacBook Pro上跑deep扫描,全盘扫完花了3分40秒,输出12,847条记录。作为应急响应工具,这个速度可以接受。日常用baseline profile,5秒搞定。
GitHub仓库:github.com/perplexitya...
有什么供应链安全相关的经验可以评论区聊聊。