Skip to content

DOMAIN:SUPPLY_CHAIN:DEPENDENCY_MANAGEMENT

OWNER: julian, pol
ALSO_USED_BY: koen, victoria, ashley
UPDATED: 2026-03-26
SCOPE: dependency audit, lockfile integrity, automated updates, licence compliance


OVERVIEW

Average Node.js app: 200-800+ dependencies (direct + transitive).
80%+ of application code is third-party packages.
Supply chain attacks increased 742% 2019-2022 (Sonatype).
454,648 malicious packages published in 2025 alone.
99% of open-source malware in 2025 occurred on npm.

Dependency management is not optional hygiene — it is a security-critical practice.


DEPENDENCY AUDIT PATTERNS

BEFORE ADOPTING NEW DEPENDENCY

Every new dependency MUST pass this checklist before adding to any project:

MAINTENANCE_HEALTH:
CHECK: last commit within 6 months (abandoned packages = unpatched vulns)
CHECK: open issues ratio (growing backlog = risk)
CHECK: active maintainers (bus factor — single maintainer = high risk)
CHECK: maintainer identity verifiable (not anonymous, has track record)
CHECK: regular release cadence (indicates active maintenance)

SECURITY_HISTORY:
CHECK: past CVEs — how many? how quickly patched?
CHECK: security policy (SECURITY.md) — does project have vulnerability disclosure process?
CHECK: signed releases or commits (evidence of provenance)
CHECK: no known malicious versions in history

LICENCE:
CHECK: licence compatible with project use (see licence section below)
CHECK: licence stable (not changed recently to incompatible type)
CHECK: all transitive dependency licences also compatible

SCOPE_AND_SIZE:
CHECK: package size reasonable (bloated packages indicate poor design)
CHECK: permission scope — does it need filesystem/network access? (Socket.dev checks this)
CHECK: install scripts — does it run postinstall scripts? (common attack vector)
CHECK: transitive dependencies — what does it pull in? (npm ls <package>)

ALTERNATIVES:
CHECK: is there a smaller, better-maintained alternative?
CHECK: can the functionality be implemented directly? (avoid dependency for trivial functions)
CHECK: is the package part of a well-maintained ecosystem (e.g., lodash, React)?

PERIODIC AUDIT

FREQUENCY: quarterly for all dependencies, monthly for critical/security-sensitive ones
TOOL: npm audit, Socket.dev, Snyk
OUTPUT: documented report with action items
ESCALATION: any critical finding → patch within SLA (see vulnerability-management.md)


LOCKFILE INTEGRITY

RULES

RULE: lock files MUST be committed to git (package-lock.json, yarn.lock, pnpm-lock.yaml)
RULE: use npm ci (not npm install) in CI/CD — respects lock file exactly
RULE: never manually edit lock files — use package manager commands
RULE: review lock file diffs in code review (new dependencies visible in diff)

WHY LOCKFILE MATTERS

WITHOUT_LOCKFILE: npm install resolves "latest compatible" — different builds get different versions
WITH_LOCKFILE: npm ci installs exact versions — deterministic, reproducible builds
SECURITY: lock file pins exact versions, preventing silent upgrades to compromised versions

LOCKFILE VERIFICATION

CHECK: lock file present in repository
CHECK: lock file not in .gitignore
CHECK: CI uses npm ci (not npm install)
CHECK: lock file integrity hash matches (npm verifies automatically)
CHECK: no unexpected changes in lock file PRs (review carefully)

LOCKFILE TAMPERING

ATTACK: attacker modifies lock file to point to malicious package version
DEFENSE: review lock file changes in every PR
DEFENSE: use npm audit signatures to verify package provenance
DEFENSE: lock file hash verification in CI pipeline


VERSION PINNING STRATEGIES

EXACT PINNING

SYNTAX: "lodash": "4.17.21" (no caret, no tilde)
USE_FOR: critical security-sensitive dependencies, production deployments
BENEFIT: zero surprise upgrades, maximum determinism
COST: requires manual updates — may miss security patches

CARET (default npm)

SYNTAX: "lodash": "^4.17.21" (allows minor + patch updates)
USE_FOR: development dependencies, well-tested libraries
BENEFIT: automatically receives compatible updates
RISK: minor version may introduce breaking changes despite semver

TILDE

SYNTAX: "lodash": "~4.17.21" (allows patch updates only)
USE_FOR: balance between stability and patching
BENEFIT: receives security patches automatically
RISK: even patches can introduce regressions

GE POLICY

PRODUCTION_DEPS: exact pinning for security-critical packages (auth, crypto, payments)
PRODUCTION_DEPS: caret for well-established libraries with good semver track record
DEV_DEPS: caret acceptable (less risk — not in production)
ALL: lock file provides actual determinism regardless of range specifier


AUTOMATED UPDATE POLICIES

TYPE: automated dependency update tool (Mend)
FEATURES: highly configurable, groups related updates, scheduled PRs
ADVANTAGE: more configurable than Dependabot, better monorepo support

RECOMMENDED_CONFIG:

{  
  "extends": ["config:recommended"],  
  "schedule": ["before 9am on Monday"],  
  "automerge": true,  
  "automergeType": "branch",  
  "packageRules": [  
    {  
      "matchUpdateTypes": ["patch"],  
      "automerge": true  
    },  
    {  
      "matchUpdateTypes": ["minor"],  
      "automerge": true,  
      "matchPackagePatterns": ["eslint", "prettier", "typescript"]  
    },  
    {  
      "matchUpdateTypes": ["major"],  
      "automerge": false,  
      "labels": ["major-update"]  
    }  
  ]  
}  

POLICY_SUMMARY:
- patch updates: auto-merge after CI passes
- minor updates: auto-merge for trusted packages, manual review for others
- major updates: always manual review
- schedule: weekly (Monday morning) to batch updates

DEPENDABOT (GitHub native)

TYPE: built-in GitHub dependency update tool
FEATURES: simpler config, good GitHub integration
LIMITATION: less configurable than Renovate, weaker grouping
USE_WHEN: Renovate not available or simpler setup preferred

UPDATE REVIEW PROCESS

FOR_AUTO-MERGED: CI must pass (tests, lint, build, security scan)
FOR_MANUAL: reviewer checks changelog, breaking changes, security advisories
FOR_MAJOR: additional testing in staging environment before merge
BLOCKED: any update introducing known vulnerability blocks merge


TRANSITIVE DEPENDENCY RISKS

THE PROBLEM

Direct dependencies are visible and reviewed.
Transitive dependencies (dependencies of dependencies) are hidden and numerous.
A typical npm project may have 10 direct deps and 500+ transitive deps.
Attackers increasingly target deep transitive dependencies.

EXAMPLES

EVENT-STREAM (2018): maintainer handed over to attacker who added malicious code in transitive dep.
UA-PARSER-JS (2021): compromised maintainer account — malicious versions published.
COLORS/FAKER (2022): maintainer deliberately sabotaged widely-used packages.
SHAI-HULUD WORM (2025): self-replicating npm worm spread across 500+ packages.
TRIVY ATTACK (2026): compromise propagated through dependency chain.

MITIGATION

VISIBILITY:
CHECK: run npm ls --all to see full dependency tree
CHECK: use Socket.dev for deep dependency analysis
CHECK: SBOM includes all transitive dependencies (see sbom.md)

REDUCTION:
CHECK: prefer packages with fewer transitive dependencies
CHECK: evaluate if direct implementation is cheaper than deep dependency chain
CHECK: remove unused dependencies regularly (depcheck)

MONITORING:
CHECK: automated alerts on maintainer changes for critical transitive deps
CHECK: continuous vulnerability scanning covers transitive deps
CHECK: lock file review catches unexpected transitive changes

ISOLATION:
CHECK: install scripts disabled by default (.npmrc: ignore-scripts=true)
CHECK: re-enable selectively only for verified packages
CHECK: container isolation limits blast radius of compromised dep


LICENCE COMPLIANCE SCANNING

RISK LEVELS (for SaaS products)

Licence Risk Obligation
MIT, BSD-2, BSD-3, ISC LOW Attribution in NOTICE file
Apache 2.0 LOW Attribution + patent grant notice
MPL 2.0 MEDIUM Copyleft for modified files only
LGPL 2.1/3.0 MEDIUM Copyleft for library mods; dynamic linking usually OK
GPL 2.0/3.0 HIGH Copyleft extends to linked code — may require open-sourcing
AGPL 3.0 CRITICAL Copyleft extends to network use (SaaS) — almost certainly requires open-sourcing
SSPL CRITICAL MongoDB-style — very broad copyleft for SaaS
BSL (Business Source License) VARIES May restrict production use — read terms carefully
UNLICENSED/CUSTOM UNKNOWN Cannot use without explicit permission

SCANNING TOOLS

TOOL: license-checker (npm) — scans node_modules for licence info
TOOL: Snyk — licence compliance as part of broader security scanning
TOOL: FOSSA — comprehensive licence compliance platform
TOOL: scancode-toolkit (nexB) — deep licence detection including source analysis

GE POLICY

ALLOWED: MIT, BSD-2, BSD-3, ISC, Apache 2.0, MPL 2.0
REVIEW_REQUIRED: LGPL (assess linking strategy)
BLOCKED: GPL, AGPL, SSPL — escalate to human before using
UNKNOWN: blocked until licence identified and reviewed

CHECK: scan at build time — fail CI if blocked licence detected
CHECK: maintain NOTICE file with all attributions
CHECK: review licence changes in dependency updates (licence can change between versions)
CHECK: transitive dependencies also checked (AGPL in transitive dep = same risk)

GPL CONTAMINATION

TRAP: direct dependency is MIT but transitive dependency is GPL.
TRAP: build tool is GPL but its licence may not extend to output (depends on tool).
TRAP: test dependency is GPL — acceptable if not bundled in production artifact.
TRAP: licence changed in new version — old version MIT, new version GPL.

RULE: if ANY production dependency (including transitive) is GPL/AGPL, escalate immediately.


DEPENDENCY CONFUSION PREVENTION

ATTACK VECTOR

Attacker publishes public package with same name as private/internal package.
Package manager resolves to public (higher version number) instead of private.
Malicious code executes in CI/CD or developer environment.

DEFENSES

CHECK: use scoped packages for internal packages (@ge/package-name)
CHECK: configure npm registry scope: .npmrc with @ge:registry=https://internal.registry
CHECK: claim internal package names on public npm (placeholder packages)
CHECK: use --registry flag in CI to pin registry source
CHECK: audit package-lock.json resolved URLs — all should point to expected registry


MAINTAINER RISK

THE PROBLEM

Single-maintainer packages are high risk — social engineering, burnout, account compromise.
800+ Lazarus Group-associated packages identified on npm in 2025.
IndonesianFoods campaign: 100,000+ malicious packages generated by automation.

MONITORING

CHECK: critical dependency maintainer count (bus factor)
CHECK: alert on maintainer changes (new maintainer added, ownership transferred)
CHECK: alert on unusual publish patterns (sudden high-frequency releases)
CHECK: verify maintainer MFA enabled (npm now requires for top packages)

RESPONSE PLAN

IF maintainer change on critical dep:
1. pin to current version immediately
2. audit new maintainer identity and history
3. review code changes since maintainer change
4. if suspicious: find alternative package or fork


READ_ALSO: domains/supply-chain/index.md, domains/supply-chain/sbom.md, domains/supply-chain/vulnerability-management.md