DOMAIN:SUPPLY_CHAIN:PITFALLS¶
OWNER: julian, pol
ALSO_USED_BY: koen, victoria, ashley
UPDATED: 2026-03-26
SCOPE: common supply chain security mistakes and misconceptions
PITFALL: PHANTOM DEPENDENCIES¶
THE PROBLEM¶
Phantom dependencies are packages your code imports but that are not declared in package.json.
They work because a declared dependency happens to install them as transitive deps.
If the declared dependency drops the transitive dep, your code breaks silently.
HOW IT HAPPENS¶
package.json declares: "framework-x"
framework-x depends on: "utility-y"
Your code imports: "utility-y" directly (works because it's in node_modules)
framework-x v2.0 drops utility-y → your code breaks
DETECTION¶
TOOL: depcheck — finds imported packages not in package.json
TOOL: knip — finds unused and undeclared dependencies
CHECK: CI step that fails on undeclared imports
PREVENTION¶
RULE: every import must correspond to a declared dependency in package.json
RULE: never import from transitive dependencies directly
RULE: if you need a transitive dep directly, add it to package.json explicitly
AUDIT: run depcheck quarterly or add to CI pipeline
RISK¶
SECURITY: phantom dep may be removed upstream, replaced with malicious package of same name.
STABILITY: version of phantom dep controlled by upstream, not by you.
REPRODUCIBILITY: different resolution strategies may resolve different versions.
PITFALL: SBOM COMPLETENESS GAPS¶
THE PROBLEM¶
Most SBOMs are incomplete.
Incomplete SBOM gives false confidence — "we scanned our SBOM, no vulnerabilities" means nothing if SBOM only covers 60% of components.
COMMON GAPS¶
GAP: container base image packages not included (tool only scanned app layer).
GAP: vendored code (copied into repo) not detected by package manager tools.
GAP: native extensions (node-gyp, Python C extensions) not captured.
GAP: system libraries installed via apt/apk in Dockerfile not included.
GAP: build-time dependencies that leak into production image.
GAP: JavaScript bundled/minified code not reverse-mapped to source packages.
GAP: git submodules not scanned.
GAP: internal/private packages not in SBOM (tool cannot access private registry).
DETECTION¶
COMPARE: SBOM component count vs actual file count in production artifact.
VERIFY: SBOM includes base image components (run syft on final container image, not just source).
TEST: manually check for known components — are they in the SBOM?
MITIGATION¶
RULE: generate SBOM from final production artifact (container image), not from source alone.
RULE: combine application-layer SBOM (npm sbom) with container-layer SBOM (syft on image).
RULE: include vendored code in SBOM manually or via scanning tools that detect it.
RULE: audit SBOM completeness quarterly.
TOOL: syft on final Docker image captures most layers.
TOOL: cdxgen has better detection of vendored and embedded components.
PITFALL: LICENCE COMPATIBILITY CONFLICTS (GPL CONTAMINATION)¶
THE PROBLEM¶
GPL and AGPL licences are "copyleft" — they require derivative works to be released under the same licence.
For SaaS products, AGPL extends this to network use.
A single AGPL transitive dependency can legally require open-sourcing your entire application.
HOW CONTAMINATION HAPPENS¶
SCENARIO_1: direct dependency is MIT, but its transitive dep is GPL.
RESULT: if GPL dep is linked into your runtime, GPL may apply to your code.
SCENARIO_2: developer adds a package without checking licence.
RESULT: GPL code enters codebase, remains undetected until client audit.
SCENARIO_3: package changes licence in new version (MIT → GPL or SSPL).
RESULT: automated update pulls in newly-GPL version without review.
SCENARIO_4: package under "dual licence" — GPL for open-source use, commercial for paid.
RESULT: developer assumes free use, but commercial licence requires payment for SaaS.
DETECTION¶
TOOL: license-checker in CI pipeline — fail build on blocked licence
TOOL: FOSSA or Snyk licence scanning — continuous monitoring
CHECK: scan transitive dependencies, not just direct
CHECK: monitor licence changes across dependency updates
PREVENTION¶
POLICY: CI gate that blocks GPL/AGPL/SSPL dependencies from merging
POLICY: licence review required for all new dependencies
POLICY: Renovate/Dependabot configured to flag licence changes in update PRs
POLICY: maintain NOTICE file with all attributions (required even for permissive licences)
GREY AREAS¶
LGPL: copyleft applies to modifications of the library itself, not to your code linking to it.
Dynamic linking to LGPL library is generally safe for SaaS.
Static linking or modification = copyleft may extend.
CONSULT: legal review for any LGPL dependency in critical path.
BUILD_TOOLS: GPL-licenced build tool (e.g., GCC) does not GPL your output.
The "GPL with GCC Runtime Library Exception" explicitly permits this.
HOWEVER: GPL-licenced code generator where output contains GPL code = GPL output.
PITFALL: SUPPLY CHAIN ATTACK VECTORS¶
THE PROBLEM¶
Supply chain attacks are no longer theoretical — they are industrialised.
454,648 malicious packages published in 2025.
First self-replicating npm worm (Shai-Hulud) in Sep 2025.
Lazarus Group: 800+ packages on npm.
Trivy (security scanner itself) compromised in Mar 2026.
ATTACK TAXONOMY¶
CREDENTIAL THEFT¶
VECTOR: phishing maintainer, MFA bypass, token theft from CI logs.
EXAMPLE: Sep 2025 npm compromise — 18 packages (chalk, debug, ansi-styles) via stolen credentials.
DEFENSE: verify maintainer identity changes, alert on unusual publish patterns.
TYPOSQUATTING¶
VECTOR: publish package with similar name to popular package.
EXAMPLE: IndonesianFoods campaign — 100,000+ packages generated at rate of 1 every 7 seconds.
DEFENSE: autocomplete for package names, lock file review, Socket.dev detection.
DEPENDENCY CONFUSION¶
VECTOR: publish public package matching internal/private package name.
EXAMPLE: Alex Birsan research (2021) — affected Apple, Microsoft, others.
DEFENSE: scoped packages, registry configuration, placeholder packages.
MAINTAINER TAKEOVER¶
VECTOR: social engineering existing maintainer to transfer ownership.
EXAMPLE: event-stream (2018) — maintainer handed over to attacker.
DEFENSE: monitor maintainer changes on critical deps, evaluate bus factor.
CI/CD COMPROMISE¶
VECTOR: compromise build pipeline to inject malicious code into artifacts.
EXAMPLE: GhostAction (2025) — compromised GitHub Action exfiltrated secrets.
DEFENSE: pin actions by SHA, verify action provenance, ephemeral runners.
SELF-PROPAGATING WORMS¶
VECTOR: malicious package that modifies other packages it has access to.
EXAMPLE: Shai-Hulud (Sep 2025) — spread across 500+ packages autonomously.
DEFENSE: disable install scripts, containerised builds, network isolation during install.
DEFENSE-IN-DEPTH¶
NO single defence is sufficient. Layer defences:
1. PREVENT: lock files, version pinning, registry configuration, disabled install scripts
2. DETECT: Socket.dev, npm audit signatures, SBOM scanning, maintainer monitoring
3. CONTAIN: containerised builds, network policies, least-privilege CI
4. RESPOND: incident response playbook, secret rotation, clean rebuild capability
PITFALL: OVER-RELIANCE ON SEVERITY SCORES¶
THE PROBLEM¶
CVSS scores are widely used but widely misunderstood.
A CVSS 9.8 that is never exploited wastes resources.
A CVSS 5.5 that is actively exploited in the wild gets ignored.
Teams that patch by CVSS alone are inefficient and still exposed.
THE DATA¶
2025: barely 1% of disclosed CVEs were actively exploited.
28.96% of KEVs showed exploitation on or before disclosure day.
Average time to patch critical vulnerabilities: 4.5 months (BitSight).
Organisations prioritising by exposure are 3x less likely to suffer breach.
WHAT CVSS GETS WRONG¶
MISSING: exploitation likelihood (CVSS does not predict exploitation)
MISSING: asset context (a CVSS 9.8 on an isolated test server ≠ CVSS 9.8 on production)
MISSING: compensating controls (WAF, network segmentation reduce real risk)
BIAS: network-accessible RCE always scores high, even if exploit is theoretical
WHAT TO DO INSTEAD¶
USE_TOGETHER: CVSS (severity) + EPSS (likelihood) + KEV (evidence) + asset context
FORMULA: effective risk = severity * likelihood * asset criticality * exposure
EXAMPLE:
- CVSS 9.8 + EPSS 0.01 + not in KEV + isolated test server = low effective risk
- CVSS 6.5 + EPSS 0.85 + in KEV + production database = critical effective risk
TOOL: CVE_Prioritizer (GitHub) — combines CVSS, EPSS, KEV into single priority score
PROCESS: daily triage using combined signals, not CVSS alone
METRICS THAT MATTER¶
BAD: "we patched all CVSS >= 9.0 within 7 days"
BETTER: "we patched all KEV-listed CVEs affecting exposed assets within 48 hours"
BEST: "MTTR for Tier 1 (KEV + exposed) is 36 hours, coverage is 98%"
PITFALL: INSTALL SCRIPTS AS ATTACK VECTOR¶
THE PROBLEM¶
npm packages can run arbitrary code during installation via lifecycle scripts:
- preinstall, install, postinstall
- These execute with the permissions of the installing user/CI runner
- Malicious install scripts can: exfiltrate secrets, install backdoors, modify other packages
SCALE¶
The IndonesianFoods campaign and multiple typosquatting campaigns
use postinstall scripts as primary payload delivery mechanism.
Several 2025 campaigns were CI-aware — triggering only in automated build environments.
MITIGATION¶
RULE: disable install scripts by default
RE-ENABLE selectively for verified packages that require native compilation:
CHECK: audit which packages have install scripts: npm ls --all --json | jq '.dependencies | to_entries[] | select(.value.scripts.postinstall)'
CHECK: review install script content before enabling for a package
CHECK: CI pipeline disables scripts by default, enables only for allowlisted packages
PITFALL: SBOM WITHOUT VULNERABILITY MANAGEMENT¶
THE PROBLEM¶
Generating SBOMs without acting on them creates compliance theatre.
An SBOM sitting in a repository, never scanned, never monitored, provides zero security value.
CRA requires not just SBOM but active vulnerability handling.
SYMPTOMS¶
- SBOM generated in CI but never consumed by any downstream process
- no automated scanning against vulnerability databases
- no defined SLA for patching vulnerabilities found in SBOM
- SBOM format or tool produces incomplete results (see completeness gaps above)
- VEX not maintained alongside SBOM
REQUIRED ECOSYSTEM¶
SBOM alone is not enough. You need:
1. SBOM generation (syft/cdxgen in CI)
2. vulnerability scanning (Grype/trivy matching SBOM against CVE databases)
3. prioritisation (KEV + EPSS + CVSS + asset context)
4. patching workflow (Renovate/Dependabot + SLA enforcement)
5. VEX communication (not_affected / affected / fixed status per CVE)
6. retention and audit trail (historical SBOMs for 10+ years per CRA)
WITHOUT any one of these, the system has a gap.
READ_ALSO: domains/supply-chain/index.md, domains/supply-chain/sbom.md, domains/supply-chain/vulnerability-management.md