supply-chain-hardening — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited supply-chain-hardening (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Defends a journalism toolchain against the dominant npm/bun supply-chain attack pattern: a maintainer account or CI pipeline is compromised, a malicious version ships, and machines install it before anyone notices. Recent example: the Mini Shai-Hulud TanStack attack (2026-05-11) compromised 84 versions across 42 @tanstack/* packages and exfiltrated AWS / GCP / Vault / GitHub / SSH credentials via a postinstall script.
The defense is layered and intentionally simple:
bwrap/firejail/unshare so a malicious package can't escape the inspection.These three together would have blocked the Mini Shai-Hulud TanStack attack on a stock laptop with no human in the loop.
Verified config keys (npm v11+ and bun 1.3+):
| Manager | File | Key | Units | Exclusion key |
|---|---|---|---|---|
| npm | ~/.npmrc (or project .npmrc) | min-release-age | days | none yet, proposed in npm/cli#8994 |
| bun | ~/.bunfig.toml (or project bunfig.toml) | [install] minimumReleaseAge | seconds | [install] minimumReleaseAgeExcludes = [] (exact names, no globs) |
Minimal config:
# ~/.npmrc
min-release-age=7# ~/.bunfig.toml
[install]
minimumReleaseAge = 604800 # 7 days
minimumReleaseAgeExcludes = []Requires npm 11+. Older npm silently ignores unknown keys, so the config looks correct but does nothing. Check with npm --version and npm config get min-release-age (should echo 7, not null).
When the cooldown blocks an install you actually want:
npm install <pkg>@<version> --min-release-age=0 --ignore-scripts
bun add <pkg>@<version> --minimum-release-age=0 --ignore-scriptsThe bun add --minimum-release-age=0 CLI flag works in 1.3+ even though the docs don't list it, it follows bun's bunfig key → kebab-case flag convention.
Always pair the bypass with `--ignore-scripts`. Postinstall is the most common payload-execution path in supply-chain malware (Mini Shai-Hulud, event-stream, ua-parser-js, coa, all used it). Native modules that legitimately need postinstall can have the script run manually after a human-readable review:
(cd node_modules/<pkg> && cat package.json | jq .scripts) # eyeball it
(cd node_modules/<pkg> && npm run postinstall) # run if it checks outThe scan is for the dangerous moment: you've decided to bypass the cooldown and need a sanity check. The skill ships a reference script (scripts/hotpatch.example.sh) implementing the heuristics. Adapt it to your machine, Bash assumes bwrap (Linux); macOS users substitute sandbox-exec or skip the sandbox layer with the trade-off documented.
Static checks the scan should perform (each backed by a real attack):
| Check | Diagnostic of | Severity |
|---|---|---|
optionalDependencies / dependencies containing github: or git+ URLs | Mini Shai-Hulud (delivered payload via github:tanstack/router#<sha> ref) | RED |
Large JS file at package root not referenced by main/module/exports/bin/files | Planted payload pattern (router_init.js in Mini Shai-Hulud) | RED |
| Unpacked size >3x the prior stable version | Bulk payload smuggling | RED |
fileCount delta of 1–4 paired with >2x size jump | Single planted file | RED |
preinstall/install/postinstall/prepare scripts present | Lifecycle-script attack vector (event-stream, ua-parser-js, etc.) | YELLOW |
JS files referencing .ssh/, .aws/, .npmrc, GITHUB_TOKEN, AWS_SECRET, kube config | Credential exfiltration | YELLOW |
Version flagged deprecated in npm registry with "security"/"compromised"/"malicious" wording | Maintainer/registry yank | RED |
OSV.dev returns known vulnerabilities for <pkg>@<version> | Disclosed CVE | RED (severity-dependent) |
Why prerelease versions are skipped from the size-delta baseline: dev/beta/rc versions have wildly different sizes than stable releases and produce false positives.
Be honest about the limits with whoever you're configuring this for:
<pkg> you install can pull in a compromised transitive. Defenses: scan against the resolved tree (npm audit, osv-scanner), and keep the cooldown active globally so transitive resolution also waits.npm ci will install it. Mitigation: scan lockfiles in CI with osv-scanner --lockfile=package-lock.json.npm audit, osv-scanner, manual review of recently-published deps in your tree).npm --version. If older, upgrade (sudo npm i -g npm@latest or tarball-swap if self-upgrade races).~/.npmrc and ~/.bunfig.toml with the config above.npm config get min-release-age returns 7. cat ~/.bunfig.toml shows the [install] block.scripts/hotpatch.example.sh to a user-owned tools directory. Make it executable. Run ./hotpatch.sh --self-test against the shipped synthetic Mini Shai-Hulud fixture to confirm the heuristics fire.| Defends against | Doesn't defend against |
|---|---|
| Maintainer account compromise (npm token theft) | Targeted attack tailored to wait through the cooldown |
| CI/CD pipeline hijack (Mini Shai-Hulud, valid OIDC tokens, SLSA-attested malice) | Compromise of a transitive dep already pinned in a lockfile |
Typosquatting (lookalike package names), when paired with npm pkg fix and lockfile review | Malicious code in your own dev dependencies that you authored |
Postinstall payload execution (cooldown + --ignore-scripts = belt and suspenders) | Runtime supply-chain attacks (e.g., dynamic loading of bad code from a CDN) |
Drive-by npm install of a brand-new transitive | Compromise of the registry itself (very rare; out of scope) |
min-release-age config: <https://docs.npmjs.com/cli/v11/using-npm/config>minimumReleaseAge config: <https://bun.com/docs/runtime/bunfig>~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.