Software Supply Chain Security Guide#
A defender’s reference for software supply chain risks — threat model across the SDLC, package-registry attack patterns, CI/CD hardening, artifact provenance and signing, SBOMs, dependency scanning, case studies, and a checklist. Compiled from 29 research articles, advisories, and incident writeups.
Table of Contents#
- Fundamentals
- Threat Model Across the SDLC
- Package Registry Risks
- Dependency Confusion, Typosquatting, Slopsquatting
- Maintainer Account Compromise
- CI/CD Pipeline Hardening
- Container Image Provenance & Verification
- SLSA Framework
- Sigstore, Cosign, in-toto
- SBOMs (SPDX, CycloneDX)
- Dependency Scanning Tooling
- Developer Host Hardening
- Admission Control & Runtime Verification
- Case Studies — Defensive Lessons
- Detection Signals & IOCs
- Defender Checklist
- Reference Configurations
1. Fundamentals#
A software supply chain attack compromises a dependency, tool, build system, or distribution channel that the target trusts, rather than attacking the target directly. The malicious payload rides in on a routine npm install, pip install, docker pull, or CI build — bypassing perimeter defenses because the artifact appears legitimate.
Why the category exploded:
| Factor | Effect |
|---|---|
| Modular package ecosystems | One compromised transitive dep can reach thousands of downstream apps |
| ~85% of enterprises use OSS | Huge shared attack surface |
| Registries default to “latest” | Window between publish and detection is the blast radius |
Lifecycle scripts (postinstall, prepare) | Arbitrary code executes during install, before any runtime control |
| AI-suggested dependencies | ~20% of LLM-suggested packages do not exist — creates slopsquatting openings |
| CI/CD secrets co-located with builds | Stealing them grants further publishing rights → worm behavior |
OWASP Top 10 2025 — A03 Software Supply Chain Failures:
| Metric | Value |
|---|---|
| Community ranking | #1 (50% of respondents) |
| Avg incidence rate | 5.19% (highest in Top 10) |
| Mapped CWEs | 477, 1035, 1104, 1329, 1357, 1395 |
Growth: Supply chain attacks on package registries surged 73% in 2025–2026. Sonatype tracked a 742% increase between 2019 and 2022. OSSF and Bastion 2026 reports attribute the spike to nation-state actors (notably DPRK clusters publishing ~1,700 malicious packages across npm, PyPI, Go, and Rust), credential-stealing worms, and industrialized typosquatting.
2. Threat Model Across the SDLC#
Map threats to lifecycle stages. Each stage has distinct actors, assets, and controls.
| Stage | Assets | Representative Threats | Primary Controls |
|---|---|---|---|
| Develop | Source code, IDEs, dev workstations, SSH/GPG keys | Stolen creds, IDE extensions (Glassworm, OpenVSX), malicious packages on dev host, AI slopsquatting | MFA, commit signing, dev-host hardening, release-age gates, disabled install scripts |
| Source | Git repo, branches, PRs, code review | Force-pushed tags, compromised reviewer, self-merge, secret commit | Branch protection, required reviews, signed commits/tags, CODEOWNERS, secret scanning |
| Build | CI runners, build tools, cache, ephemeral creds | Poisoned runner, compromised action/plugin, cache poisoning, post-build provenance forgery | Ephemeral isolated runners, pinned action SHAs, SLSA L3 hosted builds, OIDC federation, egress allowlist |
| Package | Artifacts, signatures, SBOMs, attestations | Unsigned release, forged provenance, hidden transitive dep injection | cosign sign, in-toto attestations, SLSA provenance, SBOM attached |
| Distribute | Registries (npm/PyPI/GHCR/Docker Hub), mirrors, CDN | Account takeover, typosquat, dep confusion, registry abuse, force-push image tag | Scoped tokens, 2FA, publisher policies, repository firewall, signed pulls |
| Deploy | Kubernetes, cloud, admission policies | Unsigned image rollout, drift, sidecar injection | Admission controllers (Kyverno/OPA/Gatekeeper), signature verification, image allowlists |
| Runtime | Running containers, nodes, secrets stores | Backdoor C2, token exfil via metadata, lateral movement | eBPF runtime, egress filtering, metadata service IMDSv2, least-priv IAM |
STRIDE per stage: Every stage maps to Spoofing (forged provenance), Tampering (injected code), Repudiation (missing attestations), Information disclosure (leaked secrets via install scripts), DoS (dependency deletion à la left-pad), and Elevation (signing-key theft).
Blast-radius formula:
blast_radius = trust_score(package) × downstream_installs × window_before_detection × privilege(install_context)
Reduce any factor. Release-age gates attack window_before_detection; ignore-scripts attacks privilege(install_context); pinning attacks trust_score drift.
3. Package Registry Risks#
Registry-by-registry quick reference#
| Registry | Language | Notable attack patterns (2025–2026) | Key defender features |
|---|---|---|---|
| npm | JavaScript/TypeScript | Chalk/Debug, Shai-Hulud/Shai-Hulud 2.0, Axios, Nx/s1ngularity, CanisterWorm, Glassworm | min-release-age, ignore-scripts, 2FA, scoped publish tokens, trusted publishers via OIDC |
| PyPI | Python | LiteLLM, Telnyx, DPRK ghost-package swarms, wallet stealers | 2FA mandatory for top projects, Trusted Publishers (OIDC), --require-hashes |
| Maven Central | Java | Typosquats of groupIds, unsigned POMs | GPG signing mandatory, namespace verification |
| RubyGems | Ruby | Strong_password-style hijacks | MFA, bundle config set --global frozen true |
| Go proxy | Go | Typosquat import paths, DPRK-seeded modules | Module proxy immutability, go.sum verification, GOSUMDB |
| crates.io | Rust | Typosquats, DPRK-seeded crates | Lockfile enforcement, cargo vet, cargo-audit |
| Docker Hub / GHCR | Containers | Hijacked image tags, base-image tampering, Trivy v0.69.4 force-push | Signed images, content trust, immutable digests |
| VS Code Marketplace / OpenVSX | IDE extensions | Glassworm, TeamPCP OpenVSX | Extension signing (preview), allowlists |
| GitHub Actions | CI actions | Tag repoint, compromised action → secret dump | Pin by SHA, permissions: read-all, OIDC, allowlisted actions |
Attack pattern taxonomy#
| Pattern | Description | Defender signal |
|---|---|---|
| Direct malware upload | Net-new package with malicious payload | Behavioral scanning (Socket.dev), new-publisher heuristics |
| Typosquat | Name differs by 1–2 chars from popular package | Levenshtein monitoring, registry deny-list |
| Dependency confusion | Public package with same name as private internal | Namespace private scopes (@org/*), lockfile + private-registry priority |
| Transitive injection | Legitimate package quietly pulls new malicious dep | Lockfile diffs, SBOM diff alerts |
| Account takeover | Phishing maintainer → legit publish | Publisher-change alerts, 2FA enforcement |
| Worm / self-propagation | Payload steals tokens → republishes | Anomalous publish events, new runner names, post-install egress |
| Force-push tag repoint | Git tag moved to malicious commit | Signed tag verification, tag immutability |
| Install-time execution | postinstall / setup.py runs payload on install | ignore-scripts, sandboxed installs, CI egress allowlist |
| Protestware / maintainer sabotage | Maintainer introduces destructive logic | Behavioral scanning, diff review of minor versions |
| Slopsquatting | LLM hallucinates package name → attacker registers it | Verify AI-suggested packages before install |
4. Dependency Confusion, Typosquatting, Slopsquatting#
Dependency confusion (Birsan 2021)#
Occurs when a package manager prefers public registries over private ones for a name used both internally and publicly.
| Root cause | Fix |
|---|---|
| Package manager falls back to public when private lookup fails or private registry is unreachable | Configure scoped registries; hard-fail if internal package not found in private index |
Internal package names leaked in package.json / requirements.txt | Use @org/name scopes on npm; prefix on PyPI; private Maven groupIds |
| Higher version on public wins | Pre-register internal names on public registry as “security holder” stubs |
npm mitigation:
# .npmrc
@myorg:registry=https://nexus.myorg.local/repository/npm-private/
registry=https://registry.npmjs.org/
always-auth=true
pip / uv mitigation:
# pip.conf — single index, no extra-index-url fallback
[global]
index-url = https://artifactory.myorg.local/artifactory/api/pypi/pypi/simple/
Avoid --extra-index-url — it merges indices and picks highest version. Use --index-url to a repository-manager virtual registry that proxies public content behind policy.
Typosquatting#
| Detection lever | Tool / technique |
|---|---|
| Name similarity to top-1000 packages | Socket.dev, Phylum, Sonatype Repository Firewall |
| New package + suspicious postinstall | Aikido, GitGuardian |
| Namespace deny-list in repo manager | Nexus / Artifactory policies |
| Lockfile diff alerts | GitHub Actions: compare package-lock.json PR diffs |
Slopsquatting (AI-coined)#
LLMs regularly hallucinate plausible-sounding package names. Attackers squat those names. Defensive verification before installing any AI-suggested package:
# npm
npm view <pkg> time.created versions maintainers --json
# PyPI
curl -s https://pypi.org/pypi/<pkg>/json | jq '{name:.info.name,author:.info.author,home_page:.info.home_page,releases:(.releases|keys|length)}'
Red flags: created <30 days ago, single maintainer, no linked repo, download count in tens, no release history.
5. Maintainer Account Compromise#
Phishing an npm/PyPI maintainer became the single most prolific entry vector in 2025–2026 (Chalk, Debug, Axios, LiteLLM, Telnyx, Shai-Hulud, Shai-Hulud 2.0).
| Phase | Attacker action | Defender control |
|---|---|---|
| Recon | Identify maintainers of high-traffic packages | Public — accept |
| Phishing | Lookalike npm/PyPI login page, OAuth consent phishing | Hardware-backed 2FA (WebAuthn), phishing-resistant MFA, allowlisted OAuth apps |
| Publish | Use legitimate account to push malicious version | Trusted Publishers (OIDC from CI only), publish-alerts, velocity anomaly detection |
| Propagate | Stolen tokens from infected victims used to publish new packages (worm) | Short-lived tokens, token scoping per package, detect new runner names (e.g. SHA1HULUD) |
Key principle: a legitimate publisher account with a valid 2FA bypass is indistinguishable from the real maintainer at the artifact level. The only durable defense is post-publish attestation verification (SLSA provenance + Sigstore identity) tied to a CI workflow identity rather than a human account.
6. CI/CD Pipeline Hardening#
CI/CD is high-value: it holds secrets, has network egress, and signs artifacts. Compromising it turns it into a factory for malicious releases.
Control baseline#
| Control | Rationale |
|---|---|
| Ephemeral runners | No state between builds; no cache contamination |
| Isolated builds (SLSA L3) | User build steps cannot touch signing material |
| OIDC federation (no long-lived cloud creds) | Revokes secret exfil value; token valid minutes |
| Pin GitHub Actions by SHA | uses: actions/checkout@b4ffde65... not @v4; defeats tag repoint |
Minimal permissions: | Default contents: read; grant id-token: write only when signing |
| Read-only filesystem | Limits payload persistence |
| Egress allowlist on runners | Blocks curl-to-C2, anomalous registries |
| Secret masking + short-lived tokens | Shrinks exfil window |
| Separation of duties | No single human pushes code → prod |
| Signed commits/tags enforced by branch protection | Attacker cannot force-push malicious tag without signing key |
| Runner fingerprinting | Detect rogue self-hosted runners (e.g., SHA1HULUD) |
| Audit log export + alerting | Detect anomalous workflow_dispatch, new deploy keys |
GitHub Actions hardened job skeleton#
name: build-and-release
on:
push:
tags: ['v*']
permissions:
contents: read
id-token: write # for keyless cosign + OIDC to cloud
packages: write # scoped; only if publishing to GHCR
jobs:
build:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 pinned SHA
with:
persist-credentials: false
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: 20
cache: 'npm'
- run: npm ci --ignore-scripts
- run: npm run build
- name: Generate SBOM
uses: anchore/sbom-action@d94f46e13c6c62f59525ac9a1e147a99dc0b9bf5
with:
format: cyclonedx-json
output-file: sbom.cdx.json
- name: Scan SBOM
uses: anchore/scan-action@be7a22da4f22dde4c7c9e0b14dea0d0f7a4a16a3
with:
sbom: sbom.cdx.json
fail-build: true
severity-cutoff: high
- name: Build & push image
id: build
run: |
IMAGE=ghcr.io/${{ github.repository }}:${{ github.ref_name }}
docker build -t "$IMAGE" .
docker push "$IMAGE"
echo "digest=$(docker inspect --format='{{index .RepoDigests 0}}' $IMAGE)" >> "$GITHUB_OUTPUT"
- name: Sign image (keyless)
env:
COSIGN_EXPERIMENTAL: "1"
run: cosign sign --yes ${{ steps.build.outputs.digest }}
- name: Attest SBOM
run: |
cosign attest --yes --predicate sbom.cdx.json \
--type cyclonedx ${{ steps.build.outputs.digest }}
- name: Attest SLSA provenance
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.0.0
with:
image: ${{ steps.build.outputs.digest }}
Runner egress allowlist (example)#
registry.npmjs.org
pypi.org
files.pythonhosted.org
ghcr.io
*.actions.githubusercontent.com
fulcio.sigstore.dev
rekor.sigstore.dev
oauth2.sigstore.dev
Block everything else. Most supply-chain malware phones home on install — an allowlist surfaces it immediately.
7. Container Image Provenance & Verification#
Container images are the delivery unit and must be identified by digest, not tag.
| Bad | Good |
|---|---|
FROM node:20 | FROM node:20.11.1-alpine3.19@sha256:7c3... |
docker pull myimage:latest | docker pull myimage@sha256:… |
| Tag-based Kubernetes rollout | Digest-pinned manifests + admission verification |
Base image hygiene#
| Practice | Why |
|---|---|
| Prefer distroless / chainguard / wolfi base images | Smaller attack surface, signed by producer |
| Rebuild on base CVE, not periodically | Provenance ties to cause |
Pin base by digest in FROM | Defeats silent tag repoint |
| Scan final image with Grype/Trivy | Catches inherited CVEs |
| Verify base image signature in CI | cosign verify cgr.dev/chainguard/static@sha256:… |
cosign image sign + verify#
# Keyless sign (uses OIDC → Fulcio → Rekor)
COSIGN_EXPERIMENTAL=1 cosign sign --yes \
ghcr.io/myorg/app@sha256:abcd...
# Verify against expected CI workflow identity
cosign verify \
--certificate-identity-regexp="^https://github.com/myorg/app/\.github/workflows/release\.yml@refs/tags/v.*$" \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com \
ghcr.io/myorg/app@sha256:abcd...
The identity regex is the critical verification step — it binds the signature to a specific workflow file in a specific repo, so a compromise of a different repo cannot produce a valid signature for this image.
8. SLSA Framework#
SLSA (Supply-chain Levels for Software Artifacts, “salsa”) gives a vocabulary and progressive maturity levels for build integrity. Developed originally from Google’s Binary Authorization for Borg, donated to OpenSSF in 2021, now at v1.1.
Build track levels#
| Level | Name | Requirements | Protects against |
|---|---|---|---|
| L0 | No SLSA | Nothing | Nothing |
| L1 | Provenance exists | Scripted build, auto-generated provenance | Accidental wrong artifact; incident traceability |
| L2 | Signed provenance | L1 + cryptographically signed by hosted build platform | Post-build forgery; builds from dev workstations |
| L3 | Hardened builds | L2 + isolated, ephemeral runners; signing keys inaccessible to build user code; non-forgeable provenance | Cross-build contamination, insider tampering, signing-key theft by build step |
Note: SLSA v1.0+ consolidated former L4 — hermetic and reproducible builds are now recommended practices, not strict requirements.
Provenance attack vectors addressed#
| Vector | Without SLSA | With SLSA L3 |
|---|---|---|
| Source tampering | Hidden in commit; hard to detect | Provenance binds artifact to commit digest |
| Build tampering | Undetectable mid-build | Hardened runner isolates build from signer |
| Dependency poisoning | Installed silently | Provenance lists dependencies used |
| Provenance forgery | Trivial | Non-forgeable; signed by platform, not user |
| Compromised credentials | Used to publish | OIDC-scoped short-lived, verifiable |
Minimal in-toto / SLSA provenance document#
{
"_type": "https://in-toto.io/Statement/v1",
"subject": [{
"name": "ghcr.io/myorg/app",
"digest": {"sha256": "abcd1234..."}
}],
"predicateType": "https://slsa.dev/provenance/v1",
"predicate": {
"buildDefinition": {
"buildType": "https://github.com/slsa-framework/slsa-github-generator/container@v1",
"externalParameters": {
"repository": "https://github.com/myorg/app",
"ref": "refs/tags/v1.2.3"
},
"resolvedDependencies": [
{"uri": "git+https://github.com/myorg/app", "digest": {"gitCommit": "f00..."}}
]
},
"runDetails": {
"builder": {
"id": "https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@refs/tags/v2.0.0"
},
"metadata": {
"invocationId": "https://github.com/myorg/app/actions/runs/1234567890/attempts/1",
"startedOn": "2026-04-01T12:00:00Z"
}
}
}
}
Consumer verification workflow#
- Download artifact + provenance bundle.
- Verify Sigstore signature against certificate transparency log (Rekor).
- Check
builder.idmatches allowlisted trusted builder. - Match artifact digest against
subject.digest. - Match
externalParameters.repositoryandrefagainst expected. - Policy engine (Kyverno / Conftest / OPA) approves or rejects.
9. Sigstore, Cosign, in-toto#
Component map#
| Component | Role |
|---|---|
| cosign | CLI for signing/verifying container images, blobs, SBOMs, attestations |
| Fulcio | Short-lived code signing CA; issues certs from OIDC identity |
| Rekor | Immutable transparency log of signing events |
| policy-controller / cosign verify | Enforcement at admission or deploy time |
| in-toto attestations | Statement format (subject, predicateType, predicate) |
| SLSA provenance | A predicateType inside in-toto that describes build facts |
Keyless signing flow#
dev/CI → OIDC (GitHub, Google, etc.) → Fulcio (issues 10-min cert bound to identity)
→ cosign signs artifact with ephemeral key
→ signature + cert logged in Rekor transparency log
→ verifiers check cert identity + Rekor inclusion proof
Benefit: no long-lived signing keys to manage or exfil.
Common cosign operations#
# Keyless sign a blob
cosign sign-blob --yes --bundle release.cosign.bundle release.tar.gz
# Verify blob using expected identity
cosign verify-blob \
--bundle release.cosign.bundle \
--certificate-identity-regexp="^https://github.com/myorg/app/.*$" \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com \
release.tar.gz
# Attach an SBOM attestation
cosign attest --yes --predicate sbom.cdx.json --type cyclonedx \
ghcr.io/myorg/app@sha256:abcd...
# Download + inspect attestation
cosign download attestation ghcr.io/myorg/app@sha256:abcd... | \
jq -r '.payload' | base64 -d | jq .
# Verify SLSA provenance predicate
cosign verify-attestation --type slsaprovenance \
--certificate-identity-regexp="^https://github.com/slsa-framework/.*$" \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com \
ghcr.io/myorg/app@sha256:abcd...
Key-based signing (fallback for air-gapped builds)#
cosign generate-key-pair # cosign.key / cosign.pub
cosign sign --key cosign.key ghcr.io/app:1.0.0
cosign verify --key cosign.pub ghcr.io/app:1.0.0
Store private keys in HSM or KMS (--key awskms://…, --key gcpkms://…); never embed in CI secrets if OIDC is available.
10. SBOMs (SPDX, CycloneDX)#
An SBOM is a machine-readable inventory of components, versions, licenses, and relationships in an artifact. It answers “is package X at version Y present?” in seconds when the next Log4Shell drops.
| Format | Body | Typical use |
|---|---|---|
| SPDX | Linux Foundation | License compliance, regulatory (NTIA, EO 14028) |
| CycloneDX | OWASP | AppSec, vuln scanning, VEX, SaaSBOM, ML-BOM |
Minimum viable SBOM practice#
- Generate at build time (not post-hoc from a registry).
- Attach to the artifact (
cosign attest --type cyclonedx). - Sign the SBOM.
- Store versioned alongside the release.
- Feed to a vuln scanner on every pull request and on new CVE disclosure.
- Diff SBOMs between releases to surface new transitive deps.
Generation commands#
# Syft — filesystem
syft dir:. -o cyclonedx-json=sbom.cdx.json -o spdx-json=sbom.spdx.json
# Syft — container image
syft ghcr.io/myorg/app@sha256:abcd... -o cyclonedx-json=sbom.cdx.json
# cdxgen (language-native)
cdxgen -r -t javascript -o sbom.cdx.json
# Trivy
trivy image --format cyclonedx --output sbom.cdx.json ghcr.io/myorg/app@sha256:abcd...
# Docker buildx native
docker buildx build --sbom=true --provenance=mode=max -t myimage .
SBOM vulnerability scan#
# Grype against the SBOM (consistent regardless of runtime env)
grype sbom:sbom.cdx.json --fail-on high
# OSV-Scanner against the SBOM
osv-scanner --sbom sbom.cdx.json
# Dependency-Track upload
curl -X POST "https://dtrack.myorg.local/api/v1/bom" \
-H "X-API-Key: $DTRACK_TOKEN" \
-F "autoCreate=true" \
-F "projectName=myorg/app" \
-F "projectVersion=1.2.3" \
-F "bom=@sbom.cdx.json"
SBOM diff for transitive-dep injection detection#
# Naive: detect new direct or transitive deps between tags
diff <(jq -r '.components[] | "\(.name)@\(.version)"' old.cdx.json | sort) \
<(jq -r '.components[] | "\(.name)@\(.version)"' new.cdx.json | sort)
Run in PR CI — alert a human on any new dependency added by a minor/patch version bump of an existing dependency. This is the signal that would have caught the Axios → plain-crypto-js injection.
11. Dependency Scanning Tooling#
| Tool | Kind | Strength | Limitation |
|---|---|---|---|
| Dependabot | Auto-PR bumps + advisories | Free, GitHub-native, alerts | Only known CVEs; noisy PRs |
| Renovate | Auto-PR bumps | Highly configurable grouping, schedules, merge confidence | Config complexity |
| Snyk | SCA + IaC + container | Good proprietary DB + license checks | Commercial |
| OSV-Scanner | Google OSV DB | Open DB, fast, works on SBOM, lockfiles, directories | CVE-only (but catches GHSA, PYSEC, etc.) |
| Trivy | Image + SBOM + IaC + secrets | One binary does a lot | Noisy defaults |
| Grype | SBOM-driven scanner | Works with Syft output, SPDX and CycloneDX | CVE-only |
| Dependency-Track | Continuous SBOM mgmt | Aggregates SBOMs, policy, VEX | Self-hosted |
| Socket.dev | Behavioral (not CVE) | Detects install scripts, obfuscation, network access | Commercial for private |
| Phylum | Behavioral + reputation | Pre-install blocking | Commercial |
| Sonatype Repository Firewall / Nexus IQ | Registry proxy w/ quarantine | Blocks known-malicious before cache | Commercial |
OSV-Scanner CI usage#
- name: OSV scan
uses: google/osv-scanner-action/osv-scanner-action@v1.9.0
with:
scan-args: |-
--lockfile=package-lock.json
--lockfile=poetry.lock
--recursive
--fail-on-vuln
Dependabot minimal config#
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule: { interval: "daily" }
open-pull-requests-limit: 10
groups:
production:
dependency-type: "production"
development:
dependency-type: "development"
- package-ecosystem: "github-actions"
directory: "/"
schedule: { interval: "weekly" }
- package-ecosystem: "docker"
directory: "/"
schedule: { interval: "weekly" }
Renovate minimal config#
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:recommended", ":pinAllExceptPeerDependencies"],
"minimumReleaseAge": "7 days",
"pinDigests": true,
"packageRules": [
{"matchManagers": ["github-actions"], "pinDigests": true},
{"matchUpdateTypes": ["patch"], "automerge": true, "automergeType": "pr"}
],
"vulnerabilityAlerts": {"enabled": true, "labels": ["security"]}
}
minimumReleaseAge: "7 days" is the single most effective Renovate setting: it refuses to auto-bump into a version published less than 7 days ago, which is the window most malicious releases live before takedown.
12. Developer Host Hardening#
Developer workstations and personal laptops are now prime targets (Shai-Hulud, OpenVSX compromises, Glassworm). Two host-level defenses block the majority of opportunistic install-time attacks:
- Release-age gates — refuse to install any package version published <7 days ago.
- Disable install lifecycle scripts —
postinstall,preinstall,prepare. This is the #1 execution vector for npm malware.
Per-package-manager configs (from 2026 practitioner gist)#
uv (Python) — ~/.config/uv/uv.toml
exclude-newer = "7 days"
pip (Python) — ~/.config/pip/pip.conf + shell alias
[global]
index-url = https://artifactory.myorg.local/artifactory/api/pypi/pypi/simple/
# macOS / BSD date
alias pip='pip --uploaded-prior-to $(date -u -v-7d +%Y-%m-%dT%H:%M:%SZ)'
# Linux / GNU date
alias pip='pip --uploaded-prior-to $(date -u -d "7 days ago" +%Y-%m-%dT%H:%M:%SZ)'
npm — ~/.npmrc
min-release-age=7
ignore-scripts=true
pnpm — pnpm-workspace.yaml
minimumReleaseAge: 10080 # minutes = 7 days
minimumReleaseAgeExclude:
- "@myorg/*"
- "esbuild"
pnpm config set ignore-scripts true --global
yarn — ~/.yarnrc.yml
npmMinimalAgeGate: "7d"
enableScripts: false
npmPreapprovedPackages:
- "@myorg/*"
bun — ~/.bunfig.toml
[install]
minimumReleaseAge = 10080
Bun disables lifecycle scripts by default.
Additional host hygiene#
| Control | Note |
|---|---|
| Full-disk encryption + MDM | Table stakes |
| Hardware-backed MFA (YubiKey, TouchID WebAuthn) | Resists npm/PyPI phishing |
| Separate publish identity from daily-driver | Scoped npm tokens per project |
| EDR with script execution telemetry | Catches post-install spawning shells |
| Allowlist outbound from dev VM | Block C2 traffic |
| Rotate SSH keys, move to signed-commit keys (GPG / SSH) | Prevents force-push forgery |
| Review VS Code / JetBrains extensions regularly | Glassworm / OpenVSX TeamPCP vector |
| Don’t store long-lived cloud creds on disk | Use SSO + short-lived tokens |
13. Admission Control & Runtime Verification#
CI signing is only as useful as the deploy-time verification that enforces it. Without admission control, a compromised runner or registry can push unsigned images straight into production.
Kyverno policy — require cosign-verified images#
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-image-signature
spec:
validationFailureAction: Enforce
background: false
webhookTimeoutSeconds: 30
rules:
- name: check-image-signature
match:
any:
- resources:
kinds: [Pod]
verifyImages:
- imageReferences:
- "ghcr.io/myorg/*"
attestors:
- entries:
- keyless:
subject: "https://github.com/myorg/*/.github/workflows/release.yml@refs/tags/v*"
issuer: "https://token.actions.githubusercontent.com"
rekor:
url: https://rekor.sigstore.dev
Sigstore policy-controller — verify SLSA provenance predicate#
apiVersion: policy.sigstore.dev/v1beta1
kind: ClusterImagePolicy
metadata:
name: require-slsa-provenance
spec:
images:
- glob: "ghcr.io/myorg/**"
authorities:
- keyless:
identities:
- issuer: https://token.actions.githubusercontent.com
subjectRegExp: "https://github.com/slsa-framework/slsa-github-generator/.*"
ctlog:
url: https://rekor.sigstore.dev
attestations:
- name: must-have-slsa
predicateType: https://slsa.dev/provenance/v1
policy:
type: cue
data: |
predicate: {
buildDefinition: {
externalParameters: {
repository: =~"^https://github.com/myorg/"
}
}
}
OPA Gatekeeper — deny unpinned image tags#
package kubernetes.admission
deny[msg] {
input.request.kind.kind == "Pod"
image := input.request.object.spec.containers[_].image
not contains(image, "@sha256:")
msg := sprintf("image %q must be pinned by digest", [image])
}
Runtime#
| Layer | Control |
|---|---|
| Node | eBPF-based runtime detection (Falco, Tetragon) for exec of shells from package dirs |
| Network | Egress NetworkPolicy; deny-by-default cluster egress; CoreDNS deny lists |
| Secrets | Vault/ExternalSecrets with short-lived leases; no baked-in secrets |
| IAM | Workload identity (no static cloud keys); IMDSv2 only |
| Image | Scan on pull; expired image quarantine |
14. Case Studies — Defensive Lessons#
Each case below summarizes what happened, how it was detected, and what defenders learned. No reproduction steps.
14.1 SolarWinds SUNBURST (2020)#
| Aspect | Detail |
|---|---|
| Vector | Build-system compromise; backdoor injected into Orion build |
| Scale | ~18,000 organizations, including US federal agencies |
| Detection | FireEye discovered it in its own environment via anomalous 2FA enrollment |
| Dwell time | Months — code was signed with SolarWinds’ legitimate cert |
| Defensive lesson | Code signing alone is insufficient; the build platform itself must be trusted and attested (SLSA L3). Provenance would have shown “built on dev workstation, not expected builder.” |
14.2 Codecov Bash Uploader (2021)#
| Aspect | Detail |
|---|---|
| Vector | Attackers modified a Bash uploader script distributed via Codecov’s Docker image creation |
| Payload | Exfiltrated CI environment variables (secrets, tokens) |
| Detection | Customer noticed checksum mismatch on the script |
| Defensive lesson | Checksum-verify any script piped from the internet. Never use production creds in CI. Use short-lived OIDC-federated creds. |
14.3 Log4Shell / CVE-2021-44228 (2021)#
| Aspect | Detail |
|---|---|
| Vector | JNDI lookup RCE in a transitive dependency most teams didn’t know they used |
| Detection | Public disclosure after Minecraft PoC |
| Defensive lesson | You cannot patch what you can’t see. SBOMs answer “am I using Log4j anywhere?” in minutes. |
14.4 Event-stream (2018)#
| Aspect | Detail |
|---|---|
| Vector | Original maintainer handed off publish rights to an unknown contributor who added a malicious transitive dep |
| Payload | Crypto wallet stealer targeted at one specific downstream user |
| Detection | Developer noticed deprecated API warning, investigated |
| Defensive lesson | Maintainer handoffs are a red flag. Registry-level notifications for ownership change. Lockfile diffs would have caught the new dep. |
14.5 Chalk / Debug / color-* (September 2025)#
| Aspect | Detail |
|---|---|
| Vector | Maintainer phished → legitimate publish of trojanized versions of chalk, debug, ansi-styles, strip-ansi, supports-color, color-convert, color-string, color-name, ansi-regex, and 13+ others |
| Reach | Billions of weekly downloads exposed |
| Detection | Aikido Security; subsequent analysis by Wiz, Semgrep, Sonatype, ArmorCode, Vercel |
| Defensive lesson | A single phished maintainer of a utility package cascades into the entire JS ecosystem. 2FA with phishing-resistant factors is now mandatory for any popular-package maintainer. Release-age gates would have stopped most installs. |
14.6 Nx / s1ngularity (August 2025)#
| Aspect | Detail |
|---|---|
| Vector | Compromised Nx monorepo tool versions; payload ran during nx build |
| Payload | CI environment enumeration + secret exfil |
| Detection | Upwind Security |
| Defensive lesson | Build-tool compromise executes inside the build context with full access to secrets. Egress allowlists on runners would have blocked exfil. |
14.7 Shai-Hulud npm worm (September 2025) — “First successful self-propagating npm worm”#
| Aspect | Detail |
|---|---|
| Vector | Post-install scripts harvested credentials → used stolen npm tokens to publish malicious versions of whatever the victim could publish |
| Propagation | Self-spreading; reached 500+ package versions before npm disruption |
| Detection | Trend Micro, Sonatype; npm team acted |
| Detection signals | Anomalous publish events, new npm versions without corresponding release commits, outbound exfil to public GitHub repos |
| Defensive lesson | Developer machines themselves are now the target. ignore-scripts=true would have blocked execution. Never store long-lived npm tokens; use OIDC Trusted Publishers. |
14.8 Shai-Hulud 2.0 (November 2025)#
| Aspect | Detail |
|---|---|
| Scale | ~25,000 repositories hijacked, ~500 GitHub users, ~700 npm packages — including Zapier, ENS Domains, PostHog, Postman |
| Payload | Fingerprinted CI vs workstation; dumped cloud.json, truffleSecrets.json; exfiltrated to attacker-owned GitHub repos; hijacked GitHub Actions runners registered as “SHA1HULUD” |
| GH Actions abuse | Backdoored “formatter” workflows dumped toJSON(secrets); malicious discussion.yaml for remote command execution |
| Detection | Aikido, ReversingLabs, Wiz, Trend Micro, GitLab via GitHub Archive telemetry |
| Defensive lesson | Treat self-hosted runners as production. Audit runner registrations. Restrict permissions: blocks. Avoid toJSON(secrets) anti-patterns. |
14.9 Axios / plain-crypto-js (March 2026)#
| Aspect | Detail |
|---|---|
| Vector | Maintainer account hijack → axios@1.14.1 and axios@0.30.4 published with a brand-new hidden transitive dep plain-crypto-js@4.2.1 instead of inline malware |
| Reach | axios = 300M weekly downloads |
| Payload | Obfuscated cross-platform loader via postinstall; OS fingerprint; C2 fetch; OS-specific droppers (AppleScript/PowerShell/VBS/Python); RAT |
| Anti-forensics | Deleted artifacts after execution; rewrote package.json to restore a benign appearance |
| Detection | Sonatype flagged within minutes (01:04 UTC March 31); StepSecurity reporting |
| Defensive lesson | Attackers now inject a hidden transitive instead of modifying core code — SBOM diff is the fastest reliable signal. Repository firewalls that quarantine new packages would have blocked install. Related OpenClaw packages (@qqbrowser, @shadanai) also carried the same dep — blast radius extended via shared dependency |
14.10 TeamPCP campaign (March 2026, ongoing)#
| Sub-incident | Summary |
|---|---|
| Trivy v0.69.4 | Stolen Aqua Security creds used to publish malicious release + force-push 76 GitHub Action tags. Propagated to GHCR, Docker Hub, ECR Public, deb/rpm, get.trivy.dev. Actions dumped CI runner memory and exfiltrated via lookalike domain. |
| CanisterWorm npm | Self-propagating worm across 40+ packages; stole tokens, bumped patch versions, republished; Kubernetes-targeting payload |
| Checkmarx KICS GitHub Action + OpenVSX | Same credential-theft pattern; fallback exfil by creating public repo with victim GITHUB_TOKEN |
| LiteLLM (PyPI) 1.82.7/1.82.8 | Collected env vars, SSH keys, cloud creds, Kubernetes configs, Docker configs, shell history, DB creds, wallet files, CI secrets; encrypted locally and exfiltrated |
| Telnyx (PyPI) 4.87.1/4.87.2 | Same exfil payload |
Unifying defensive lesson: credential theft → automated republishing is the modern blueprint. Required controls: (1) no long-lived publish tokens — OIDC Trusted Publishers only; (2) pinned, digest-bound Action SHAs; (3) signed tag enforcement; (4) egress allowlist on runners; (5) anomaly detection on publish velocity.
14.11 Glassworm (October 2025)#
| Aspect | Detail |
|---|---|
| Vector | Self-spreading VS Code extension on OpenVSX |
| Defensive lesson | IDE extensions are code executing with user privileges. Treat extension installs like package installs — pin, review, scan. |
14.12 PhantomRaven (October 2025)#
| Aspect | Detail |
|---|---|
| Vector | 126 npm packages with stealer payloads |
| Defensive lesson | Behavioral scanners (Socket, Phylum) that look at what a package does catch these before CVE databases do. |
14.13 DPRK 1,700-package flood (2025–2026)#
| Aspect | Detail |
|---|---|
| Vector | North Korean cluster (Contagious Interview / Jackpot Panda overlaps) published across npm, PyPI, Go, Rust |
| Defensive lesson | Nation-state volume makes manual review infeasible. Automation + behavioral analysis + release-age gates are required. |
14.14 dYdX npm + PyPI (2026)#
| Aspect | Detail |
|---|---|
| Vector | Compromised dYdX packages deliver wallet stealers + RAT |
| Defensive lesson | Fintech / Web3 packages are high-value targets. Multi-registry campaigns mean defenders must unify SCA across language ecosystems. |
14.15 React2Shell CVE-2025-55182 (Nov–Dec 2025)#
Not a supply-chain injection but relevant as a dependency-triggered RCE: React Server Components 19.0.0–19.2.0 had a pre-auth RCE. Discovered by Lachlan Davidson; exploited within days by Earth Lamia, Jackpot Panda, Contagious Interview. Over 77,000 vulnerable IPs scanned by Shadowserver. CISA KEV December 17.
Defensive lesson: SBOM-driven scanning plus an emergency response playbook are load-bearing. The fastest organizations to patch had automated SBOM → KEV lookups.
14.16 Oracle EBS CVE-2025-61882 (Oct 2025) — Clop#
Oracle E-Business Suite zero-day (CVSS 9.8) exploited by Clop for mass data theft (Barts Health, Canon, GlobalLogic, LKQ, Logitech, Mazda).
Lesson: Commercial dependencies need the same patching SLAs as OSS.
14.17 ToolShell CVE-2025-53770/-53771 (July 2025)#
Chained SharePoint on-prem exploits by Linen Typhoon, Violet Typhoon, Storm-2603; ~396 systems compromised; web shells deployed.
Lesson: Internet-facing internally-run commercial software is a supply chain node — treat it with the same monitoring as your own binaries.
14.18 CitrixBleed 2 CVE-2025-5777 (June 2025)#
Out-of-bounds read bypassing MFA / hijacking session tokens in NetScaler ADC/Gateway.
Lesson: Network-appliance firmware is supply chain. SBOM your appliances.
14.19 Bybit $1.5B (Feb 2025)#
Wallet-software supply chain compromise executing only when the target wallet was active.
Lesson: Conditional payloads evade test benches. Runtime behavioral monitoring complements static analysis.
14.20 Composite: SolarWinds, Codecov, Log4Shell#
Common thread: a small number of high-trust components → catastrophic downstream. The only durable defense is verifiable provenance + SBOM transparency + deploy-time enforcement.
15. Detection Signals & IOCs#
Build / CI signals#
| Signal | Possible attack |
|---|---|
New postinstall / preinstall in a minor version bump | npm dropper |
| New transitive dep introduced in a patch release | Axios-style hidden injection |
Runner egress to unrecognized domain during npm install | C2 beacon |
toJSON(secrets) in workflow diff | Shai-Hulud 2.0 pattern |
Self-hosted runner registered with anomalous name (e.g. SHA1HULUD) | Worm propagation |
| Force-push to release tag | Trivy v0.69.4 pattern |
| Publish event without corresponding release commit | Worm re-publish |
Unexpected npm publish / twine upload in CI logs | Token abuse |
Package-level signals#
| Signal | Check |
|---|---|
| Obfuscated strings, base64/XOR decoding in install scripts | Static scan |
OS fingerprinting (process.platform, sys.platform) in install scripts | Behavioral |
Writes to /tmp + chmod +x + exec in install scripts | Behavioral |
| Network fetch during install | Behavioral |
package.json rewritten at runtime | Anti-forensics — Axios pattern |
| Single-maintainer, <30 day age, no source repo | Metadata heuristics |
Workstation / dev signals#
| Signal | Source |
|---|---|
Shell spawn from node, python, pip, npm processes | EDR |
Read of ~/.aws/credentials, ~/.ssh/id_*, ~/.kube/config, ~/.docker/config.json by package install | EDR + DLP |
New files under /tmp, %TEMP%, ~/Library correlated with package install | EDR |
| Outbound to GitHub Gists / raw.githubusercontent.com from install | Network |
Hunting queries (SIEM pseudocode)#
# Anomalous npm publish from CI
process.name = "npm" AND argv contains "publish"
AND runner.self_hosted = true
AND runner.name NOT IN (allowlist)
# Credential file access by package install
process.parent IN ("npm","node","pip","python","uv")
AND file.path MATCHES ("~/.aws/*","~/.ssh/*","~/.kube/*","~/.docker/*")
# New transitive dep in patch version
repo.event = "pull_request"
AND file.path IN ("package-lock.json","poetry.lock","go.sum")
AND diff.adds CONTAINS "new package" AND semver.bump = "patch"
16. Defender Checklist#
Strategy#
- Inventory every supply chain node: registries, CI systems, artifact repos, IDE extensions, base images, vendor SaaS
- Assign an owner per node with a runbook and rotation plan
- Establish patch SLAs per severity; measure MTTR to KEV
- Build an incident response playbook keyed to supply-chain scenarios (maintainer ATO, worm, hidden transitive, build poisoning)
- Table-top a Chalk/Debug-scale and an Axios-scale incident annually
Source & Code#
- Branch protection on default branch
- Required review (>=1), with CODEOWNERS for sensitive paths
- Signed commits + signed tags enforced
- Disallow force-push to protected branches/tags
- Secret scanning enabled on all repos (GitGuardian / GitHub push protection)
- No long-lived secrets in CI — OIDC federation to cloud
Dependencies#
- Lockfiles committed for all language ecosystems
-
--require-hashesor equivalent for reproducibility where supported - Dependabot or Renovate enabled, with
minimumReleaseAge >= 7 days - OSV-Scanner or Trivy on every PR + on a nightly schedule
- Dependency-Track (or equivalent) SBOM aggregation and VEX
- Behavioral scanner (Socket, Phylum) wired into PR gate
- Private-registry virtual proxy (Nexus/Artifactory) quarantining new packages
Build / CI#
- Ephemeral runners only
- GitHub Actions pinned by commit SHA, not tag
- Minimal
permissions:per job; default deny -
ignore-scripts=truewhere possible in CI installs - Egress allowlist on runners
- No
toJSON(secrets)anywhere - Tamper-evident audit log export
- Separation of duties: code author != release signer
Artifacts / Packaging#
- SBOM generated at build (CycloneDX + SPDX)
- SBOM attached to artifact as attestation (
cosign attest) - Image signed keyless via cosign + Sigstore
- SLSA L3 provenance via
slsa-github-generator - Reproducible builds where feasible
- Artifacts immutable once published
Registry / Distribution#
- 2FA required on all maintainer accounts (hardware, not SMS)
- Trusted Publishers (OIDC) replacing long-lived tokens
- Publish-alert notifications to shared channel
- Namespace/scope claim for internal packages on public registries
- Repository firewall quarantining newly published or low-reputation deps
Deploy / Runtime#
- Admission controller (Kyverno / policy-controller) verifies cosign signature + SLSA predicate
- Images referenced by digest only
- Deny-by-default egress NetworkPolicy
- IMDSv2 only; workload identity; no baked creds
- Runtime detection (Falco/Tetragon) for shells-from-package dirs
- Canary / staged rollout — never deploy all systems simultaneously (OWASP A03 guidance)
Developer Host#
- Release-age gate configured in npm/pnpm/yarn/bun/pip/uv
-
ignore-scripts/enableScripts: falseglobally - Hardware MFA on registry accounts
- EDR with script-execution telemetry
- IDE extension allowlist; review extension updates
- Verify AI-suggested packages before install (slopsquatting)
Response#
- On suspected compromise, assume full credential exposure — rotate all secrets, cloud keys, SSH keys, tokens touched by the affected environment
- Rebuild affected workstations and CI runners from a clean image
- Pull audit logs for the suspected window
- Block the indicator at repository firewall and network egress
- Publish an internal advisory with affected package/version/IOC list
- Retrospect to close the gap that permitted the compromise
17. Reference Configurations#
17.1 Full hardened .npmrc#
registry=https://nexus.myorg.local/repository/npm-group/
@myorg:registry=https://nexus.myorg.local/repository/npm-private/
always-auth=true
audit=true
fund=false
save-exact=true
min-release-age=7
ignore-scripts=true
engine-strict=true
package-lock=true
17.2 Full hardened pip.conf#
[global]
index-url = https://artifactory.myorg.local/artifactory/api/pypi/pypi/simple/
require-virtualenv = true
disable-pip-version-check = true
no-cache-dir = false
require-hashes = true
17.3 GitHub Actions repository default permissions#
permissions:
actions: read
contents: read
deployments: none
id-token: none
issues: none
packages: none
pages: none
pull-requests: none
repository-projects: none
security-events: none
statuses: none
Escalate per-workflow only where needed.
17.4 Trivy image + config scan#
trivy image --severity HIGH,CRITICAL --exit-code 1 \
--ignore-unfixed \
--format sarif --output trivy.sarif \
ghcr.io/myorg/app@sha256:abcd...
trivy config --severity HIGH,CRITICAL --exit-code 1 .
trivy fs --scanners secret,vuln,misconfig .
17.5 OSV-Scanner offline#
osv-scanner --experimental-offline --experimental-download-offline-databases ./
17.6 Syft + Grype end-to-end#
syft dir:. -o cyclonedx-json=sbom.cdx.json
cosign sign-blob --yes --bundle sbom.cosign.bundle sbom.cdx.json
grype sbom:sbom.cdx.json --fail-on high -o sarif > grype.sarif
17.7 Policy-as-code example (Conftest / OPA on Dockerfile)#
package main
deny[msg] {
input[i].Cmd == "from"
val := input[i].Value[0]
not contains(val, "@sha256:")
msg := sprintf("FROM %s must use @sha256 digest", [val])
}
deny[msg] {
input[i].Cmd == "run"
contains(input[i].Value[_], "curl")
contains(input[i].Value[_], "|")
contains(input[i].Value[_], "sh")
msg := "curl | sh is banned; use pinned, verified installers"
}
17.8 Dependency-Track policy example#
| Policy | Condition | Action |
|---|---|---|
| New high-severity CVE | component.vuln.severity >= HIGH | Fail build |
| Unmaintained dep | component.last_modified > 24 months | Warn |
| Unapproved license | license NOT IN allowlist | Fail |
| Dep from untrusted registry | component.purl !~ allowed_registries | Fail |
| New publisher | component.publisher NEW | Manual review |
17.9 Release playbook summary#
1. Feature branch -> PR -> required review -> merge to main
2. Tag v* -> triggers release workflow (OIDC, read-only by default)
3. Workflow: build -> SBOM -> scan -> sign -> attest (SLSA + SBOM)
4. Publish artifact + signature bundle to registry
5. Admission controller verifies at deploy time
6. Canary rollout 5% -> 25% -> 100% with automated rollback
7. Post-deploy: runtime telemetry + SBOM archived alongside release
17.10 Emergency triage on a suspected compromised dependency#
1. Identify affected package + version(s) from advisory
2. Query SBOM store: "which services ship this?"
3. For each hit:
- Pin to last-known-good version and force-rebuild
- Invalidate all secrets accessible from the build and runtime context
- Rebuild and redeploy affected workloads from clean state
- Review CI runner logs + egress traffic during vulnerable window
4. Block malicious version at repository firewall
5. Add advisory to internal KEV-equivalent list
6. Notify downstream consumers if you publish packages
7. Post-incident: determine which SLSA level / SBOM coverage / admission rule would have prevented it; file follow-up action items
Appendix A — Frameworks & Standards Quick Map#
| Standard | Body | Scope |
|---|---|---|
| SLSA v1.1 | OpenSSF | Build integrity levels, provenance format |
| in-toto | CNCF | Attestation statement format |
| Sigstore / cosign | OpenSSF | Keyless signing, transparency log |
| SPDX 2.3 / 3.0 | Linux Foundation | SBOM (compliance-oriented) |
| CycloneDX 1.5/1.6 | OWASP | SBOM (security-oriented), VEX, ML-BOM |
| NIST SSDF (SP 800-218) | NIST | Secure software development framework |
| EO 14028 | US Executive Order | Federal software supply chain baseline |
| NIST SP 800-161 Rev.1 | NIST | C-SCRM for systems and organizations |
| ISO/IEC 5230 (OpenChain) | ISO | OSS compliance program |
| OWASP SAMM / ASVS V15 | OWASP | Secure coding & architecture verification |
| CIS Software Supply Chain Security Guide | CIS | Benchmark controls |
| SAFECode Software Integrity Controls | SAFECode | Integrity control practices |
Appendix B — Key CWEs#
| CWE | Description |
|---|---|
| CWE-477 | Use of Obsolete Function |
| CWE-1035 | Using Components with Known Vulnerabilities (2017 Top 10 A9) |
| CWE-1104 | Use of Unmaintained Third-Party Components |
| CWE-1329 | Reliance on Component That Is Not Updateable |
| CWE-1357 | Reliance on Insufficiently Trustworthy Component |
| CWE-1395 | Dependency on Vulnerable Third-Party Component |
Appendix C — Sources (29 articles)#
- 12 Months That Changed Supply Chain Security (Silobreaker)
- 2026 Supply Chain Security Report — Lessons from a Year of Devastating Attacks (Bastion)
- OWASP Top 10 2025 A03 — Software Supply Chain Failures (Authgear)
- Axios Compromise on npm Introduces Hidden Malicious Package (Sonatype)
- Axios npm Package Compromised in Supply Chain Attack (InfoQ)
- Compromised dYdX npm and PyPI Packages Deliver Wallet Stealers and RAT Malware (The Hacker News)
- Five Key Flaws Exploited in 2025’s Major Software Supply Chain Incidents (Infosecurity Magazine)
- Hackers Supply Chain Attack Moves From npm to PyPI as Trivy Breach Extends into LiteLLM (Semgrep)
- How to Prevent OWASP Software Supply Chain Failures (CrossClassify)
- LiteLLM PyPI Packages Compromised in Expanding TeamPCP Supply Chain Attacks (Help Net Security)
- Malicious PyPI and npm Packages Discovered Exploiting Dependencies in Supply Chain Attacks (The Hacker News)
- N. Korean Hackers Spread 1,700 Malicious Packages Across npm, PyPI, Go, Rust (The Hacker News)
- NPM Supply Chain Attacks Explained — Dependency Confusion, Exploits, and Defense (jsmon)
- OWASP Top 10 2025 A03 — Software Supply Chain Failures (OWASP)
- Predictions for Open Source Security in 2025 — AI, State Actors, and Supply Chains (OpenSSF)
- Protecting Your Software Supply Chain — Typosquatting and Dependency Confusion (GitGuardian)
- PyPI, npm, and the New Frontline of Software Supply Chain Attacks (RapidFort)
- Securing Software Supply Chains — Critical Infrastructure Priorities for 2026 (Leadership Connect)
- SLSA Framework — The Definitive Guide for Securing Your Software Supply Chain (Practical DevSecOps)
- Software Supply Chain Attacks 2025–2026 — Axios, Shai-Hulud, Chalk, TeamPCP (Cyber Army)
- Software Supply Chain Risks — 2026 Software Supply Chain Report (Sonatype)
- Supply-Chain Attack Defense — Developer Host Machine Hardening (gist)
- Supply-chain Levels for Software Artifacts (slsa.dev)
- Supply Chain Attack — How Attackers Weaponize Software Supply Chains (Netlas)
- Supply Chain Attacks Are Exploiting Our Assumptions (Trail of Bits)
- Supply Chain Attacks in Q4 2025 — From Isolated Incidents to Systemic Failure Modes (Sygnia)
- Supply Chain Security in CI — SBOMs, SLSA, and Sigstore (nathanberg.io)
- The 2026 Guide to Software Supply Chain Security (Cloudsmith)
- The Next Wave of Supply Chain Attacks — NPM, PyPI, Docker Hub Set the Stage for 2026 (LinuxSecurity)
This document synthesizes publicly reported research and advisories for defensive reference. It contains no reproduction steps, no working malicious code, and no attacker tooling. All code snippets are defensive configurations.