{"id":50377576,"url":"https://github.com/jlevy/supply-chain-hardening","last_synced_at":"2026-05-30T10:30:41.064Z","repository":{"id":357483290,"uuid":"1236897747","full_name":"jlevy/supply-chain-hardening","owner":"jlevy","description":"Agent guidebook for hardening against supply chain attacks on npm/PyPi/crates/go ecosystems","archived":false,"fork":false,"pushed_at":"2026-05-23T22:26:30.000Z","size":477,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-24T00:18:46.200Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jlevy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-05-12T17:18:09.000Z","updated_at":"2026-05-23T22:26:35.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/jlevy/supply-chain-hardening","commit_stats":null,"previous_names":["jlevy/supply-chain-hardening-guidebook","jlevy/supply-chain-hardening"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/jlevy/supply-chain-hardening","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jlevy%2Fsupply-chain-hardening","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jlevy%2Fsupply-chain-hardening/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jlevy%2Fsupply-chain-hardening/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jlevy%2Fsupply-chain-hardening/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jlevy","download_url":"https://codeload.github.com/jlevy/supply-chain-hardening/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jlevy%2Fsupply-chain-hardening/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33689564,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-30T02:00:06.278Z","response_time":92,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-30T10:30:40.510Z","updated_at":"2026-05-30T10:30:41.046Z","avatar_url":"https://github.com/jlevy.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Supply Chain Hardening Guidebook\n\n**For AI agents and developers.** Concrete recipes, zero-dep audit scripts, and a\ncurated watch list of recent compromises across npm, PyPI, crates.io, and Go modules.\n\n**Author:** Joshua Levy (github.com/jlevy) with agent assistance\\\n**Last updated:** 2026-05-23\n\n## Quick Start\n\nRead the [Safety Note](#safety-note) before applying anything, and validate every recipe\nagainst the [Authoritative Sources](#authoritative-sources).\n\n### Which Path Do I Follow?\n\n- **Consumer-only repo** (you install dependencies, you do not publish packages): apply\n  the ecosystem [playbook](#harden-a-single-ecosystem), commit lockfiles, and add a CI\n  scanner gate.\n- **Repo that publishes packages or releases via GitHub Actions:** apply\n  [`guidelines/hardening-ci-cd.md`](guidelines/hardening-ci-cd.md) first, then the\n  ecosystem playbook. The minimum GitHub Actions defaults: top-level\n  `permissions: contents: read`; no `pull_request_target` workflow that checks out PR\n  head code; restore-only cache on PRs (and avoid implicit cache saves from setup\n  actions); SHA-pin actions; OIDC trusted publishing plus npm staged publishing; publish\n  job behind a GitHub Environment with required reviewers.\n- **Agent working in an untrusted repo:** follow\n  [`guidelines/untrusted-repo-first-run.md`](guidelines/untrusted-repo-first-run.md)\n  before any install / build / test / run command.\n- **Machine with publish tokens or production access:** enter Strict mode\n  ([`guidelines/strict-mode.md`](guidelines/strict-mode.md)).\n\n### Harden A Single Ecosystem\n\nPick the playbook for the ecosystem you use.\nEach is a copy-pasteable Ten-Minute Setup.\n\n| Ecosystem | Playbook |\n| --- | --- |\n| **npm / Node.js** | [guidelines/hardening-npm.md](guidelines/hardening-npm.md) |\n| **PyPI / Python** | [guidelines/hardening-pypi.md](guidelines/hardening-pypi.md) |\n| **crates.io / Rust** | [guidelines/hardening-crates.md](guidelines/hardening-crates.md) |\n| **Go modules** | [guidelines/hardening-go.md](guidelines/hardening-go.md) |\n| **CI/CD and publish pipeline** (cross-ecosystem) | [guidelines/hardening-ci-cd.md](guidelines/hardening-ci-cd.md) |\n\nThe four per-ecosystem playbooks harden the **install** side.\nIf you publish packages, or your repo releases via GitHub Actions, also apply the\ncross-ecosystem [CI/CD playbook](guidelines/hardening-ci-cd.md): most 2026 incidents\n(TanStack, @antv, Megalodon, `durabletask`) compromised the publish pipeline, not a\nconsumer.\n\n**Ecosystems not yet covered:** RubyGems / Bundler and Homebrew have no copy-pasteable\nplaybook here yet. The same methodology applies — commit `Gemfile.lock` and install with\n`bundle install --frozen`; use a committed `Brewfile` with `brew bundle`, and disable\nHomebrew auto-update (`HOMEBREW_NO_AUTO_UPDATE=1`) for reproducible installs; verify\nbefore upgrading — but neither has a native release-age gate, so treat them like Cargo\nand Go (pin, commit the lockfile, review before updating).\nAdding a full playbook follows\n[self-update-instructions.md](self-update-instructions.md) → “Adding A New Ecosystem”.\n\n### Harden All Ecosystems\n\nFor an agent or human walking through every ecosystem on a workstation, in order:\n\n1. **Inventory.** Identify which of npm, PyPI, crates.io, Go is installed and used.\n   Skip the rest.\n2. **Per ecosystem,** open the playbook above and:\n   1. Apply the Ten-Minute Setup verbatim, including shell-init and per-platform\n      variants.\n   2. Run the verification commands.\n      Confirm each control reports the expected value.\n   3. Run the “Compromise Assessment” commands once to baseline the current state.\n   4. Append an entry to the user’s `supply-chain-audit-log.md` (copy from\n      [`supply-chain-audit-log-template.md`](supply-chain-audit-log-template.md))\n      recording what was set and any hits found.\n3. **Cross-check installed packages** against\n   [compromised-packages.md](compromised-packages.md) for any `package@version` in the\n   watch list.\n4. **For npm specifically,** run an OSV-API scan against the global tree:\n   `uv run scripts/audit_npm.py`. The script reports `[MALICIOUS]` separately from\n   ordinary CVEs and has zero third-party dependencies; see\n   [scripts/README.md](scripts/README.md).\n5. **If any hit lands,** follow the “If You Have Hits” section in the relevant playbook\n   for credential rotation, downgrade, and post-incident steps.\n\nThe long-form companions live in [`research/`](research/): threat model, attack\ntimeline, per-shell setup detail, and severity assessment per ecosystem.\n\n### Drop A Reminder Into Your Own Codebase\n\n[`SUPPLY-CHAIN-SECURITY.md`](SUPPLY-CHAIN-SECURITY.md) is a self-contained, portable\nversion of the install rules (no newer than 14 days, no unthinking installs, audit after\nevery install, link back here for detail).\nCopy it to your own project root and reference it from your project’s `AGENTS.md` so any\nAI agent working in your codebase sees the rules before installing anything.\n\n## For AI Agents\n\nWhen the user asks you to harden, audit, or assess a package-manager supply chain:\n\n| User Intent | Action |\n| --- | --- |\n| “Harden my npm setup” | Apply [guidelines/hardening-npm.md](guidelines/hardening-npm.md). Verify with the listed config-get commands. Log to `supply-chain-audit-log.md`. |\n| “Harden my PyPI setup” | Apply [guidelines/hardening-pypi.md](guidelines/hardening-pypi.md). Verify, log. |\n| “Harden my Rust setup” | Apply [guidelines/hardening-crates.md](guidelines/hardening-crates.md). Verify, log. |\n| “Harden my Go setup” | Apply [guidelines/hardening-go.md](guidelines/hardening-go.md). Verify, log. |\n| “Harden my CI / release pipeline” or “We publish packages” | Apply [guidelines/hardening-ci-cd.md](guidelines/hardening-ci-cd.md): read-only PR caches, SHA-pinned actions, runner egress block, OIDC/staged publishing, provenance monitoring. |\n| “Harden everything on this machine” | Walk [Harden All Ecosystems](#harden-all-ecosystems) end to end. One audit-log entry per ecosystem. |\n| “I just installed X. Am I compromised?” | Start at [compromised-packages.md](compromised-packages.md). For npm, run `uv run scripts/audit_npm.py --packages \u003cpkg@ver\u003e`. For other ecosystems, `osv-scanner` per the playbook. Log findings. |\n| “Add a new ecosystem (RubyGems, NuGet, …)” | Follow [self-update-instructions.md](self-update-instructions.md) → “Adding A New Ecosystem”. Cite multiple [authoritative sources](#authoritative-sources). |\n| “Update the watch list with a new incident” | Follow [self-update-instructions.md](self-update-instructions.md) → “Updating `compromised-packages.md`”. Verify with at least two [Incident Reporting Feeds](#incident-reporting-feeds-free-public-two-source-verification). |\n\n[`AGENTS.md`](AGENTS.md) carries the same table plus a Safety Rule For Agents block, for\nIDEs and agents that auto-load that filename.\n\n## Safety Note\n\n\u003e [!WARNING]\n\u003e It is increasingly unsafe to trust even seemingly trustworthy packages or GitHub\n\u003e repos. Validate instructions before following them, and validate packages before\n\u003e installing them. Have your agent cross-check every recipe in this repo against the\n\u003e [Authoritative Sources](#authoritative-sources).\n\n## What This Repo Is (And Is Not)\n\n**This repo is** a methodology resource for agents and humans:\n\n- Per-ecosystem **hardening guides** (the four\n  [playbooks above](#harden-a-single-ecosystem)) with copy-pasteable shell and CI\n  configuration, plus a cross-ecosystem\n  [CI/CD and publish-pipeline guide](guidelines/hardening-ci-cd.md) for the GitHub\n  Actions and release-token vectors behind the 2026 worm campaigns.\n- Per-ecosystem **research docs** in [`research/`](research/) explaining the threat\n  model, attack mechanisms, and defensive trade-offs.\n- A **strict-mode reference** at\n  [`guidelines/strict-mode.md`](guidelines/strict-mode.md) for agents and high-risk\n  environments, plus an\n  [untrusted-repo sandbox procedure](guidelines/untrusted-repo-first-run.md) for the\n  first run of any third-party code.\n- A **curated watch list** at [`compromised-packages.md`](compromised-packages.md) for\n  spot-checking installed packages and recognising attack patterns by name.\n- A **zero-dependency audit script** at [`scripts/audit_npm.py`](scripts/audit_npm.py)\n  and an audit-log template at\n  [`supply-chain-audit-log-template.md`](supply-chain-audit-log-template.md).\n- A **self-update procedure** at\n  [`self-update-instructions.md`](self-update-instructions.md) so any human or agent\n  revisiting the repo months later can refresh it predictably.\n\n**This repo is not** a real-time feed of supply-chain compromises.\nFor that, use the [Authoritative Sources](#authoritative-sources).\nThe watch list is curated, not exhaustive: notable named incidents that defenders should\nrecognise, plus enough context to make the hardening guides concrete.\n\n## The Layered Model\n\nSupply-chain hardening is a stack of six layers.\nThis repo covers L1-L3 and L6 directly, names L5 with a concrete recipe, and points\nelsewhere for L4. Everything in the repo maps to one of these layers.\n\n| Layer | What | Where in this repo |\n| --- | --- | --- |\n| **L1** Developer defaults | Shell-init env vars (`UV_EXCLUDE_NEWER`, `NPM_CONFIG_BEFORE`, etc.) that harden every `install` from an interactive shell | The four per-ecosystem playbooks; [`SUPPLY-CHAIN-SECURITY.md`](SUPPLY-CHAIN-SECURITY.md) as the portable drop-in |\n| **L2** Project policy | Committed lockfiles, build-script allowlists, registry pins, workspace-level config | “Step 2” of each playbook; `pnpm-workspace.yaml`, `Cargo.lock`, `uv.lock`, `go.sum` |\n| **L3** CI enforcement | Hardening env vars inside CI runners; scanner jobs that fail merge on findings; publish-pipeline hardening (read-only PR caches, SHA-pinned actions, runner egress block, OIDC/staged publishing, provenance monitoring) | “CI Enforcement” section of each playbook; the cross-ecosystem [CI/CD playbook](guidelines/hardening-ci-cd.md) |\n| **L4** Org registry / proxy | Internal mirror with quarantine and delay policy (Artifactory, Nexus, Verdaccio, devpi) | **Out of scope for hands-on guidance.** Strongest team-level control; implementations vary by org. Use a controlled `GOPROXY` and crates.io vendoring for Go and Rust. |\n| **L5** Untrusted-repo sandbox | Container or namespace-isolated execution for the first run of any third-party repo | [`guidelines/untrusted-repo-first-run.md`](guidelines/untrusted-repo-first-run.md) |\n| **L6** Incident response | Per-incident credential rotation, persistence checks, downgrade, audit-log entry | “If You Have Hits” sections in each playbook; [`supply-chain-audit-log-template.md`](supply-chain-audit-log-template.md) |\n\nHow to read the stack:\n\n- **L1 alone** is enough for personal workstations and small teams against the\n  fast-yanked-incident class of attack.\n- **L1 + L2 + L3** is the minimum for any shared codebase: L1 protects the individual\n  developer, L2’s committed lockfile + L3’s CI gate close the gap when a peer skips L1.\n- **L4** is the strongest team-level control because it is the only layer that enforces\n  policy across every developer, agent, CI job, and tool that resolves packages.\n  If you can stand up a delayed internal mirror, do so.\n  This repo describes what the controls should enforce, not how to stand up the mirror.\n- **L5** is critical for AI agents and for anyone routinely cloning third-party repos:\n  install scripts, source builds, `build.rs`, proc-macros, and test files all execute\n  code with ambient credentials.\n- **L6** is the difference between “a malicious package landed on a developer machine”\n  and “a malicious package compromised production.”\n  Treat the audit log as the canonical record; do not rely on memory.\n\n[`guidelines/strict-mode.md`](guidelines/strict-mode.md) documents the Strict and\nEmergency-Exception modes that sit on top of the Balanced default; agents and high-risk\nenvironments should consult that file before installing anything.\n\n## Why The Hardening Pattern Is Stable Even When The Incident List Changes\n\nThe dominant pattern in the 2025-2026 wave is fast-yanked named incidents: malicious\npackage versions live for minutes to hours before researchers detect them and the\nmaintainer or registry yanks the bad release (qix, Shai-Hulud 1.0/2.0, Axios, TanStack,\nUltralytics, LiteLLM, Mini Shai-Hulud).\n\n**Core pattern:** delay newly-published versions where the package manager supports it;\notherwise prevent unintentional re-resolution, pin exact versions, verify checksums and\nadvisories, and require explicit human review for dependency updates.\n\n| Ecosystem | Native release-age gating | Primary protection |\n| --- | --- | --- |\n| npm / pnpm | yes (`NPM_CONFIG_BEFORE`, `MINIMUM_RELEASE_AGE` on pnpm 10.16+, `MIN_RELEASE_AGE` on npm 11.10+) | release-age delay + disabled install scripts + frozen lockfile |\n| PyPI (uv, pip 26.1+, poetry 2.4+, pdm) | yes (`UV_EXCLUDE_NEWER`, `PIP_UPLOADED_PRIOR_TO`, `solver.min-release-age`, `--exclude-newer`) | release-age delay + refuse sdist builds + frozen lockfile with hashes |\n| Cargo (crates.io) | no native release-age control | committed `Cargo.lock` + `--locked` + `cargo audit`/`deny`/`vet` |\n| Go modules | no native release-age control | committed `go.sum` + `go mod verify` + `govulncheck` + readonly module mode |\n\nFor Cargo and Go, “cool-off” can still be implemented through Renovate/Dependabot\npolicy, internal mirrors, or update wrappers, but it is not a flag the toolchain\nexposes. The playbooks translate the per-ecosystem pattern into copy-pasteable commands;\nthe methodology is what the repo is really about.\n\n**What this neutralises:** the fast-yanked named incidents above.\n**What it does not neutralise on its own:** long-lived compromises that survive past the\ncool-off window (BoltDB and `shopsprint/decimal` cached in the Go module proxy for ~3\nyears; ctx ATO published for ~10 days), lockfiles that already captured a malicious\nversion before the control was active, runtime payloads in wheels or proc-macros (and\n`require()`-time payloads like node-ipc) that execute on import or build rather than at\ninstall time, and **publish-pipeline compromises** where the malicious version ships\nfrom the legitimate maintainer’s own CI, sometimes carrying valid (forged) provenance,\nas in the May 2026 @antv worm.\nThose require additional controls: lockfile review, typo-resistance checks, the\nper-ecosystem build-time controls in the playbooks, and the publish-side controls in the\n[CI/CD playbook](guidelines/hardening-ci-cd.md) (OIDC trusted publishing, staged\npublishing, runner hardening, provenance monitoring).\n\n## The Default Policy: A 14-Day Cool-Off\n\n**Never install or upgrade to a package version less than 14 days old, unless a\ndocumented exception applies.** This is the single default this repo recommends across\nevery ecosystem.\nThe control differs by tool (the per-ecosystem playbooks have the exact,\nversion-specific recipes and verification):\n\n| Tool | 14-day control |\n| --- | --- |\n| npm (any) | `NPM_CONFIG_BEFORE=\u003cnow-minus-14d\u003e` |\n| npm 11.10+ | `NPM_CONFIG_MIN_RELEASE_AGE=14` (days) |\n| pnpm 10.16-10.x | `NPM_CONFIG_MINIMUM_RELEASE_AGE=20160` (minutes) |\n| pnpm 11+ | `minimumReleaseAge: 20160` in `pnpm-workspace.yaml` (pnpm 11 ignores `NPM_CONFIG_*`) |\n| uv | `UV_EXCLUDE_NEWER=\"14 days\"` |\n| pip 26.1+ | `PIP_UPLOADED_PRIOR_TO=\"P14D\"` |\n| Cargo / Go | no native gate: committed lockfile + `--locked` / `-mod=readonly` + human review before re-resolution |\n\n**The general principle.** A cool-off works because the registry and researchers detect\nand yank malicious versions while legitimate versions keep accruing age.\nSo the *only* thing the window length trades off is detection coverage against how stale\nyour dependencies are: a longer window catches more of the slow-detection tail, and its\nonly cost is waiting longer for legitimate updates.\nThe benefit curve flattens out (most incidents die in hours to a few days), while the\nstaleness cost grows roughly linearly, so there is a knee in the curve rather than a\nsingle magic number.\n**14 days is the recommended floor**, not a ceiling.\n\nWhy at least 14 days:\n\n- **Detection window.** Most malicious publishes are reported and yanked within 3-7\n  days; 14 days is a generous buffer past that median.\n- **It covers the realistic tail, not just the fast cases.** Many incidents die in\n  minutes (Bitwarden ~93 min, @antv ~22 min), but the value of a cool-off is set by the\n  *slowest*-detected incidents.\n  The `ctx` PyPI takeover was malicious for ~10 days.\n  A 7-day window misses it; a 14-day window catches it.\n- **Patch bumps are where malware hides.** Many compromises arrive as a `1.2.3 -\u003e 1.2.4`\n  patch. A trailing-age window neutralises the whole “fresh patch is malicious” class\n  regardless of which dependency moved.\n- **The cost is asymmetric.** Waiting 14 days on a routine upgrade is essentially free;\n  the only real cost is an urgent security patch, which the exception process handles.\n\n**Pick a larger number if you can.** Nothing here caps the window at 14: a 30-, 60-, or\n90-day cool-off is strictly safer, and high-risk environments (machines with publish\ntokens or production access) should go higher.\nThe “Live X hours” timings in [`compromised-packages.md`](compromised-packages.md) are\nthe evidence base, and pnpm 11 ships a 1-day default (`minimumReleaseAge: 1440`) as the\necosystem’s own floor, so treat 14 days as a balanced minimum and lengthen it to taste.\n\nScope: applies to `dependencies`, `devDependencies` (historically *more* dangerous,\nsince build tooling runs with full developer privileges), `peerDependencies`, and\n`optionalDependencies`; to new installs and upgrades; and to transitive dependencies to\nthe extent the package manager enforces it.\nThe cool-off applies to the **whole resolved set, not just the package you named**:\nadding or upgrading one dependency can pull in many transitive packages, any of which\nmay be brand-new, so review the full lockfile diff and confirm the window for *every*\nnewly added package.\nTo fix a single violator without re-resolving the whole graph, pin it forward in place\n(e.g. `uv lock --upgrade-package \u003cname\u003e==\u003cversion\u003e`, `pnpm update \u003cpkg\u003e@\u003cversion\u003e`).\nPins resolved before adopting the policy are grandfathered until their next planned\nupgrade.\n\n### The Exception Process\n\nWhen a version inside the 14-day window is genuinely needed (for example a CVE patch\npublished yesterday that fixes a vulnerability you are exposed to), take the exception\n*explicitly and on the record*:\n\n- State the reason in the commit message or PR description: the CVE ID (or vulnerability\n  description if none yet), a link to the upstream release notes, and a `Reviewed-by:`\n  sign-off line.\n- Pin the exact `package@version`, not a range.\n  Verify it against the [authoritative sources](#authoritative-sources): publisher,\n  publish time, and integrity hash.\n- Install it **surgically** — a direct tarball / wheel URL or a pinned git ref — rather\n  than relaxing the global cool-off for the whole dependency graph.\n  Each playbook’s “When You Intentionally Need A Fresh Package” step has the\n  verify-then-install commands\n  ([npm](guidelines/hardening-npm.md#step-4-when-you-intentionally-need-a-fresh-package),\n  [PyPI](guidelines/hardening-pypi.md#step-4-when-you-intentionally-need-a-fresh-package);\n  [crates](guidelines/hardening-crates.md#step-6-when-you-intentionally-need-an-unvetted-crate)\n  and [Go](guidelines/hardening-go.md#verify-a-specific-version-before-adding-it) verify\n  before pinning instead, since they have no cool-off to relax).\n- Log it in `supply-chain-audit-log.md` with a follow-up to confirm the version was not\n  yanked after the fact.\n\nNo exception is “trivial” (even a `prettier` patch is in scope): the point of the rule\nis that we do not trust ourselves to eyeball which fresh versions are safe.\n**Agents never self-approve an exception**; they prepare the record above and a human\nsigns off. See [`guidelines/strict-mode.md`](guidelines/strict-mode.md) for the full\nEmergency-Exception record format.\n\n### Update Discipline: The Safest Update Is The One You Skip\n\nA cool-off decides *when* to take an update.\nThe prior question is *whether* to update at all.\nEach update is fresh attack surface, and updating has repeatedly proven riskier than the\nlatent bugs it fixes.\nMitchell Hashimoto (HashiCorp, Ghostty) puts the strong form of this well:\n\n\u003e Fork your dependencies, trim them to only your use case, never update unless it breaks\n\u003e for your users. [...] updating is way riskier than latent bugs (which can be tracked\n\u003e and CVEs monitored).\n\u003e If you are updating a dependency, it’s on you to analyze every single commit in the\n\u003e full transitive set of dependencies.\n\u003e If you don’t see anything compelling, don’t update!\n\u003e [...] Don’t update for the sake of it.\n\nThis is one influential school, and the absolutist version trades supply-chain risk for\nthe risk of *not* applying a needed security fix.\nThe balance this repo recommends:\n\n- **Default to not updating.** Don’t bump a dependency without a concrete reason (\"show\n  me the commit we need\"). Minimise the dependency count, and prefer vendoring or\n  pinning for small, stable libraries.\n- **Monitor CVEs so the exception is data-driven.** The post-install audit commands\n  (`npm audit`, `pip-audit`, `cargo audit`, `govulncheck`) and the IOC feeds are how you\n  learn a real security update is needed, which is exactly when the 14-day exception\n  applies.\n- **When you do update, review the change set,** not just the version number, and then\n  still wait out the 14-day window unless it is a security exception.\n\n## Maintaining This Repo\n\nAll doc-update procedures live in\n[`self-update-instructions.md`](self-update-instructions.md), including the table of\npackage-manager versions the playbooks have been validated against and the\nre-verification procedure for major-version bumps.\nAt a glance:\n\n| Document | When To Update | Typical Cadence |\n| --- | --- | --- |\n| [`compromised-packages.md`](compromised-packages.md) | A notable new supply-chain incident is verified by at least two independent Tier-2 sources, or by CISA | Weeks-to-months |\n| Hardening playbooks ([npm](guidelines/hardening-npm.md), [PyPI](guidelines/hardening-pypi.md), [Rust](guidelines/hardening-crates.md), [Go](guidelines/hardening-go.md)) | A package manager ships a relevant new control, or an existing flag or env-var name changes | Months-to-years |\n| Research docs (in [`research/`](research/)) | An ecosystem-specific mechanism or control set changes, or a new incident merits a dedicated mechanism deep-dive | Months-to-years |\n| [`supply-chain-audit-log-template.md`](supply-chain-audit-log-template.md) | The audit-log entry format evolves | Rarely |\n\nEvery doc follows `std-doc-guidelines.md` (author: jlevy), flagged by the footer at the\nbottom of each file.\nStyle for additions: Title Case headings, no spaced em dashes, concrete examples over\ngeneralities, no “talking about talking”, cite primary sources.\n\n## Contributing\n\nEach new ecosystem guide must:\n\n1. Cite multiple independent sources for any named-incident claim.\n2. Be specific enough to copy-paste: exact env-var names, exact filenames, exact version\n   numbers.\n3. Cover macOS, Linux, and Windows where the underlying tooling supports them.\n4. End with the standard doc-guidelines footer.\n5. Follow the procedure in [`self-update-instructions.md`](self-update-instructions.md).\n\n## Authoritative Sources\n\nEvery cross-reference in this repo points back here.\nVerify any new incident against at least two of the “Incident Reporting Feeds” before\nadding it to [`compromised-packages.md`](compromised-packages.md).\n\n### Per-Ecosystem Vulnerability Databases (System Of Record)\n\n- **npm:** [OSV.dev npm feed](https://osv.dev/list?ecosystem=npm),\n  [GHSA npm filter](https://github.com/advisories?query=type%3Areviewed+ecosystem%3Anpm),\n  `npm audit`.\n- **PyPI:** [OSV.dev PyPI feed](https://osv.dev/list?ecosystem=PyPI),\n  [PyPA Advisory DB](https://github.com/pypa/advisory-database),\n  [GHSA PyPI filter](https://github.com/advisories?query=type%3Areviewed+ecosystem%3Apip),\n  `pip-audit`.\n- **crates.io:** [RustSec Advisory DB](https://rustsec.org/),\n  [OSV.dev crates.io feed](https://osv.dev/list?ecosystem=crates.io),\n  [GHSA Rust filter](https://github.com/advisories?query=type%3Areviewed+ecosystem%3Arust),\n  `cargo audit`.\n- **Go modules:** [Go Vulnerability DB](https://pkg.go.dev/vuln/)\n  ([machine-readable](https://vuln.go.dev/)),\n  [OSV.dev Go feed](https://osv.dev/list?ecosystem=Go),\n  [GHSA Go filter](https://github.com/advisories?query=type%3Areviewed+ecosystem%3Ago),\n  `govulncheck`.\n- **Cross-ecosystem programmatic:** [OSV.dev API](https://google.github.io/osv.dev/api/)\n  (`POST /v1/query` for one package, `POST /v1/querybatch` for up to 1000),\n  [deps.dev API](https://deps.dev/).\n\n### Incident Reporting Feeds (Free, Public, Two-Source Verification)\n\n- [Aikido Intel](https://intel.aikido.dev): live tracker with per-incident package\n  lists.\n- [StepSecurity Blog](https://www.stepsecurity.io/blog): often the first public\n  detector; publishes file-level IOCs.\n- [Socket Security Blog](https://socket.dev/blog): sandboxed-execution analysis.\n- [Datadog Security Labs](https://securitylabs.datadoghq.com/): worm-pattern technical\n  deep-dives.\n- [ReversingLabs Blog](https://www.reversinglabs.com/blog): malware analysis with\n  file-level IOCs.\n- [Unit 42 living doc](https://unit42.paloaltonetworks.com/monitoring-npm-supply-chain-attacks/):\n  Palo Alto’s tracking of the ongoing wave.\n- [Phylum Blog](https://www.phylum.io/blog): package-registry-attack focus.\n- [JFrog Security Research](https://jfrog.com/blog/category/security-research/): npm and\n  PyPI coverage.\n- [CISA Alerts](https://www.cisa.gov/news-events/cybersecurity-advisories): US-CERT\n  advisories for major incidents.\n- Maintainer postmortems (e.g.\n  [TanStack postmortem](https://tanstack.com/blog/npm-supply-chain-compromise-postmortem)):\n  primary sources when available.\n\n### Commercial (Paid Or Mostly-Paid)\n\n[Snyk Vulnerability DB](https://snyk.io/vuln/),\n[Sonatype OSS Index](https://ossindex.sonatype.org/),\n[JFrog Xray](https://jfrog.com/xray/), [Wiz Threat Intel](https://threats.wiz.io/).\n\n## License\n\n[MIT](LICENSE).\n\n\u003c!-- This document follows std-doc-guidelines.md.\nReview guidelines before editing.\n--\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjlevy%2Fsupply-chain-hardening","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjlevy%2Fsupply-chain-hardening","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjlevy%2Fsupply-chain-hardening/lists"}