A monorepo multiplies your dependency surface. Each workspace has its own package.json, its own dependencies, its own attack surface. npm audit doesn't aggregate across workspaces. Neither does pnpm audit.
I ran a scan across a typical pnpm monorepo with 4 workspaces — apps/web, apps/api, packages/ui, packages/shared. Here's what came back:
Monorepo: 4 workspaces → 10 unique external dependencies (npm)
Package Risk Score Publishers Downloads Age
clsx 🔴 CRITICAL 70 1 95.3M/wk 7.4y
lodash 🔴 CRITICAL 81 1 149.2M/wk 14y
zod 🔴 CRITICAL 86 1 164.4M/wk 6.2y
axios 🔴 CRITICAL 86 1 104.2M/wk 11.7y
react 🟢 HEALTHY 88 2 125.2M/wk 14.5y
express 🟢 HEALTHY 90 5 96.2M/wk 15.4y
⚠ 4 CRITICAL packages found.
4 out of 10 unique dependencies are CRITICAL. Not because of known CVEs — because each one has a single npm publisher with >10M weekly downloads.
That's the exact pattern behind the axios supply chain attack (March 30, 2026) and the LiteLLM compromise before it. Stolen credentials → malicious publish → millions of machines exposed. One person's npm token is the entire attack surface.
The monorepo blind spot
pnpm audit checks for known CVEs. It doesn't tell you:
- How many people can publish each package
- Whether your most-downloaded dependency has a single point of failure
- Which packages across ALL your workspaces share this risk
In a monorepo, the same risky package might appear in 3 workspaces. You need the cross-workspace view.
One command
npx proof-of-commitment --file pnpm-workspace.yaml
This:
- Parses your
pnpm-workspace.yamlglob patterns (apps/*,packages/*) - Reads
package.jsonfrom every workspace + root - Merges and deduplicates all external dependencies
- Excludes internal workspace packages (they're yours, not a supply chain risk)
- Scores each package on behavioral signals — publisher count, age, release consistency, download trend
Auto-detection
If you pass the lock file and a workspace file exists next to it, it detects the monorepo automatically:
npx proof-of-commitment --file pnpm-lock.yaml
# Monorepo: 4 workspaces → 10 unique external dependencies (npm)
# (auto-detected pnpm-workspace.yaml next to pnpm-lock.yaml)
CI/CD integration
JSON output for pipelines:
npx proof-of-commitment --file pnpm-workspace.yaml --json | jq '.criticalCount'
# 4
Or use the GitHub Action:
name: Supply Chain Audit
on:
pull_request:
paths: ['**/package.json', 'pnpm-lock.yaml']
jobs:
audit:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: piiiico/commit-action@v1
with:
fail-on-critical: true
comment-on-pr: true
What CRITICAL means
A package is flagged CRITICAL when it has:
-
1 npm publisher (the person with
npm publishaccess — distinct from GitHub contributors) - >10M weekly downloads
zod has 30+ GitHub contributors but 1 npm publisher. If that one token gets stolen, 164M weekly installs get the payload. GitHub contributor count is irrelevant to publish-access risk.
What this doesn't replace
This is not a CVE scanner. It doesn't replace pnpm audit or Snyk or Socket. It measures a different attack surface — behavioral commitment and publisher concentration risk. Use both.
The question pnpm audit answers: does this package have a known vulnerability?
The question behavioral scoring answers: if this package gets compromised tomorrow, how bad is the blast radius?
proof-of-commitment is open source. v1.5.0 shipped today with pnpm workspace support.
npx proof-of-commitment --file pnpm-workspace.yaml — try it on your monorepo.
Top comments (0)