agentleFS
GoldenWing-360GoldenWing-360/claude-security-skills/dependency-supply-chain/SKILL.md

dependency-supply-chain

Skill16 starsChanged 6 months agoMITView on GitHub

Audit and defend against malicious dependencies in npm, pnpm, PyPI, and similar ecosystems. Covers lockfile hygiene, the limits of npm audit, behavior-level scanning with socket.dev, postinstall script review, typosquat and slopsquat detection, and minimum-permission CI runs. Invoke when adding a new dependency, after a supply-chain incident, or as periodic audit.

  • Reads credentials
  • Installs packages

1 other project uses a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

6 rules. About 0 are common, 3 are distinctive, and 3 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈3

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 3

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    After a publicized supply-chain incident affecting your ecosystem

    line 15
  2. Very specific:

    Commit the lockfile: Always: package-lock.json, pnpm-lock.yaml, yarn.lock, poetry.lock, Pipfile.lock, go.sum.

    line 36
  3. Very specific:

    Pin direct dependencies tightly: when the dep is small / new / sketchy.

    line 39
  4. Very specific:

    Treat npm audit as the first line of defense, not the only one.

    line 64
  5. Distinctive:

    When adding a new dep, especially a small or unfamiliar one, take 60 seconds first.

    line 79
  6. Distinctive:

    Don't let your install run with prod credentials sitting in env.

    line 151

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Dependency / Supply-Chain Security

The supply chain is the part of your stack you trust by default and rarely review. npm, PyPI, RubyGems, Maven, Go modules — they have all hosted malicious packages, typosquats, and compromised maintainer accounts at scale.

This skill is the working developer's defense — pragmatic checks that fit in a normal workflow, not a software bill-of-materials regulatory program.

When to invoke

  • Adding a new dependency to a project (especially a small / new / single-maintainer one)
  • After a publicized supply-chain incident affecting your ecosystem
  • Quarterly audit of package.json / requirements.txt / go.mod
  • Inheriting a project with a large node_modules and no obvious hygiene
  • CI/CD just leaked a secret — start here to find which dep grabbed it

The threat model

What can a malicious dependency actually do?

  • Read environment variables / files during install (postinstall script)
  • Read environment variables / files at runtime (any code path)
  • Exfiltrate via outbound HTTPS to any host
  • Modify other dependencies (link-stage tampering, build-script monkey-patching)
  • Hide payloads in transitive deps so your package.json looks clean

You cannot inspect every line of every dependency. You can raise the cost of attacks and detect them when they happen.

Lockfile hygiene

A lockfile is the only thing standing between "I added one dep" and "my install grabbed 1,200 transitive deps any of which can be swapped tomorrow".

  • Commit the lockfile. Always: package-lock.json, pnpm-lock.yaml, yarn.lock, poetry.lock, Pipfile.lock, go.sum. No exceptions.
  • npm ci (or pnpm install --frozen-lockfile) in CI, not npm install. install can mutate the lockfile silently; ci errors if the lockfile and package.json disagree.
  • Lockfile drift alerts. A PR that touches package-lock.json but not package.json, or vice versa, deserves a review comment. Add a CI check.
  • Pin direct dependencies tightly when the dep is small / new / sketchy. "left-pad": "1.3.0" rather than "left-pad": "^1.3.0". The caret invites unattended updates.

For high-stakes apps consider npm install --omit=dev in production builds — dev deps are a huge attack surface that production runtime does not need.

Built-in audit — useful, limited

npm audit --omit=dev --audit-level=high
pnpm audit --audit-level=high
yarn npm audit
pip-audit

What npm audit does well:

  • Catches known CVEs against installed versions
  • Fast, no extra tooling
  • Good signal-to-noise at --audit-level=high or critical

What it misses:

  • Malicious-by-design packages (the package is doing what it's "supposed to do", which happens to be evil)
  • Unreported issues — many compromised packages are not in any CVE feed
  • Behavior-level analysis — audit doesn't know that this package suddenly started making outbound HTTPS calls

Treat npm audit as the first line of defense, not the only one.

Behavior-level scanning

Tools that look at what a package does, not just which CVE it has:

  • socket.dev — flags new packages, packages that gained network/install-script behavior, typosquat-shaped names. GitHub integration that comments on PRs. Free tier covers most personal use.
  • deps.dev (Google) — package metadata, transitive graph, advisory feed
  • OSV-Scanner — open-source scanner against the OSV database, broader than npm audit
  • Snyk / GitHub Dependabot security alerts — overlap with audit but include curated advisories

A reasonable stack: GitHub Dependabot (free, native) + socket.dev (free, behavior-aware) + npm audit in CI.

Reviewing a new dependency before npm install

When adding a new dep, especially a small or unfamiliar one, take 60 seconds first.

# What is this package, who maintains it, what's the install footprint?
npm view <pkg>
npm view <pkg> dependencies         # transitive count
npm view <pkg> repository.url        # is there a public repo?
npm view <pkg> maintainers           # who can publish a new version?
npm view <pkg> time                  # when was each version published?

# How popular is it? (huge install counts don't prove safety, but obscure single-digit downloads warrant scrutiny)
curl -s "https://api.npmjs.org/downloads/point/last-week/<pkg>" | jq

# Red flags in the publish history
npm view <pkg> time --json | jq 'to_entries | sort_by(.value) | reverse | .[0:5]'

Red flags:

  • Single maintainer with no other notable packages + the package handles auth/crypto/payments
  • Recently transferred ownership (new maintainer's other packages look unrelated)
  • Sudden version jump with a vague changelog ("bug fixes" and a 1k-line diff)
  • No public repo, no homepage, no README
  • Typosquat-shaped name — lodash-utils vs lodash, colors-js vs colors
  • Postinstall script (see next section)
  • Network calls during install (you can sandbox install to detect — see CI section)

Postinstall scripts

postinstall, preinstall, install, prepare scripts run on npm install. They are the most common malware delivery vector.

# Enumerate every package with an install-time script
find node_modules -name package.json -not -path '*/node_modules/*/node_modules/*' \
  -exec grep -l '"postinstall"\|"preinstall"\|"prepare"' {} \; \
  | xargs -I {} dirname {} | sed 's|node_modules/||'

# Block ALL install scripts globally — most packages do not need them
npm config set ignore-scripts true
# Or per-install
npm install --ignore-scripts

--ignore-scripts breaks some legitimate native-module builds (node-sass, sharp, better-sqlite3). Allow scripts only for the specific packages that need them — yarn 2+ and pnpm have allowlists.

In pnpm:

# .npmrc
ignore-scripts=true

# package.json
"pnpm": {
  "onlyBuiltDependencies": ["sharp", "better-sqlite3"]
}

Typosquat detection

Common substitutions: i ↔ l ↔ 1, 0 ↔ o, missing/extra dashes, plural/singular. Run:

# Cheap typosquat heuristic — flag packages with very similar names to well-known ones
npm ls --depth=0 --json | jq -r '.dependencies | keys[]' | while read p; do
  echo "$p"  # human review or feed to socket.dev
done

socket.dev / npq do this systematically; for a manual pass, just eyeball your direct deps with cat package.json | jq '.dependencies | keys'.

CI / build sandbox

Don't let your install run with prod credentials sitting in env.

# GitHub Actions
- name: Install dependencies
  run: npm ci --ignore-scripts
  env: {}                 # no env vars passed in
# Then run the few allowed install scripts intentionally
- name: Run build scripts for native deps
  run: npm rebuild sharp better-sqlite3

See github-actions-security for the broader CI story (pinned action SHAs, OIDC, secret scoping).

Audit node_modules for an inherited project

# How many packages did this single npm install actually install?
find node_modules -name package.json -not -path '*/node_modules/*/node_modules/*' | wc -l

# Largest packages — these are where you'd hide a payload
du -sh node_modules/*/  | sort -h | tail -20

# Packages with install scripts (as above)

# Packages with .bin executables
find node_modules/.bin -type l -o -type f | head -30

Cross-check against package.json direct deps. Anything in node_modules you cannot trace back to a direct or expected-transitive dep is worth investigating.

What to do when a dependency is compromised

You wake up to "package X version 2.4.5 was compromised, contains a credential stealer".

  1. Determine if you shipped it.
    # Did you have it installed at the bad version?
    git log --all -p -- package-lock.json | grep -A2 -B2 'package-x.*2\.4\.5'
  2. Assume credentials accessed during install/run are burned. That includes .env files readable by the user that ran npm install, plus anything in process env at runtime.
  3. Rotate every credential that was in scope. Order per secret-hygiene.
  4. Pin to a known-clean version, or remove the package entirely if no clean version exists.
  5. Audit package-lock.json for the transitive surface — the compromised package may be a transitive dep you didn't add directly.
  6. Check CI logs for unexplained outbound traffic during recent builds.
  7. Add the package + version range to a permanent denylist in your CI (whatever scanner you use) so you don't accidentally re-add it.

Periodic audit checklist

Once a quarter:

  • Lockfile present and committed for every project
  • CI uses npm ci / --frozen-lockfile, not loose install
  • Dependabot (or equivalent) enabled, PRs reviewed not auto-merged for major versions
  • npm audit --audit-level=high passes; exceptions documented
  • socket.dev or equivalent reviews new dependency PRs
  • Install scripts allowlisted; defaults to ignore
  • No env vars passed to npm ci step in CI
  • Direct dependencies you don't recognize have been investigated
  • A known-vulnerable package list is maintained for the project's ecosystem

What this skill will not do

  • Replace a formal Software Bill of Materials (SBOM) program for regulated environments
  • Provide payloads or attack patterns to publish malicious packages
  • Recommend installing packages without lockfiles "for now"

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.