Skip to content

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

# .npmrc  
ignore-scripts=true  

RE-ENABLE selectively for verified packages that require native compilation:

npm install --ignore-scripts  
npm rebuild <specific-package-that-needs-it>  

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