Skip to content
Content
Skill

/supply-chain-hardening

Configure install-time cooldowns for npm/bun (minimum release age) and run a sandboxed pre-install scan when the cooldown has to be bypassed. Use when the user asks about supply-chain attacks, npm/bun security, "minimum release age", a "cooldown" for installs, hardening against

From plugin
claude-skills-journalism
35957 skills1 agent22 commands1 hook
Install
$ npx -y skills add jamditis/claude-skills-journalism --skill supply-chain-hardening --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/supply-chain-hardening

Context preview

The summary Claude sees to decide when to auto-load this skill.

Configure install-time cooldowns for npm/bun (minimum release age) and run a sandboxed pre-install scan when the cooldown has to be bypassed. Use when the user asks about supply-chain attacks, npm/bun security, "minimum release age", a "cooldown" for installs, hardening against

SKILL.md

supply-chain-hardening.SKILL.md
name: supply-chain-hardening
description: Configure install-time cooldowns for npm/bun (minimum release age) and run a sandboxed pre-install scan when the cooldown has to be bypassed. Use when the user asks about supply-chain attacks, npm/bun security, "minimum release age", a "cooldown" for installs, hardening against Shai-Hulud-class worms, or how to safely install a package that was just published. Also use after any recent supply-chain incident in the npm ecosystem.

Supply-chain hardening

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:

1. **Install-time cooldown** — only install package versions older than N days (default 7). This is the primary defense. By the time the cooldown expires, the security community has almost always flagged a compromised version and the registry has yanked it. 2. **Sandboxed pre-install scan** — when the cooldown has to be bypassed (CVE patch, fresh dep, urgent install), run the candidate tarball through a static-analysis scan that looks for the diagnostic signatures of supply-chain malware. The scan runs inside `bwrap`/`firejail`/`unshare` so a malicious package can't escape the inspection. 3. **`--ignore-scripts` at install** — postinstall is the #1 attack vector. Skip lifecycle scripts on every cooldown-bypass install.

These three together would have blocked the Mini Shai-Hulud TanStack attack on a stock laptop with no human in the loop.

Configure the cooldown

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](https://github.com/npm/cli/issues/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`).

Per-command bypass

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-scripts

The `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 out

When to scan before bypassing

The 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.

What the cooldown does *not* catch

Be honest about the limits with whoever you're configuring this for:

  • **Old packages with new malicious versions still in the cooldown window are blocked**, but if the bad version *also* passes the cooldown (rare but possible — a compromise that goes >7 days undetected), the cooldown alone won't help. The scan catches most of those.
  • **Transitive deps**. A clean `<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`** against an existing lockfile. The cooldown applies during *resolution*, not installation of already-pinned versions. If your lockfile pins a compromised versio
Read more
Ships withclaude-skills-journalism

A collection of Agent Skills for journalists, researchers, academics, media professionals, and communications practitioners. The same repository serves Claude Code and Codex while keeping Claude-only commands, agents, and hooks clearly labeled.

Get the whole plugin

Other skills on claude-skills-journalism.