1. 为什么我开始在本地跑一个只读扫描器
供应链投毒这件事,真正让人后背发凉的地方不在于漏洞本身,而在于你为了查漏洞所做的动作,可能正好把恶意代码跑起来了。我见过太多团队在收到预警后的第一反应是打开终端敲npm ls或者pip show,想确认某个包在不在机器上。问题是,包管理器一旦被调用,postinstall、preinstall这类生命周期钩子就有机会执行,你本来是想排查,结果变成了主动触发。
Perplexity 开源的 Bumblebee 就是冲着这个死循环来的。它是一个用 Go 写的只读端点扫描器,核心工作方式是直接读取磁盘上的元数据文件,比如package.json、yarn.lock、go.mod、requirements.txt,以及各类编辑器插件目录和 MCP 配置文件,全程不调用包管理命令、不执行任何脚本、不读取你的应用源码。它回答的问题很聚焦:当某个组件被爆出问题时,我这些开发机上到底有没有真实安装它。
它适合谁?我觉得三类人最该关注。一是负责团队开发安全建设的人,需要一种不打扰开发者、又能快速摸清暴露面的手段;二是经常在自己笔记本上折腾各种 AI 编程工具、MCP 配置、浏览器插件的独立开发者;三是 CI 流水线里想加一道轻量依赖巡检、但不想引入重型 SCA 引擎的工程团队。这篇就按本地开发机和 CI 两个场景,把 Bumblebee 的只读模式跑起来,再结合 TaoToken 的统一 Key 通道,把扫描结果摘要和告警推送串成一条可复制的链路。
2. TaoToken 前置:统一 Key 与 API 通道准备
Bumblebee 本身只负责扫描和产出结构化报告,它不会帮你把报告变成人能快速读懂的摘要,也不会主动推送告警。这部分我选择用 TaoToken 的统一 Key 来做模型调用,把扫描出来的 JSON 结果喂给模型生成中文摘要,再决定推送到哪里。
TaoToken 在这里的角色是一个统一的 API 通道,你不需要为不同模型分别维护多套 Key 和计费,一个 Key 就能覆盖对话、编码等场景。对 Bumblebee 这种“扫描完要立刻出人话摘要”的需求来说,省掉切换成本很关键。
你需要先拿到 Key。进入控制台创建 API Key,地址是 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 。创建后把 Key 存到环境变量里,别硬编码进脚本:
export TAOTOKEN_API_KEY="sk-你的key" export TAOTOKEN_BASE_URL="https://taotoken.net/api"如果你后面想长期在 CI 里跑编码类 Agent 做自动修复建议,可以了解下 Coding Plan,入口在 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite 。单纯做扫描摘要的话,用标准 API 通道就够了。想先验证模型通不通,可以直接在模型对话页试一句,地址 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite 。
注意:TaoToken 的 API 地址是
https://taotoken.net/api,不要在后面拼接多余的路径,具体端点按接入文档来。文档入口在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。
3. 可复制配置:config.toml 骨架与只读启动参数
Bumblebee 的配置我建议单独放一个目录,不要塞进被扫仓库里,这样能保证“不改动被扫仓库”这个前提。下面是我实测下来比较稳的一份config.toml骨架,字段名以你拉到的版本为准,结构可以参考:
# ~/.bumblebee/config.toml [scan] # 只读模式,禁止任何执行动作 read_only = true # 扫描模式:baseline / project / deep profile = "project" # 项目模式下的目标目录,可多个 targets = [ "/Users/me/work/api-service", "/Users/me/work/web-frontend" ] # 排除目录,避免扫进 node_modules 之外的构建产物 exclude = [ "**/dist/**", "**/build/**", "**/.git/**" ] [surfaces] # 语言包管理器元数据 package_managers = ["npm", "pnpm", "yarn", "bun", "pypi", "gomod", "rubygems", "composer"] # AI 代理配置,MCP 单独列为一个攻击面 mcp_configs = true # 编辑器插件 editor_plugins = ["vscode", "cursor", "windsurf", "vscodium"] # 浏览器插件 browser_extensions = ["chrome", "edge", "brave", "arc", "firefox"] [output] format = "json" path = "/Users/me/.bumblebee/reports/latest.json"启动时显式带上只读参数,别依赖默认值:
bumblebee scan \ --config ~/.bumblebee/config.toml \ --read-only \ --profile project \ --output json \ --output-path ~/.bumblebee/reports/latest.json几个参数的含义我拆一下。--read-only是硬约束,确保扫描器不触发任何安装钩子;--profile project针对指定工作空间做定向扫描,比 baseline 更聚焦;--output json是为了后续喂给模型做摘要,纯文本报告不好解析。如果你要做全量应急排查,把 profile 换成deep,但耗时会长不少,建议只在确认有事件时用。
提示:
targets里写的是仓库根目录,Bumblebee 会自己去读 lockfile 和元数据,你不需要先npm install。这一点和传统 SCA 工具的思路完全不同,也是它敢叫只读扫描器的底气。
4. 验证请求:一次依赖漏洞验证动作与结果
配置好之后,先做一次最小验证,确认扫描链路是通的。我拿一个故意装了旧版本依赖的项目来试。假设预警说lodash某个版本有原型污染问题,我要确认本地到底装没装。
第一步,跑扫描:
bumblebee scan --config ~/.bumblebee/config.toml --read-only --profile project第二步,看报告里有没有命中。JSON 结构大致长这样:
{ "profile": "project", "read_only": true, "findings": [ { "surface": "npm", "package": "lodash", "version": "4.17.15", "path": "/Users/me/work/api-service/package-lock.json", "severity_hint": "outdated" } ] }注意read_only: true这个字段,它是你确认扫描过程没有执行任何脚本的直接证据。整个过程中,被扫仓库的node_modules没有被重建,postinstall也没有被触发。
第三步,把这份 JSON 交给 TaoToken 生成中文摘要。用 curl 就能验证:
curl -s https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-3-5-sonnet", "messages": [ {"role": "system", "content": "你是安全巡检助手,把扫描结果总结成三条以内的中文告警,指出包名、版本和受影响路径。"}, {"role": "user", "content": "'"$(cat ~/.bumblebee/reports/latest.json)"'"} ] }'成功的话你会拿到一段类似“检测到 api-service 项目使用 lodash 4.17.15,建议升级到修复版本”的摘要。到这里,只读扫描加统一 Key 摘要的闭环就跑通了。CI 里可以把这段 curl 换成脚本,把摘要写进构建日志或者推到告警渠道。
5. 本篇常见错排查
扫描结果为空,但明明装了包。大概率是targets路径写错了,或者exclude把关键目录排掉了。先确认路径是仓库根目录,再检查 exclude 规则有没有误伤 lockfile 所在层级。
报错提示无法读取某个目录。只读模式对权限依然敏感。macOS 上某些编辑器插件目录需要终端有完全磁盘访问权限,去系统设置里给终端授权即可,和扫描器本身无关。
调用 TaoToken 返回 401。检查TAOTOKEN_API_KEY有没有正确导出,以及请求头是不是Authorization: Bearer。另外确认 base URL 是https://taotoken.net/api,多拼路径会导致 404。
摘要里出现乱码或截断。扫描报告太大时,直接整段塞进 prompt 会超上下文。建议先在本地用jq把 findings 精简成包名加版本两列,再发给模型,既省钱又稳。
CI 里跑得很慢。把 profile 从 deep 降到 project,并且只扫变更的仓库目录。deep 模式是给应急响应用的,不适合每次提交都跑。
6. 把这条链路固定下来
我现在习惯把 Bumblebee 的扫描和 TaoToken 的摘要做成两个独立步骤,中间用 JSON 文件解耦。扫描器只负责产出事实,模型只负责把事实翻译成人话,任何一步出问题都不会互相污染。Key 统一放在 CI 的 secret 里,本地开发机则用环境变量,不写进任何仓库文件。
如果你还想把这套东西接到编码 Agent 上做自动升级建议,可以走 Coding Plan 那条线;只是做巡检告警的话,标准 API 通道加一个定时任务就够了。接入细节和端点说明都在文档里,照着配一遍基本不会踩坑。