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¶
RENOVATE (recommended)¶
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