supply-chain-security
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSupply-Chain Security Scanner
供应链安全扫描器
This skill turns the model into a dependency reviewer. Read the manifests, lockfiles, install scripts, and the dependency diff; walk the detection passes; report each supply-chain risk with a severity and a concrete fix. No tools to install, no package to run, no registry to phone — the analysis is the model reading what's on disk and in the diff.
A dependency is the one thing in your project you didn't write and didn't review, running with your full privileges. The defender's problem is that it looks legitimate: the largest npm compromise in history rode and (2 billion weekly downloads) through a phished maintainer, and the Shai-Hulud worm backdoored ~800 packages by republishing them under stolen tokens. Popularity is not safety. The moment to catch this is when a dependency is added or changed — that is what this skill reviews.
chalkdebug本 skill 让模型充当依赖审查器。读取清单、锁文件、安装脚本和依赖差异;依次执行各项检测;按严重程度和具体修复建议报告每个供应链风险。无需安装工具,无需运行软件包,也不会连接 registry——所有分析都由模型读取磁盘上的内容和差异完成。
依赖是项目中少数不由你编写、也未经你审查,却以你的全部权限运行的东西。防御者面临的问题是,它看起来很合法:史上规模最大的 npm 失陷事件借助被钓鱼的维护者账户,攻破了 和 (每周 20 亿次下载);而 Shai-Hulud 蠕虫则利用被盗的 token 重新发布约 800 个包并植入后门。流行不等于安全。抓住这类风险的时机,正是依赖被添加或变更的时候——这正是本 skill 的审查对象。
chalkdebugMental model
思维模型
A new or changed dependency runs code from a stranger twice: once at install time (lifecycle scripts — on your laptop and your CI runner, before a single line is reviewed) and again at runtime (in your app, your users' browsers, your servers). Two questions decide whether it is safe:
- Identity — is this the package you meant, from who you think published it? Defeated by typosquatting (a misspelling of a popular name), slopsquatting (a plausible name an AI hallucinated and an attacker pre-registered), dependency / namespace confusion (an internal name claimed on a public registry), and maintainer account takeover (a trusted name, a stolen token).
- Behavior — does it do something a library of its stated purpose has no reason to do? Especially at install time. Defeated by malicious lifecycle scripts, obfuscated payloads, credential harvesting, exfiltration, persistence, and self-propagation.
The install-time hook is the crown jewel. It executes before any code review, on developer machines and CI runners, with full environment and token access — which is exactly why every major worm (Shai-Hulud, the nx attack, the June 2026 binding.gyp campaign) targets install time. Most land in / ; the binding.gyp worm bypasses those hooks entirely by tricking into running arbitrary shell during a fake native build. A package can be perfectly functional and still own your machine the instant you install it.
s1ngularitypreinstallpostinstallnode-gypWhen you cannot confirm a package's identity, or cannot explain what its install script does, treat it as untrusted and flag it. The cost of a false positive is a held PR and a few minutes. The cost of a false negative is every credential on the machine and in CI — and your own packages republished as the next link in the worm. This asymmetry governs every judgment call below.
新添加或发生变更的依赖会两次运行陌生人的代码:一次在安装时(生命周期脚本——在你的笔记本电脑和 CI runner 上,在任何一行代码被审查之前),另一次在运行时(在你的应用、用户的浏览器和服务器中)。判断它是否安全取决于两个问题:
- 身份——这是你想要的包吗?发布者是你以为的那个人吗? 以下攻击可能绕过这些检查:仿冒包(typosquatting,热门包名的拼写错误)、AI 幻觉包名抢注(slopsquatting,AI 幻觉出一个看似合理的名称,而攻击者已提前注册)、依赖/命名空间混淆(内部名称被公共 registry 抢注),以及维护者账户接管(可信名称配上被盗的 token)。
- 行为——它是否做了某个声明用途的库根本没有理由做的事情? 尤其是在安装时。恶意生命周期脚本、混淆载荷、凭据收集、外泄、持久化和自我传播都可能绕过行为检查。
安装时钩子是最关键的防线。它会在任何代码审查之前执行,运行于开发机器和 CI runner 上,并拥有完整的环境变量与 token 访问权限——这正是每个重大蠕虫(Shai-Hulud、nx 攻击、2026 年 6 月的 binding.gyp 活动)都针对安装阶段下手的原因。大多数恶意载荷会落在 / 中;binding.gyp 蠕虫则完全绕过这些钩子,通过在伪造的原生构建过程中诱使 执行任意 shell 命令。一个软件包可能功能完全正常,却仍会在你安装它的瞬间接管你的机器。
s1ngularitypreinstallpostinstallnode-gyp如果无法确认软件包的身份,或者无法解释其安装脚本的用途,就应将其视为不可信并予以标记。 误报的成本是暂缓合并一个 PR,并花费几分钟时间。漏报的成本则是机器和 CI 中的所有凭据,以及你自己的软件包被重新发布,成为蠕虫链条中的下一环。以下每项判断都应遵循这种不对称性。
Scan procedure
扫描流程
Walk these passes in order. Pass 0 establishes what you're reviewing and what's new — don't skip it.
按顺序执行以下阶段。阶段 0 用于确定审查对象和变更内容——不可跳过。
Pass 0: scope what you're reviewing
阶段 0:确定审查范围
You can't assess a dependency without knowing the ecosystem and how much of it you can actually see.
- Identify the ecosystem and files. + a lockfile (npm/pnpm/yarn),
package.json/requirements.txt/pyproject.toml/poetry.lock(PyPI),uv.lock+go.mod,go.sum+Cargo.toml,Cargo.lock+Gemfile,Gemfile.lock/pom.xml,build.gradle/.csproj,packages.lock.json+composer.json. Seecomposer.lockfor where install hooks, pinning, integrity, and scoping live in each.references/ecosystems.md - Find what's new or changed. In a PR, the diff of the manifest and lockfile is the target — a freshly added package, a version bump, a new transitive entry, a new install script. Note for each: direct vs transitive, new package vs version change, and whether the install runs anywhere holding secrets (CI with publish tokens, a developer laptop with cloud creds).
- Note how much you can see. If the package source is on disk (, vendored, an extracted tarball), you can read its install scripts and payload directly — do it. If you only have a name and version in a manifest, you are limited to identity and hygiene checks; say so, and recommend the confirmation commands in the closing section.
node_modules/
如果不知道依赖所属的生态系统,以及实际能够看到多大范围,就无法评估它。
- 识别生态系统和文件。 + 锁文件(npm/pnpm/yarn)、
package.json/requirements.txt/pyproject.toml/poetry.lock(PyPI)、uv.lock+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 中,审查目标是清单和锁文件的差异——新添加的软件包、版本升级、新增的传递依赖条目或新的安装脚本。针对每项变更记录:它是直接依赖还是传递依赖,是新软件包还是版本变更,以及安装过程是否会在持有敏感信息的环境中运行,例如带发布 token 的 CI,或保存了云凭据的开发人员笔记本电脑。
- 确认可见范围。 如果软件包源代码已存在于磁盘上(、vendored 目录或解包后的 tarball),就可以直接读取其安装脚本和载荷——务必这样做。如果清单中只有名称和版本,则只能进行身份与安全卫生检查;应明确说明这一限制,并推荐文末章节中的确认命令。
node_modules/
Pass 1: identity — is this the package you meant?
阶段 1:身份——这是你想要的包吗?
Before behavior, confirm the name resolves to who you think.
- Typosquatting. A name within a small edit or keyboard distance of a popular package: ↔
crossenv,cross-env↔reqeusts,requests↔lodahs,lodash↔electorn. Flag any dependency whose name is one transposition/omission/duplication away from a well-known package it is not.electron - Slopsquatting (AI-hallucinated names). Roughly 1 in 5 package names suggested by LLMs don't exist, and the hallucinations repeat — so attackers pre-register the popular ones with malware. Be suspicious of plausible-but-unfamiliar "helper" names (,
flask-gpt-helper,auth-helper-pro,py-openai-utils). If a dependency was added on an AI's suggestion, confirm the real package exists under that exact name before installing — do not let a hallucinated import become an install.easy-requests - Dependency / namespace confusion. An unscoped name that looks internal (,
internal-,corp-, a product codename) can be hijacked: an attacker publishes the same name publicly with a higher version and wins resolution. Also flag a scope that is almost the org's (company-vs@acme-corp). The fix is scoped names pinned to the private registry in a committed config — see@acme.references/patterns.md - Account-takeover smell. A long-stable package that suddenly ships an odd version, changes maintainer, adds an install script it never had, or whose diff adds files unrelated to its purpose — especially a or a multi-megabyte root
binding.gypin a package that has never shipped native code.index.js/chalk, nx, Shai-Hulud, and the binding.gyp worm all rode trusted names through phished or stolen tokens. A diff that addsdebug+ two new files to a styling library is the shape to fear; so is a diff that addspreinstall+binding.gypto a pure-JS library.index.js
检查行为之前,先确认名称解析到了预期的发布者。
- 仿冒包(typosquatting)。 名称与热门软件包仅相差一次转置、遗漏或重复:↔
crossenv、cross-env↔reqeusts、requests↔lodahs、lodash↔electorn。如果某个依赖的名称与一个知名软件包只差一次转置、遗漏或重复,却并非该知名软件包,应予以标记。electron - AI 幻觉包名抢注(slopsquatting)。 LLM 推荐的软件包名称大约有 1/5 并不存在,而且这些幻觉会反复出现——因此攻击者会提前注册其中最常见的名称并植入恶意软件。对看似合理但不熟悉的“辅助工具”名称应保持警惕,例如 、
flask-gpt-helper、auth-helper-pro、py-openai-utils。如果某个依赖是 AI 建议添加的,安装前必须确认真实软件包确实以该准确名称存在——不要让 AI 幻觉出的导入语句演变成一次安装。easy-requests - 依赖/命名空间混淆。 看起来像内部用途的非 scoped 名称(、
internal-、corp-或产品代号)可能被劫持:攻击者会在公共 registry 发布同名更高版本的软件包,并在解析时胜出。如果某个 scope 与组织名称非常相似,也应予以标记,例如company-与@acme-corp。修复方式是在已提交的配置中,将 scoped 名称固定到 private registry;参见@acme。references/patterns.md - 账户接管迹象。 一个长期稳定的软件包突然发布异常版本、更换维护者、添加此前从未有过的安装脚本,或者其差异中加入了与声明用途无关的文件——尤其是在从未提供原生代码的软件包中出现 ,或根目录中出现数 MB 的
binding.gyp。index.js/chalk、nx、Shai-Hulud 和 binding.gyp 蠕虫都借助可信名称,通过被钓鱼或盗取的 token 发起攻击。给样式库添加debug和两个新文件,正是需要警惕的形态;给纯 JS 库添加preinstall和binding.gyp同样如此。index.js
Pass 2: install-time execution — the crown jewel
阶段 2:安装时执行——最关键的环节
Read every lifecycle script. In npm: , , , and in . Equivalents elsewhere: / PEP 517 build hooks (PyPI), native (RubyGems), (Cargo). is the loudest signal — Shai-Hulud 2.0 moved there specifically to run before any inspection tooling.
preinstallinstallpostinstallpreparepackage.jsonscriptssetup.pyextconf.rbbuild.rspreinstallAlso read if present. When npm sees one, it invokes to compile what it assumes is a native addon — even with , because this is not a lifecycle hook. The June 2026 binding.gyp worm keeps clean and hides execution in a ~100-byte that abuses shell expansion in the array:
binding.gypnode-gypignore-scripts=truepackage.jsonscriptsbinding.gypsourcesjson
// [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)"]
}]
}That silently runs a root-level (4.5–4.9 MB obfuscated in known campaigns) during . Flag any in a package with no legitimate native code, any entry with , backticks, or shell redirection, and any target whose only job is to trigger a command.
index.jsnpm installbinding.gypsources$()type: "none"Legitimate install scripts exist: , , , , compile or download a platform binary into the package's own directory. Distinguish that from a script that:
node-gypesbuildsharpbcryptplaywright- makes network calls to non-registry hosts,
- reads environment variables, , SSH keys, or cloud credentials,
~/.npmrc - writes outside the package directory (shell rc files, , system paths),
~ - spawns a shell, or invokes a local AI CLI (,
claude,gemini) to hunt for secrets (the nxqtechnique), ors1ngularity - installs an alternate runtime (Bun) to execute a payload outside Node's monitored process.
json
// [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" }
}The files + (Shai-Hulud 2.0/3.0), (s1ngularity), a malicious + root (binding.gyp worm), or any install path that installs Bun to run an obfuscated blob are P0 on sight — treat the package as compromised, do not install, and rotate any credentials already exposed. See the campaign markers in .
setup_bun.jsbun_environment.jstelemetry.jsbinding.gypindex.jsreferences/checklist.md读取每个生命周期脚本。在 npm 中,包括 下的 、、 和 。其他生态系统中的对应机制包括:PyPI 的 / PEP 517 构建钩子、RubyGems 的原生 ,以及 Cargo 的 。 是最显眼的信号——Shai-Hulud 2.0 特意转移到那里执行,就是为了在任何检查工具运行之前行动。
package.jsonscriptspreinstallinstallpostinstallpreparesetup.pyextconf.rbbuild.rspreinstall如果存在 ,也必须读取。npm 看到该文件后,会调用 编译它所认为的原生插件——即使设置了 也一样,因为这不是生命周期钩子。2026 年 6 月的 binding.gyp 蠕虫会保持 干净,并将执行逻辑藏在一个约 100 字节的 中,在 数组中滥用 shell 展开:
binding.gypnode-gypignore-scripts=truepackage.jsonscriptsbinding.gypsourcesjson
// [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)"]
}]
}已知攻击活动会在 期间静默运行根目录中的 (混淆后大小为 4.5–4.9 MB)。如果软件包没有合法的原生代码却包含 ,任何 条目含有 、反引号或 shell 重定向,或者任何 目标唯一的作用只是触发命令,都应予以标记。
npm installindex.jsbinding.gypsources$()type: "none"合法安装脚本确实存在:、、、、 会进行编译,或将平台二进制文件下载到软件包自己的目录中。应将它们与执行以下操作的脚本区分开来:
node-gypesbuildsharpbcryptplaywright- 向非 registry 主机发起网络调用;
- 读取环境变量、、SSH 密钥或云凭据;
~/.npmrc - 写入软件包目录之外的位置,如 shell rc 文件、或系统路径;
~ - 启动 shell,或调用本地 AI CLI(、
claude、gemini)搜寻密钥(nxq技术);s1ngularity - 安装替代运行时(Bun),以在 Node 监控进程之外执行载荷。
json
// [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.jsbun_environment.jstelemetry.jsbinding.gypindex.jsreferences/checklist.mdPass 3: obfuscation and dynamic code
阶段 3:混淆与动态代码
Obfuscation in a dependency that has no reason to obfuscate is itself the finding. Flag:
- a large single minified/encoded blob in a package whose stated job is small;
- hex / base64 / XOR string arrays, ,
eval(...),new Function(...),vm.runInThisContextpiped intoatob();eval - decode-then-execute and fetch-then-execute: code pulled from a URL and run (of a downloaded file,
require()running a fetched script) — the staged-payload pattern that lets the attacker change the malware without republishing.child_process
You don't have to fully deobfuscate it. A styling or date library that ships an encrypted payload and an is suspicious enough to block on.
eval如果一个依赖没有任何理由进行混淆,混淆本身就是风险项。应标记以下情况:
- 声明用途很小的软件包中却包含体积很大的单个压缩或编码 blob;
- 十六进制/base64/XOR 字符串数组、、
eval(...)、new Function(...),或将vm.runInThisContext的输出传入atob();eval - 先解码再执行和先下载再执行:从 URL 拉取代码并运行(一个下载的文件,或由
require()运行下载的脚本)——这种分阶段载荷模式允许攻击者无需重新发布软件包就能更换恶意代码。child_process
不需要完全完成反混淆。一个样式库或日期库如果发布了加密载荷并包含 ,就足以令人警惕并阻止安装。
evalPass 4: credential and secret access
阶段 4:凭据与密钥访问
Malicious packages harvest everything reachable from the install or runtime context. Flag reads of:
- enumerated and sent off-box;
process.env(npm token),~/.npmrc, SSH keys,~/.aws/credentials,.env;gh auth token - the cloud instance metadata service (AWS/GCP/Azure IMDS) and container creds (
169.254.169.254, ECS task tokens), Vault (AWS_CONTAINER_CREDENTIALS_FULL_URI/VAULT_ADDR);VAULT_TOKEN - cloud secret managers (AWS Secrets Manager, Azure Key Vault, GCP Secret Manager);
- a download or invocation of TruffleHog to actively scan the filesystem for secrets (a Shai-Hulud hallmark).
恶意软件包会收集从安装或运行上下文能够访问到的一切。应对以下读取行为予以标记:
- 枚举 并将其发送到机器之外;读取
process.env(npm token)、~/.npmrc、SSH 密钥、~/.aws/credentials或.env;gh auth token - 访问云实例元数据服务 (AWS/GCP/Azure IMDS)和容器凭据(
169.254.169.254、ECS task token)、Vault(AWS_CONTAINER_CREDENTIALS_FULL_URI/VAULT_ADDR);VAULT_TOKEN - 访问云密钥管理服务(AWS Secrets Manager、Azure Key Vault、GCP Secret Manager);
- 下载或调用 TruffleHog,主动扫描文件系统中的密钥(Shai-Hulud 的典型标志)。
Pass 5: exfiltration, C2, and self-propagation
阶段 5:外泄、C2 与自我传播
Where harvested data goes, and how one compromised dependency becomes the next:
- Exfiltration to non-registry destinations: HTTP(S) POST to a hardcoded host/IP, DNS tunneling, Discord/Telegram/Slack webhooks, or creating a public GitHub repo as a dead-drop (Shai-Hulud's repos; s1ngularity's
Sha1-Hulud: The Second Coming.).s1ngularity-repository-* - Self-propagation (worm). The package reads its own file, validates an npm token (), enumerates the maintainer's other packages (
/-/whoami), and runs/-/v1/search?text=maintainer:...to backdoor them. This is what turned one bad commit into ~800 compromised packages. Any dependency that contains its own re-publishing logic is P0.npm publish
需要确定收集到的数据会流向何处,以及一个已失陷的依赖如何成为下一次入侵的入口:
- 外泄到非 registry 目的地:向硬编码的主机/IP 发起 HTTP(S) POST、DNS 隧道、Discord/Telegram/Slack webhook,或创建公共 GitHub 仓库作为死信点(Shai-Hulud 的 仓库;s1ngularity 的
Sha1-Hulud: The Second Coming.)。s1ngularity-repository-* - 自我传播(蠕虫)。 软件包读取自身文件、验证 npm token()、枚举维护者的其他软件包(
/-/whoami),然后运行/-/v1/search?text=maintainer:...给它们植入后门。正是这种机制让一次恶意提交扩散到约 800 个已失陷软件包。任何包含自行重新发布逻辑的依赖都是 P0。npm publish
Pass 6: persistence and sabotage
阶段 6:持久化与破坏
- Persistence beyond the install: edits to /
~/.bashrc, a registered self-hosted GitHub Actions runner (named~/.zshrc) plus a rogue workflow (SHA1HULUD) for remote command execution, injected GitHub Actions workflow steps (discussion.yaml+ payload execution — the binding.gyp worm's CI persistence), cron, or systemd units.setup-bun - Destructive failsafe. Shai-Hulud 2.0 attempts to wipe the user's home directory (,
shred,rm -rf $HOME) if it can neither exfiltrate nor propagate. Flag any dependency containing destructive filesystem or shutdown commands.del /F /Q /S - Protestware. Sabotage gated on geography or date (e.g. wiping files for certain locales). A dependency that branches on
node-ipc, a country, orprocess.env.LANGto do something harmful is a finding even if "only" targeting others.Date
- 安装后持久化: 修改 /
~/.bashrc;注册名为~/.zshrc的自托管 GitHub Actions runner,并添加恶意 workflow(SHA1HULUD)以远程执行命令;向 GitHub Actions workflow 注入步骤(discussion.yaml+ 载荷执行——binding.gyp 蠕虫的 CI 持久化手段);以及写入 cron 或 systemd 单元。setup-bun - 破坏性兜底逻辑。 如果既不能外泄数据也无法传播,Shai-Hulud 2.0 会尝试清空用户的主目录(、
shred、rm -rf $HOME)。任何包含破坏性文件系统或关机命令的依赖都应予以标记。del /F /Q /S - 抗议型破坏(protestware)。 根据地理位置或日期实施破坏,例如 会清除特定语言环境下的文件。如果依赖根据
node-ipc、国家或process.env.LANG分支并执行有害操作,即使它“仅”针对其他人,也属于风险项。Date
Pass 7: integrity and version hygiene
阶段 7:完整性与版本卫生
This pass reads the manifest and lockfile, and applies even when you can't see the package source. Flag:
- Floating versions — ,
^,~,*, or alatest/gittarball dependency. A range means "whatever the registry serves next," including a compromised patch. Pin exact versions.http(s) - No committed lockfile, or a lockfile not enforced in CI. The lockfile pins the whole transitive tree with integrity hashes; /
npm cirefuses to mutate it. Without it, every install is a fresh roll of the dice.--frozen-lockfile - A lockfile entry with a tarball URL but no hash — functionally
integrity, and the entry the registry can't verify. Audit for empty/missinglatestand off-registryintegrityURLs.resolved - Manifest ↔ lockfile drift, a suspicious version jump (a long-stable package leaping versions), a brand-new or recently-published version (no cooldown), and missing provenance where the ecosystem supports it. The fixes — cooldown / ,
min-release-age, OSV — are in the closing section andnpm audit signatures.references/patterns.md
此阶段读取清单和锁文件,即使无法查看软件包源代码也应执行。应标记以下情况:
- 浮动版本——、
^、~、*,或使用latest/gittarball 的依赖。范围版本意味着“registry 下次提供什么都可以”,其中可能包括已被攻陷的补丁版本。应固定精确版本。http(s) - 未提交锁文件,或 CI 未强制使用锁文件。锁文件通过 integrity 哈希固定整个传递依赖树;/
npm ci会拒绝修改锁文件。没有锁文件时,每次安装都像重新掷一次骰子。--frozen-lockfile - 锁文件条目包含 tarball URL,却没有 哈希——实际上等同于
integrity,而且 registry 无法验证该条目。应检查空缺或缺失的latest,以及指向非 registry 的integrityURL。resolved - 清单与锁文件不一致、可疑的版本跃升、长期稳定的软件包突然跨越多个版本、全新或近期发布的版本(没有冷却期),以及在生态系统支持 provenance 时缺少 provenance。冷却期 / 、
min-release-age、OSV 等修复方案将在文末章节和npm audit signatures中介绍。references/patterns.md
Finding format
发现项格式
Report each finding in this structure. Group by severity, P0 first.
[P0] malicious-install-script in package.json:14 (dependency: innocent-looking-util@1.2.4)
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.If the user pastes a manifest or snippet without a path, refer to "the manifest" / "the package" and use line numbers within the snippet. Always name the package and version, the pass that fired, and where the install or runtime context exposes secrets — that is the proof the dependency is dangerous and the spec for the fix.
按照以下结构报告每项发现。按严重程度分组,P0 排在最前。
[P0] malicious-install-script in package.json:14 (dependency: innocent-looking-util@1.2.4)
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.如果用户粘贴了没有路径的清单或代码片段,请使用“该清单”/“该软件包”指代,并使用片段内的行号。始终注明软件包及其版本、命中的阶段,以及安装或运行上下文暴露密钥的位置——这些信息既证明该依赖具有危险性,也构成具体修复方案的实施规范。
Severity scale
严重程度分级
- P0 — confirmed malicious behavior reachable now: an install script that harvests and exfiltrates, a known-compromised version (campaign IOC match), worm self-propagation, fetch-then-execute of remote code, or a destructive payload. Do not install; rip it out and rotate exposed credentials.
- P1 — high-risk and unvetted: an unexplained install script doing network/filesystem/obfuscated work, a typosquat / slopsquat / dependency-confusion candidate of a real dependency, an unscoped internal-looking name resolvable from a public registry, or a brand-new low-reputation package pulled into a sensitive path. Block merge until confirmed.
- P2 — hygiene gaps that widen blast radius: floating version ranges, a missing or unenforced lockfile, lockfile entries without integrity hashes, no cooldown, install scripts not disabled in CI, no provenance verification. Fix in the normal review cycle.
- P3 — defense-in-depth: no SBOM, no OpenSSF Scorecard signal, no build-script allowlist, missing dependency-review automation on PRs.
- P0——当前即可触发的已确认恶意行为:收集并外泄数据的安装脚本、命中攻击活动 IOC 的已知失陷版本、蠕虫式自我传播、下载并执行远程代码,或破坏性载荷。不要安装;立即移除,并轮换已暴露的凭据。
- P1——高风险且尚未验证:执行网络、文件系统或混淆相关操作但原因不明的安装脚本;真实依赖的仿冒包、AI 幻觉抢注或依赖混淆候选;看似内部用途的非 scoped 名称却能从公共 registry 解析;或新出现且信誉较低的包被引入敏感路径。在确认之前阻止合并。
- P2——扩大影响范围的卫生问题:浮动版本范围、缺失或未强制使用的锁文件、缺少 integrity 哈希的锁文件条目、没有冷却期、CI 未禁用安装脚本,以及缺少 provenance 验证。在正常审查周期中修复。
- P3——纵深防御:没有 SBOM、没有 OpenSSF Scorecard 信号、没有构建脚本允许列表,或 PR 中缺少依赖审查自动化。
When the dependencies look fine
当依赖看起来没有问题时
After all passes, if nothing fires:
- Say so explicitly, and state what you verified: the ecosystem and files from Pass 0, the added/changed packages reviewed, that no install scripts (or only benign platform-build ones) are present, no obfuscation or credential/exfiltration patterns in the source you could see, and that versions are pinned with a committed, integrity-bearing lockfile.
- Note what a static read cannot confirm: the published tarball may differ from the source repo (the registry is the trust boundary, not GitHub); transitive dependencies you didn't open; runtime-only behavior; whether the version on the registry right now matches what you reviewed; and reputation, age, and provenance, which need a registry/OSV query you didn't run.
- Recommend the confirming commands (the user runs these against public data — the skill itself stays offline): (provenance),
npm audit signatures(known-malicious/CVE), for a JS/TS projectosv-scanner --lockfile=...(the OWASP local-first lockfile scanner — OSV matching across npm/pnpm/yarn/bun with direct-vs-transitive visibility, copy-and-runcve-lite-cliremediation, and an--fixadvisory DB; matches known-advisory vulns, so it complements rather than replaces the behavioral passes above),--offline(recency, for a cooldown decision), and a Socket/Snyk/Aikido behavioral check. Plus a cooldown (npm view <pkg> time/min-release-age, 1–7 days) so a freshly published compromise gets caught by the community before it lands. WalkminimumReleaseAgefor a full audit.references/checklist.md
A clean static read does not prove the installed artifact is safe.
完成所有阶段后,如果没有发现任何风险:
- 明确说明这一点,并陈述已验证的内容:阶段 0 中确定的生态系统和文件、审查过的新增/变更软件包、不存在安装脚本(或仅存在良性的平台构建脚本)、在可见源代码中不存在混淆或凭据/外泄模式,以及版本已通过带 integrity 的已提交锁文件固定。
- 说明静态读取无法确认的事项:已发布的 tarball 可能与源代码仓库不同(registry 才是信任边界,而不是 GitHub);未打开的传递依赖;仅在运行时出现的行为;registry 上当前的版本是否与审查版本一致;以及信誉、发布时间和 provenance——这些信息需要查询 registry/OSV,而本次并未执行查询。
- 推荐以下确认命令,由用户针对公共数据运行——skill 本身保持离线:(provenance)、
npm audit signatures(已知恶意软件/CVE);对于 JS/TS 项目,可使用osv-scanner --lockfile=...(OWASP 的本地优先锁文件扫描器——支持在 npm/pnpm/yarn/bun 中进行 OSV 匹配,区分直接与传递依赖,可复制运行并使用cve-lite-cli修复,还提供--fix漏洞数据库;它匹配的是已知公告漏洞,因此只能作为上述行为检查的补充,无法替代它们);使用--offline检查发布时间,以决定是否采用冷却期;以及使用 Socket/Snyk/Aikido 进行行为检查。此外,应设置冷却期(npm view <pkg> time/min-release-age,1–7 天),让新发布的失陷版本有机会先被社区发现,再进入项目。完整的审计流程请参阅minimumReleaseAge。references/checklist.md
静态审查结果干净,并不能证明最终安装的制品是安全的。
Reference files
参考文件
- — per-ecosystem facts you need to judge a dependency: where install hooks live, how to pin and lock, the integrity/hash mechanism, namespace/scoping support (so you can call dependency-confusion exposure), the provenance mechanism, and the exact offline-and-registry verification commands — for npm/pnpm/yarn, PyPI, Go, Cargo, RubyGems, Maven/Gradle, NuGet, and Composer.
references/ecosystems.md - — "I want to add or update a dependency safely" recipes: cooldown windows, lockfile +
references/patterns.md,npm ciwith an allowlist, scoped names + registry pinning against dependency confusion, provenance/Trusted-Publisher verification, exact pinning, and isolating CI secrets from install scripts. Read it when proposing the fix.ignore-scripts - — a flat per-dependency / per-PR-diff / per-repo / per-org checklist with a triage order, plus an appendix of known-campaign markers (Shai-Hulud 1/2/3, nx
references/checklist.md, the binding.gyp worm, thes1ngularity/chalkcompromise). Use it for a full audit, for scanning many dependencies at once, and to match concrete IOCs.debug
- ——判断依赖时所需的各生态系统信息:安装钩子的位置、固定版本与锁文件的方法、完整性/哈希机制、命名空间/scoping 支持(用于判断依赖混淆暴露)、provenance 机制,以及各生态系统的准确离线和 registry 验证命令——涵盖 npm/pnpm/yarn、PyPI、Go、Cargo、RubyGems、Maven/Gradle、NuGet 和 Composer。
references/ecosystems.md - ——“我想安全地添加或更新依赖”的操作方案:冷却窗口、锁文件与
references/patterns.md、使用允许列表配合npm ci、使用 scoped 名称和 registry 固定防止依赖混淆、provenance/Trusted Publisher 验证、精确版本固定,以及隔离安装脚本与 CI 密钥。提出修复方案时应参阅此文件。ignore-scripts - ——按依赖、PR 差异、仓库和组织组织的扁平检查清单,包含分诊顺序;附录还列出了已知攻击活动标记(Shai-Hulud 1/2/3、nx
references/checklist.md、binding.gyp 蠕虫、s1ngularity/chalk失陷事件)。完整审计、批量扫描多个依赖以及匹配具体 IOC 时应使用此文件。debug
What this skill won't do
本 skill 不会执行的操作
- It won't install, run, or otherwise execute the package (even with ). It reads manifests, lockfiles, and any source already on disk. For runtime confirmation, pair it with a sandboxed install and behavioral monitoring (Socket, Datadog SCFW, a network-isolated container) — the dynamic complement to this static review.
--ignore-scripts - It won't silently phone home. Live reputation, recency, and provenance need a registry/OSV query; this skill hands you the exact commands to run yourself rather than shipping your dependency list anywhere.
- It won't replace a continuous SCA pipeline (OSV-Scanner, Snyk, Socket, Dependabot/Renovate with cooldown). It is the pre-install read that catches what a manifest-only scanner misses — an install script's intent — and the gate when you're about to add code from someone you've never met.
- It won't wave a package through because it's popular or has millions of downloads. Popular packages get compromised; that is the entire threat model. An unexplained install script in is more dangerous than one in a package nobody uses.
left-pad
- 不会安装、运行或以其他方式执行软件包,即使使用 也不例外。它只读取清单、锁文件以及磁盘上已有的任何源代码。若需进行运行时确认,可将其与沙箱安装和行为监控配合使用(Socket、Datadog SCFW、网络隔离容器)——这是对静态审查的动态补充。
--ignore-scripts - 不会静默回连外部服务。实时信誉、发布时间和 provenance 需要查询 registry/OSV;本 skill 会提供准确的命令供你自行运行,而不会把你的依赖列表发送到任何地方。
- 不会替代持续运行的 SCA pipeline(OSV-Scanner、Snyk、Socket、带冷却期的 Dependabot/Renovate)。它属于安装前审查,能够发现仅扫描清单的工具所遗漏的内容——例如安装脚本的意图——并作为引入陌生人编写代码前的关卡。
- 不会因为软件包热门或拥有数百万次下载就放行。热门软件包同样会失陷,这正是威胁模型的核心。中一个原因不明的安装脚本,可能比一个无人使用的软件包中的同类脚本更危险。
left-pad