mcpField
S
supply-chain-security

supply-chain-security

github.com/superagent-ai安全8068 次安装11 周前更新
代码仓库

关于 supply-chain-security

supply-chain-security 是一项由模型辅助的依赖审查技能,用于检查清单、锁文件、安装脚本和依赖变更。它无需安装工具或运行软件包,即可按严重程度报告供应链风险并给出具体修复建议。

主要功能

  1. 01审查依赖清单和锁文件
  2. 02优先检查新增或变更的软件包
  3. 03检查安装时脚本和构建钩子
  4. 04检测混淆代码和秘密信息访问
  5. 05识别数据外传与持久化行为
  6. 06报告严重程度和修复建议

适用场景

  1. 01审查拉取请求中的依赖变更
  2. 02安装前检查不熟悉的软件包
  3. 03调查可疑的安装脚本
  4. 04在有软件包文件时评估依赖来源

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/superagent-ai/skills --skill supply-chain-security

安装后在 Claude Code 中输入/supply-chain-security确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
checklist.md
ecosystems.md
patterns.md

详细介绍

供应链安全扫描器

这项技能会让模型成为依赖审查员。读取清单文件、锁文件、安装脚本和依赖差异;逐项执行检测流程;报告每项供应链风险,标明严重程度并给出具体修复方案。无需安装工具,无需运行软件包,也无需联系软件包注册表——分析完全由模型读取磁盘上的内容和差异来完成。

依赖是项目中唯一一类由他人编写、未经你审查,却以你的全部权限运行的东西。防御者面临的问题在于,它看起来合法:史上规模最大的 npm 入侵事件利用被钓鱼的维护者,通过 chalk 和 debug(每周下载量达 20 亿次)传播;Shai-Hulud 蠕虫则利用被盗令牌重新发布约 800 个软件包并植入后门。受欢迎不等于安全。发现问题的最佳时机,是依赖新增或变更的时候——这正是这项技能审查的内容。

思维模型

新增或变更的依赖会让陌生人的代码运行两次:一次在安装时(生命周期脚本——在审查任何代码之前,就会在你的笔记本电脑和 CI 运行器上执行),另一次在运行时(在你的应用、用户的浏览器和服务器中)。两个问题决定它是否安全:

  • 身份——这是你想要的软件包吗?发布者是你认为的那个人吗? 风险包括仿冒包名(拼错热门软件包的名称)、AI 幻觉包名抢注(AI 幻觉出一个看似合理的名称,攻击者提前注册该名称)、依赖项/命名空间混淆(有人在公共注册表上抢注内部名称),以及维护者账户被接管(名称值得信任,但令牌被盗)。
  • 行为——它是否做了与其声称用途无关的事情? 尤其是在安装时。风险包括恶意生命周期脚本、混淆载荷、窃取凭据、数据外传、持久化和自我传播。

安装时钩子是重中之重。它会在任何代码审查之前执行,在开发者机器和 CI 运行器上拥有完整的环境和令牌访问权限——这正是每个重大蠕虫(Shai-Hulud、nx 的 s1ngularity 攻击、2026 年 6 月的 binding.gyp 活动)都针对安装时执行的原因。大多数攻击利用 preinstall/postinstall;binding.gyp 蠕虫则通过诱骗 node-gyp 在伪造的原生构建过程中运行任意 shell 命令,完全绕过这些钩子。一个软件包可能完全正常,却仍会在你安装它的瞬间控制你的机器。

如果无法确认软件包的身份,或无法解释其安装脚本的作用,就应将其视为不可信并标记出来。 误报的代价是 PR 被暂缓,以及几分钟的时间。漏报的代价是机器和 CI 上的所有凭据——以及你自己的软件包被重新发布,成为蠕虫传播链的下一个环节。下文的每项判断都应遵循这种风险不对称性。

扫描流程

按顺序逐项执行这些检查。第 0 项用于确定审查对象和新增内容——不要跳过。

第 0 轮:确定审查范围

不了解生态系统及其实际可见范围,就无法评估依赖项。

  • 识别生态系统和文件。 package.json + 锁文件(npm/pnpm/yarn)、requirements.txt / pyproject.toml / poetry.lock / uv.lock(PyPI)、go.mod + go.sum、Cargo.toml + Cargo.lock、Gemfile + Gemfile.lock、pom.xml / build.gradle、.csproj / packages.lock.json、composer.json + composer.lock。每种生态系统中的安装钩子、版本固定、完整性校验和范围控制位于何处,请参阅 references/ecosystems.md。
  • 找出新增或变更内容。 在 PR 中,清单文件和锁文件的差异就是审查目标——新添加的包、版本升级、新的传递依赖条目、新的安装脚本。逐项注明:直接依赖还是传递依赖、新增包还是版本变更,以及安装是否会在存有机密的环境中运行(例如持有发布令牌的 CI、拥有云凭据的开发者笔记本)。
  • 记录可见范围。 如果包源码在磁盘上(node_modules/、vendored 目录或解压后的 tarball),就可以直接检查其安装脚本和载荷——请这样做。如果清单中只有名称和版本,那么审查仅限于身份核验和卫生检查;请说明这一点,并在结尾部分建议用于确认的命令。
第 1 轮:身份核验——这是你要找的那个包吗?

在检查行为之前,先确认该名称解析到的是你认为的发布者。

  • 拼写仿冒。 名称与热门包仅差少量字符或键盘上的相邻按键,例如:crossenv↔cross-env、reqeusts↔requests、lodahs↔lodash、electorn↔electron。如果某个依赖项的名称与一个知名包仅相差一次字符换位、遗漏或重复,请标记出来。
  • Slopsquatting(AI 幻觉名称抢注)。 LLM 建议的包名中,大约五分之一并不存在,而且幻觉名称会反复出现——因此攻击者会抢先注册热门名称并植入恶意软件。对于看似合理但陌生的“辅助”类名称(flask-gpt-helper、auth-helper-pro、py-openai-utils、easy-requests),应保持警惕。如果依赖项是根据 AI 的建议添加的,安装前请确认该包确实以完全相同的名称存在——不要让幻觉生成的导入语句变成实际安装。
  • 依赖项/命名空间混淆。 看似内部包名的未加作用域名称(如 internal-、corp-、company- 或产品代号)可能遭到劫持:攻击者在公共仓库发布同名包,并使用更高版本号,从而在解析时胜出。也要标记与组织名称几乎相同的作用域(例如 @acme-corp 与 @acme)。解决方法是在已提交的配置中使用带作用域的名称,并将其固定到私有仓库——请参阅 references/patterns.md。
  • 账号接管迹象。 长期稳定的包突然发布异常版本、更换维护者、添加此前从未有过的安装脚本,或其差异中出现与包用途无关的文件——尤其是纯 JS 包中出现 binding.gyp 或数兆字节的根目录 index.js。chalk/debug、nx、Shai-Hulud 和 binding.gyp 蠕虫都曾利用钓鱼或盗取的令牌,通过受信任的名称传播。样式库的差异中新增 preinstall 和两个新文件,就是应当警惕的情况;纯 JS 库新增 binding.gyp + index.js 也是如此。
第 2 轮:安装时执行——重中之重

阅读所有生命周期脚本。在 npm 中,检查 package.json scripts 里的 preinstall、install、postinstall 和 prepare。其他生态中的对应项包括:setup.py / PEP 517 构建钩子(PyPI)、原生 extconf.rb(RubyGems)、build.rs(Cargo)。preinstall 是最明显的信号——Shai-Hulud 2.0 特意将代码移到这里,以便在任何检查工具运行之前执行。

如果存在 binding.gyp,也要阅读。npm 发现该文件时,会调用 node-gyp 来编译它认为是原生扩展的内容——即使设置了 ignore-scripts=true 也一样,因为这不属于生命周期钩子。2026 年 6 月的 binding.gyp 蠕虫让 package.json scripts 保持干净,并将执行逻辑藏在一个约 100 字节的 binding.gyp 中,利用 sources 数组里的 shell 展开:

// [P0] binding.gyp worm — node-gyp runs arbitrary shell during "compile"
{
  "targets": [{
    "target_name": "Setup",
    "type": "none",
    "sources": ["< $(node index.js >/dev/null) >/dev/null 2>&1 && echo stub.c)"]
  }]
}

这样会在 npm install 期间静默执行根目录下的 index.js(已知攻击活动中的混淆文件为 4.5–4.9 MB)。如果某个包没有正当的原生代码却包含 binding.gyp,或 sources 条目含有 $()、反引号或 shell 重定向,或某个 type: "none" 目标唯一的作用就是触发命令,都应标记出来。

正当的安装脚本确实存在:node-gyp、esbuild、sharp、bcrypt、playwright 会编译或下载到包自身目录中的平台二进制文件。要将这类脚本与以下情况区分开来:

  • 向非注册表主机发起网络请求;
  • 读取环境变量、~/.npmrc、SSH 密钥或云凭据;
  • 在包目录之外写入文件(shell rc 文件、~、系统路径);
  • 启动 shell,或调用本地 AI CLI(claude、gemini、q)搜寻机密(nx 的 s1ngularity 技术);或
  • 安装其他运行时(Bun),以便在 Node 的监控进程之外执行载荷。
// [P0] Shai-Hulud-class install hook — preinstall runs a dropped payload before review
{
  "name": "innocent-looking-util",
  "version": "1.2.4",
  "scripts": { "preinstall": "node setup_bun.js" }
}

setup_bun.js + bun_environment.js(Shai-Hulud 2.0/3.0)、telemetry.js(s1ngularity)、恶意的 binding.gyp + 根目录 index.js(binding.gyp 蠕虫),或任何为运行混淆代码而安装 Bun 的安装路径,一经发现就是 P0——应视该包已遭入侵,不要安装,并轮换任何已暴露的凭据。请参阅 references/checklist.md 中的攻击活动标记。

第 3 轮:混淆与动态代码

如果某个依赖没有理由进行混淆,那么混淆本身就是发现的问题。请标记:

  • 声称功能简单的包中存在大型、单一的压缩/编码代码块;
  • 十六进制/Base64/XOR 字符串数组、eval(...)、new Function(...)、vm.runInThisContext、传入 eval 的 atob();
  • 解码后执行和获取后执行:从 URL 获取代码并运行(require() 下载的文件、由 child_process 运行获取的脚本)——这是一种分阶段载荷模式,可让攻击者无需重新发布就能更改恶意软件。

不必彻底反混淆。一个样式或日期库如果包含加密载荷和 eval,就足以构成可疑因素,应予以拦截。

第 4 轮:凭据和机密访问

恶意包会搜集安装或运行环境中可访问的一切内容。请标记以下内容的读取行为:

  • 枚举 process.env 并将内容发送到本机以外;读取 ~/.npmrc(npm 令牌)、~/.aws/credentials、SSH 密钥、.env、gh auth token;
  • 云实例元数据服务 169.254.169.254(AWS/GCP/Azure IMDS)和容器凭据(AWS_CONTAINER_CREDENTIALS_FULL_URI、ECS 任务令牌)、Vault(VAULT_ADDR/VAULT_TOKEN);
  • 云机密管理器(AWS Secrets Manager、Azure Key Vault、GCP Secret Manager);
  • 下载或调用 TruffleHog,主动扫描文件系统以搜寻机密(Shai-Hulud 的典型特征)。
第 5 轮:数据外传、C2 和自我传播

收集到的数据会流向何处,以及一个被攻陷的依赖如何成为下一个目标:

  • 向非注册表目的地外传数据:通过 HTTP(S) POST 将数据发送到硬编码的主机/IP、DNS 隧道、Discord/Telegram/Slack Webhook,或创建公开 GitHub 仓库作为死信箱(Shai-Hulud 的 Sha1-Hulud: The Second Coming. 仓库;s1ngularity 的 s1ngularity-repository-*)。
  • 自我传播(蠕虫)。软件包会读取自身文件、验证 npm 令牌(/-/whoami)、枚举维护者的其他软件包(/-/v1/search?text=maintainer:...),然后运行 npm publish 为这些软件包植入后门。这正是一次恶意提交演变成约 800 个软件包被攻陷的原因。任何包含自我重新发布逻辑的依赖都是 P0。
第 6 轮:持久化和破坏
  • 安装后持久化:编辑 ~/.bashrc / ~/.zshrc、注册自托管 GitHub Actions 运行器(命名为 SHA1HULUD)并添加恶意工作流(discussion.yaml)以远程执行命令、注入 GitHub Actions 工作流步骤(setup-bun + 载荷执行——binding.gyp 蠕虫的 CI 持久化手段)、cron,或 systemd 单元。
  • 破坏性故障保护机制。如果既无法外传数据也无法传播,Shai-Hulud 2.0 会尝试擦除用户的主目录(shred、rm -rf $HOME、del /F /Q /S)。标记任何包含破坏性文件系统或关机命令的依赖。
  • 抗议软件。根据地理位置或日期触发破坏行为(例如,node-ipc 会为特定区域设置的用户擦除文件)。如果某个依赖根据 process.env.LANG、国家/地区或 Date 执行有害操作,即使它“只是”针对其他人,也应作为发现项报告。
第 7 轮:完整性和版本卫生

本轮检查清单文件和锁文件,即使看不到软件包源代码也适用。标记以下情况:

  • 浮动版本——^、~、*、latest,或 git/http(s) tarball 依赖。版本范围意味着“使用注册表下次提供的任何版本”,包括被攻陷的补丁版本。请锁定精确版本。
  • 未提交锁文件,或 CI 中未强制使用锁文件。锁文件通过完整性哈希锁定整个传递依赖树;npm ci / --frozen-lockfile 会拒绝修改锁文件。没有锁文件时,每次安装都像掷骰子一样充满风险。
  • 锁文件条目包含 tarball URL,但没有 integrity 哈希——这在功能上等同于 latest,也是注册表无法验证的条目。检查 integrity 是否为空或缺失,以及 resolved URL 是否指向注册表之外。
  • 清单与锁文件不一致、可疑的版本跳跃(长期稳定的软件包突然大幅升级)、全新或近期发布的版本(尚未经过冷却期),以及生态系统支持时缺少来源证明。冷却期 / min-release-age、npm audit signatures、OSV 等修复方案见结尾部分和 references/patterns.md。

发现项格式

按以下结构报告每个发现项。按严重性分组,P0 优先。

[P0] malicious-install-script in package.json:14 (dependency: [email protected])
  Adds a `preinstall` hook running `node setup_bun.js`. This is the Shai-Hulud
  install-time worm pattern: the dropped payload installs the Bun runtime and runs
  an obfuscated credential stealer before any review, on every dev machine and CI
  runner that installs this package.

  Vulnerable:
    "scripts": { "preinstall": "node setup_bun.js" }

  Fix: do not install. Remove the dependency, delete node_modules and the lockfile
  entry, clear the npm cache, and rotate every credential reachable from machines
  that already installed it (npm, GitHub, cloud, SSH). Confirm against the published
  IOC lists before trusting any nearby version.

如果用户粘贴的清单或代码片段没有路径,请称其为“清单” / “软件包”,并使用代码片段内的行号。务必注明软件包和版本、触发该发现的轮次,以及安装或运行时上下文中暴露机密的位置——这些就是依赖有危险的证据,也是修复方案的依据。

严重性等级

  • P0 — 已确认存在当前可触发的恶意行为:会窃取并外传数据的安装脚本、已知遭入侵的版本(匹配活动 IOC)、蠕虫式自传播、获取后执行远程代码,或破坏性载荷。不要安装;彻底移除并轮换已暴露的凭据。
  • P1 — 高风险且未经审查:行为不明的安装脚本会执行网络、文件系统或混淆操作;可能仿冒真实依赖的名称(typosquat / slopsquat / 依赖混淆);未限定作用域、看似内部名称却能从公共注册表解析;或声誉低且刚发布的软件包被引入敏感路径。确认前阻止合并。
  • P2 — 卫生方面的缺失会扩大影响范围:版本范围浮动、缺少锁文件或未强制使用锁文件、锁文件条目没有完整性哈希、未设置冷却期、CI 中未禁用安装脚本、未验证来源证明。在常规审查周期内修复。
  • P3 — 深度防御:没有 SBOM、没有 OpenSSF Scorecard 信号、没有构建脚本允许列表、PR 上缺少依赖审查自动化。

依赖项看起来正常时

完成所有轮次后,如果没有发现问题:

  1. 明确说明这一点,并列出已验证的内容:Pass 0 中识别的生态系统和文件、已审查新增或变更的软件包、不存在安装脚本(或仅有无害的平台构建脚本)、在可查看的源代码中未发现混淆或凭据窃取/外传模式,以及版本已固定并提交了带完整性信息的锁文件。
  2. 说明静态阅读无法确认的内容:已发布的 tarball 可能与源代码仓库不同(信任边界是注册表,而非 GitHub);你未查看的传递依赖;仅在运行时发生的行为;注册表上的当前版本是否与审查时的版本一致;以及声誉、发布时间和来源证明,这些都需要查询注册表/OSV,而你并未执行此类查询。
  3. 建议使用以下命令进行确认(用户对公共数据运行这些命令——此技能本身保持离线):npm audit signatures(来源证明)、osv-scanner --lockfile=...(已知恶意软件/CVE);对于 JS/TS 项目,使用 cve-lite-cli(OWASP 本地优先的锁文件扫描器——跨 npm/pnpm/yarn/bun 匹配 OSV,并显示直接/传递依赖、提供可复制运行的 --fix 修复方式,以及 --offline 公告数据库;匹配的是已知公告漏洞,因此它是对上述行为审查的补充,而非替代);npm view <pkg> time(检查新近程度,以决定冷却期);以及 Socket/Snyk/Aikido 行为检查。另外,设置冷却期(min-release-age / minimumReleaseAge,1–7 天),让社区有时间在新发布的入侵版本进入项目之前发现问题。完整审计请按 references/checklist.md 操作。

静态审查未发现问题,并不能证明已安装的制品是安全的。

参考文件

  • references/ecosystems.md — 判断依赖项所需的各生态系统信息:安装钩子的位置、如何固定版本并锁定依赖、完整性/哈希机制、命名空间/作用域支持(据此判断依赖混淆风险)、来源证明机制,以及 npm/pnpm/yarn、PyPI、Go、Cargo、RubyGems、Maven/Gradle、NuGet 和 Composer 的确切离线及注册表验证命令。
  • references/patterns.md — “安全地新增或更新依赖项”操作方案:冷却期、锁文件 + npm ci、使用 ignore-scripts 并配置允许列表、通过限定作用域的名称 + 固定注册表防范依赖混淆、来源证明/Trusted-Publisher 验证、精确固定版本,以及将 CI 密钥与安装脚本隔离。提出修复方案时请阅读。
  • references/checklist.md — 按依赖项/PR 差异/仓库/组织划分的清单,包含分流顺序,以及已知攻击活动标记附录(Shai-Hulud 1/2/3、nx s1ngularity、binding.gyp 蠕虫、chalk/debug 入侵事件)。完整审计、一次扫描多个依赖项或比对具体 IOC 时请使用。

此技能不会做什么

  • 它不会安装、运行或以其他方式执行软件包(即使使用 --ignore-scripts)。它会读取清单、锁文件以及磁盘上已有的任何源代码。如需运行时确认,请结合使用沙箱化安装和行为监控(Socket、Datadog SCFW、网络隔离容器)——这是对此静态审查的动态补充。
  • 它不会悄悄向外发送数据。实时信誉、时效性和来源信息需要查询 registry/OSV;此技能会提供准确的命令供你自行运行,而不会将你的依赖列表发送到任何地方。
  • 它不会取代持续的 SCA 流水线(OSV-Scanner、Snyk、Socket、配合冷却期使用的 Dependabot/Renovate)。它是安装前的检查,可发现仅检查清单的扫描器会漏掉的问题——安装脚本的意图;当你准备添加来自陌生人的代码时,它也是一道门禁。
  • 它不会因为某个软件包很受欢迎或有数百万次下载就放行。热门软件包也会遭到入侵;这正是整个威胁模型。在 left-pad 中,一个无法解释的安装脚本比一个无人使用的软件包中的安装脚本更危险。

评论

支持 Markdown