Security skill

Supply-chain hardening

Install-time cooldowns for npm and bun, plus a sandboxed pre-install scan when the cooldown has to be bypassed. Layered defense against the npm/bun maintainer-compromise pattern that powered Mini Shai-Hulud.

When to use

The attack this stops

Mini Shai-Hulud TanStack attack, 2026-05-11. 84 versions across 42 @tanstack/* packages compromised via OIDC token theft from an orphan-commit workflow run. The payload exfiltrated AWS, GCP, Vault, GitHub, and SSH credentials via a prepare script on a github:-resolved optional dependency.

A 7-day install-time cooldown plus --ignore-scripts on every cooldown bypass would have blocked the entire attack on a stock laptop with no human in the loop. This skill makes that posture the default.

Three layers of defense

Layer 1

Install-time cooldown

Only install versions older than N days (default 7). By the time the cooldown expires, the security community has almost always flagged a compromised version and the registry has yanked it. This is the primary defense.

Layer 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 inside bwrap/firejail/unshare with no network and no access to ~/.ssh, ~/.aws, ~/.npmrc, or your pass-store.

Layer 3

--ignore-scripts on every bypass

Postinstall is the #1 attack vector. Skip lifecycle scripts on every cooldown-bypass install. Native modules that legitimately need postinstall can have the script run manually after a human-readable review.

Configure the cooldown

Verified config keys (npm v11+ and bun 1.3+):

Manager File Key Units
npm ~/.npmrc min-release-age days
bun ~/.bunfig.toml [install] minimumReleaseAge seconds

# ~/.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. Verify with 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.

What the scan checks for

Each check is backed by a real attack signature:

Check Diagnostic of Severity
optionalDependencies or dependencies containing github:/git+ URLs Mini Shai-Hulud-style payload delivery Red
Large JS file at package root not referenced by main/module/exports/bin/files Planted payload (router_init.js) Red
Unpacked size >3x prior stable version Bulk payload smuggling Red
fileCount delta of 1–4 with >2x size jump Single planted file Red
preinstall/install/postinstall/prepare scripts present Lifecycle-script attack vector Yellow
JS referencing .ssh/, .aws/, .npmrc, GITHUB_TOKEN, kube config Credential exfiltration Yellow
Version flagged deprecated with "security"/"compromised"/"malicious" wording Maintainer/registry yank Red
OSV.dev returns known vulnerabilities for the candidate version Disclosed CVE Red

The /security-toolkit:hotpatch command

Bundled with this skill in the security-toolkit plugin: a slash command that wraps the scan workflow.

# Scan a registry tarball before installing

/security-toolkit:hotpatch lodash@4.17.21

# Scan a local .tgz

/security-toolkit:hotpatch ./suspicious.tgz

The command tries the local hotpatch.example.sh reference implementation first and falls back to an in-conversation scan when no script is present. A bundled synthetic Mini Shai-Hulud fixture (test-fixtures/fake-mini-shai.tgz) lets you self-test the heuristics — --self-test against it should report 2 red, 1 yellow, SELF-TEST PASSED.

Threat model

Defends against

  • Maintainer account compromise (npm token theft)
  • CI/CD pipeline hijack (Mini Shai-Hulud, valid OIDC tokens)
  • Typosquatting when paired with lockfile review
  • Postinstall payload execution
  • Drive-by npm install of a brand-new transitive

Doesn't defend against

  • Targeted attack tailored to wait out the cooldown
  • Compromise of a transitive already pinned in a lockfile
  • Malicious code in dev dependencies you authored
  • Runtime supply-chain attacks (CDN-loaded code)
  • Compromise of the registry itself (rare; out of scope)

Resources

Installation

# Recommended: install the security-toolkit plugin

/plugin marketplace add jamditis/claude-skills-journalism

/plugin install security-toolkit@claude-skills-journalism

# Or copy just this skill from the plugin tree

git clone https://github.com/jamditis/claude-skills-journalism.git

cp -r claude-skills-journalism/security-toolkit/skills/supply-chain-hardening ~/.claude/skills/

Or browse this skill in the GitHub repository.

Layered defense, no human in the loop

Cooldown plus sandbox plus --ignore-scripts would have blocked Mini Shai-Hulud on a stock laptop.

View on GitHub