I wanted to see how different tools handle the same dependency tree, so I ran both npm audit and my open-source tool DepGra against a real Next.js project with 1,312 packages. Here's what actually happened.
The project
The test subject is a production Next.js app with a 19,000-line package-lock.json. 1,312 packages, 3,792 dependency relationships. A pretty typical mid-size project.
npm audit results
npm audit
npm audit reported 10 vulnerabilities (3 moderate, 7 high) across 8 packages:
-
serialize-javascript@6.0.2— RCE via RegExp.flags (high) -
next@15.5.9— 2 advisories (high) -
minimatch@3.1.2andminimatch@9.0.5— 3 ReDoS advisories each (high) -
flatted@3.3.3— unbounded recursion DoS (high) -
rollup@4.54.0— arbitrary file write via path traversal (high) -
ai@4.3.19— filetype whitelist bypass (moderate) -
jsondiffpatch@0.6.0— XSS via HtmlFormatter (moderate) -
ajv@6.12.6andajv@8.17.1— ReDoS with$dataoption (moderate)
npm audit also tells you which vulnerabilities have fixes available and whether they'd require breaking changes. That's useful context DepGra doesn't provide.
DepGra results
DepGra scanned the same package-lock.json in 6.5 seconds. It found 12 unique advisories across 10 packages:
CRITICAL GHSA-5c6j-r48x-rmvq serialize-javascript@6.0.2
HIGH GHSA-23c5-xmqv-rm74 minimatch@3.1.2, minimatch@9.0.5
HIGH GHSA-25h7-pfq9-p65f flatted@3.3.3
HIGH GHSA-3ppc-4f35-3m26 minimatch@3.1.2, minimatch@9.0.5
HIGH GHSA-7r86-cg39-jmmj minimatch@3.1.2, minimatch@9.0.5
HIGH GHSA-h25m-26qc-wcjf next@15.5.9
HIGH GHSA-mw96-cpmx-2vgc rollup@4.54.0
MEDIUM GHSA-33vc-wfww-vjfv jsondiffpatch@0.6.0
MEDIUM GHSA-5f7q-jpqc-wp7h next@15.5.9
MEDIUM GHSA-9g9p-9gw9-jx7f next@15.5.9
MEDIUM GHSA-rwvc-j5jr-mgvh ai@4.3.19
UNKNOWN GHSA-2g4f-4pwh-qvx6 ajv@6.12.6, ajv@8.17.1
What was different
All 11 advisories from npm audit showed up in DepGra's results. But DepGra found one extra:
GHSA-5f7q-jpqc-wp7h (CVE-2025-59472) — Next.js Unbounded Memory Consumption via PPR Resume Endpoint. Published January 28, 2026. npm audit didn't report it.
Why? DepGra queries OSV.dev, which aggregates vulnerability data from multiple sources. npm audit queries the GitHub Advisory Database. Sometimes one source has advisories the other hasn't ingested yet. In this case, OSV.dev had this CVE and GitHub's advisory database didn't surface it through npm audit at the time I tested.
This isn't a knock on npm audit — advisory databases update at different speeds and there will always be timing differences. Tomorrow npm audit might catch something OSV.dev doesn't. The point is that checking against multiple data sources catches more.
A few other differences worth noting:
- npm audit classified
serialize-javascriptas "high." DepGra pulled the full CVSS vector and scored it as CRITICAL. Same vulnerability, different severity classification depending on the data source. - npm audit counts vulnerabilities by affected package instance (so minimatch across 4
node_moduleslocations counts differently). DepGra counts unique CVE IDs. - npm audit tells you if a fix is available and whether it's a breaking change. DepGra doesn't do remediation — it's a visibility tool.
What the graph adds
The flat list above tells you what's vulnerable. But when I loaded the same scan into DepGra's graph view, two things jumped out:
minimatch is a chokepoint. The flat list shows minimatch has 3 HIGH advisories, same as any other package. But the graph shows that minimatch@3.1.2 has packages like @sentry/node, @typescript-eslint/typescript-estree, and glob all depending on it. If you're prioritizing what to fix, minimatch has a bigger blast radius than its severity alone suggests.
serialize-javascript's risk path is clear. In the flat list, it's one line item. In the graph, you can see the chain: copy-webpack-plugin and terser-webpack-plugin both depend on serialize-javascript@6.0.2. That CRITICAL RCE vulnerability has two separate paths into the project. You can trace each one visually.
None of this is information you can't figure out from npm ls and some digging. But having it laid out as a graph makes the topology obvious instead of something you have to reconstruct in your head.
How DepGra works
The tech stack is pretty simple:
-
Parsers for
package-lock.json,Cargo.lock,poetry.lock,requirements.txt, andgo.mod - OSV.dev batch API for vulnerability data — sends all packages in one request, then fetches full details for any hits
- SQLite for storage, NetworkX for graph analysis (centrality scoring, path finding)
- Flask serves the REST API, Svelte + Cytoscape.js renders the graph
- Topological sort for the DAG layout — O(V+E), handles 1,300+ nodes without choking
For Python requirements.txt specifically, it resolves transitive dependencies by querying the PyPI API, since requirements.txt doesn't include the dependency tree like a lockfile does.
Try it
git clone https://github.com/KPCOFGS/depgra
cd depgra
# Install
cd backend && uv venv .venv && source .venv/bin/activate && uv pip install -r requirements.txt && cd ..
cd frontend && npm install && npm run build && cd ..
# Run
python run.py
# Open http://127.0.0.1:5000
Or use the CLI:
python run.py scan path/to/package-lock.json
python run.py scan requirements.txt --fail-on HIGH
What DepGra doesn't do
I want to be upfront about the limitations:
- No auto-remediation. It won't suggest version upgrades or create fix PRs.
npm audit fixdoes this and DepGra doesn't. - No container scanning, no license compliance, no secrets detection. It's specifically a dependency vulnerability visualizer.
- The severity classifications come from OSV.dev and can differ from what npm audit or Snyk reports for the same CVE.
- For very large graphs (1,000+ packages), the visualization gets dense. It's still functional but not as clean as a 50-package graph.
What's next
- SBOM export (CycloneDX/SPDX)
- Remediation suggestions (which minimum version upgrade resolves a CVE)
- GitHub Action for CI/CD integration
The repo is at github.com/KPCOFGS/depgra. MIT licensed. Feedback welcome.
Top comments (0)