5月12日TanStack被投毒,160+个npm包沦陷——我用Bumblebee扫了一遍开发机,发现3个中招的

上个月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 auditpip-audit就行
  • 需要持续监控,Bumblebee是点扫描不是守护进程

我在一台装了26个项目的MacBook Pro上跑deep扫描,全盘扫完花了3分40秒,输出12,847条记录。作为应急响应工具,这个速度可以接受。日常用baseline profile,5秒搞定。


GitHub仓库:github.com/perplexitya...

有什么供应链安全相关的经验可以评论区聊聊。

相关推荐
SEO_juper1 小时前
B2B 工厂专属双引擎策略:SEO 承接采购词排名,GEO 抢占 AI 咨询问答
aigc·seo·跨境电商·外贸·geo·谷歌优化·gsc
Z-D-K3 小时前
考验AI的“自我和意识“-AI对《红楼梦》后40回的改写(22)
人工智能·ai·aigc·agent·agi
Prowler_92564 小时前
创新项目实训博客(十一):大模型智能标题生成与多级降维兜底策略
人工智能·flutter·aigc
sunneo5 小时前
本周 AI 新动态精选(2026.06.08–06.14)
人工智能·aigc·ai编程·ai写作·ai-native
leeyi5 小时前
Tool 组件:让 Agent 学会「动手」的统一接口
aigc·agent·ai编程
张申傲5 小时前
拆解 harness9(4):Skills 系统架构
aigc·agent·deepseek·harness
冰^6 小时前
AI CC Switch 解决了什么?
人工智能·gpt·网络协议·chatgpt·github·aigc
倔强的石头_19 小时前
腾讯云一键部署OpenClaw打造QQ机器人-正文初稿
aigc
不爱洗脚的小滕21 小时前
【Agent】如何为 AI Agent 设计高可用的 Tools
人工智能·aigc·ai编程·rag