Most package ecosystems ship a native audit tool — npm audit, pip-audit, govulncheck, cargo audit, bundler-audit, dotnet audit, composer audit — and Java teams use OWASP Dependency-Check. All of them surface CVEs, though pip-audit shows no severity column. None include EPSS exploitation probability, and only Dependency-Check flags CISA KEV entries, which is the gap this guide maps ecosystem by ecosystem.
A backend team ships a Node.js API, a Python data pipeline, and a Go service for infrastructure work. Security asks for a vulnerability report. The team runs npm audit, pip-audit, and govulncheck separately — three different formats, three different severity scales, three dashboards. Nobody can tell the CISO which dependency represents the highest actual risk across the entire platform.
This is the multi-ecosystem problem in practice. Each language ecosystem has a native vulnerability scanner. Each one does its job reasonably well within its own domain. But none of them speak to each other, and none of them answer the question teams need answered: "Which of these 340 findings requires a patch before end of day?" Understanding what dependency vulnerability scanning actually involves — matching dependency versions against known CVEs, then layering EPSS and KEV on top — is the first step toward one report that covers your entire stack, not just whichever ecosystem's native tool happens to run in CI. For a deeper look at how CVSS-only ranking creates alert fatigue on its own, see our vulnerability prioritization guide.
What We'll Cover
- Why ecosystem-specific scanning has a ceiling
- npm / Node.js
- Python / PyPI
- Java / Maven
- Go modules
- Rust / crates.io
- Ruby / RubyGems
- .NET / NuGet
- PHP / Packagist
- Multi-ecosystem projects: scanning a polyglot stack
- FAQ
Why Ecosystem-Specific Scanning Has a Ceiling
Every native scanner in this guide works the same way at its core: it parses your manifest or lock file, matches installed versions against a vulnerability database, and returns findings labelled by severity at best — a workflow that catches real vulnerabilities but, apart from OWASP Dependency-Check's KEV flag, has no way to tell you which ones anyone is actually trying to exploit.
- Parse your manifest or lock file (
package-lock.json,go.sum,Cargo.lock, etc.) - Match installed versions against a vulnerability database (OSV, NVD, an ecosystem-specific advisory feed, or some combination)
- Return findings, at best labelled by CVSS severity — Critical, High, Moderate, Low
This is useful. Running zero tools is worse than running one tool with limitations. But there's a hard ceiling on what severity-labelled output tells you.
CVSS measures theoretical severity — how bad a vulnerability could be if a specific attacker exploited it in a specific way against a specific configuration. It doesn't measure whether anyone is actually trying to exploit it. A CVSS 9.8 in a parsing library with no public exploit code and zero PoC activity should not receive the same response urgency as a CVSS 6.5 with an active Metasploit module and a CISA KEV entry.
Two signals close this gap:
EPSS (Exploit Prediction Scoring System): A machine-learning model maintained by FIRST.org that scores every CVE with a probability (0.0–1.0) of exploitation in the next 30 days. It's updated daily as exploit code, threat intelligence, and malware analysis data flows in. A CVE with EPSS 0.03 is unlikely to be exploited anytime soon. A CVE with EPSS 0.78 is actively being weaponized.
CISA KEV (Known Exploited Vulnerabilities): A CISA-curated catalog of vulnerabilities with confirmed active exploitation in the wild. KEV membership means threat actors are using this CVE in live attacks right now — not "could be used," not "has a PoC" — actively being used.
Every ecosystem section below covers the native tool's strengths and the specific gaps EPSS and KEV fill.
Each native tool also pulls from a different upstream advisory source — GHSA, NVD, or an ecosystem-specific feed. GeekWala's cross-ecosystem scan sidesteps that fragmentation by querying OSV.dev, which already aggregates those sources into one normalized schema, instead of integrating each of the eight advisory feeds separately.
Normalizes package, version-range, and severity data from every upstream source into one queryable format.
Batch-queries OSV.dev once per scan instead of hitting GHSA, NVD, and every ecosystem feed independently.
One aggregation layer means one integration to maintain, and results that stay consistent across every ecosystem GeekWala supports.
npm / Node.js
npm / Node.js relies on npm audit, built into the npm CLI and run automatically on every install. It queries the npm Security Advisory Database, matches your package-lock.json against known-vulnerable versions, and returns findings labelled by severity — but with no EPSS or KEV signal layered on top.
Native tool: npm audit
npm audit is embedded in the npm CLI and runs automatically on npm install. It queries the npm Security Advisory Database (backed by GitHub Advisory and NVD), matches your package-lock.json against known vulnerable versions, and returns results labelled by severity.
What it does well: Zero setup. Runs on every install without configuration. npm audit fix can automatically resolve many findings within semver constraints. Reports include the full transitive dependency chain, so you know exactly which direct dependency pulled in the vulnerable package.
What it misses:
| Limitation | Impact |
|---|---|
| Severity labels only, at best | High and Critical findings pile up; teams can't distinguish urgent from noise |
| No EPSS scores | Can't identify which findings are being actively exploited |
| No CISA KEV integration | No flag for vulnerabilities with confirmed live exploitation |
| npm ecosystem only | Doesn't see your Python or Go dependencies |
| DevDependency blind spot | Flags devDependencies alongside production ones; severity not adjusted for non-production risk |
A team running a typical React application will see 30-80 npm audit findings. With EPSS filtering, the number requiring immediate action usually drops to 2-5. The rest are real vulnerabilities worth patching — but on a normal sprint schedule, not emergency response.
Deep dive: npm audit vs GeekWala: Why CVSS Scores Alone Are Misleading
Python / PyPI
Python / PyPI's native tool is pip-audit, created by Trail of Bits with support from Google and maintained under the Python Packaging Authority. It checks packages against the PyPI or OSV vulnerability service, supports requirements.txt and pyproject.toml, and — like every native scanner here — gives no exploitation signal; its output doesn't even include a severity column.
Native tool: pip-audit
pip-audit checks installed packages against the PyPI vulnerability service (PyPA's PYSEC advisories) or, optionally, OSV. It supports requirements.txt, pyproject.toml (via pip-compile), and virtual environment scanning.
What it does well: Can check against OSV as well as PyPI. Supports --fix for automatic resolution. Works with multiple dependency formats. The PYSEC advisory feed typically lands the same day as the NVD entry (median 0 days), so it keeps pace with NVD for Python-specific issues.
What it misses:
| Limitation | Impact |
|---|---|
| No EPSS or KEV | No severity column either — nothing to rank findings by |
| PyPI ecosystem only | Doesn't see your npm or Go stack |
| Lock file gaps | Doesn't natively parse Pipfile.lock or poetry.lock without conversion |
| Advisory coverage | Its limit is coverage, not freshness: PyPI advisories typically appear the same day as the NVD entry (median 0 days, n=738), but about 5.6% have no CVE ID, so CVE-keyed tools never see them |
| Transitive depth | Deep dependency chains can obscure which package introduced a vulnerability |
Python's multiple competing package formats (pip, poetry, pipenv, conda) and lack of a single authoritative lock file standard create real fragmentation — but not a timing gap. In 28.6% of the cases we measured (n=738), the PyPI advisory landed before the NVD entry. The fragmentation costs you coverage, not speed.
Deep dive: What pip-audit Misses and the Python dependency security guide
Java / Maven
Java / Maven has no built-in advisory scanner — teams add the OWASP Dependency-Check Maven plugin, which queries NVD via CPE matching to flag vulnerable artifacts. It's thorough but prone to false positives with shaded JARs, and it shows CVSS severity and flags CISA KEV entries, but has no EPSS context.
Native tool: mvn dependency-check (via OWASP Dependency-Check Maven plugin)
Java's vulnerability scanning story is fragmented. Maven ships no built-in advisory scanner — you add the OWASP Dependency-Check plugin to your pom.xml. It queries NVD using CPE (Common Platform Enumeration) matching to identify vulnerable artifacts.
What it does well: Broad artifact coverage for the Maven ecosystem. Generates detailed HTML and XML reports suitable for audit trail requirements. Integrates with Maven and Gradle build lifecycle. Large community documentation and enterprise adoption.
What it misses:
| Limitation | Impact |
|---|---|
| CPE matching heuristics | High false positive rate, especially with shaded JARs and BOM imports |
| NVD-only advisory source | Misses GitHub Advisory (GHSA) entries not yet propagated to NVD |
| No EPSS | KEV entries are flagged, but nothing ranks the rest by exploitation probability |
| NVD API rate limits | Without an NVD API key, initial database sync takes hours |
| Transitive shading | Shaded JARs (uber-JARs) embed dependencies that scanners often miss |
The false positive rate is the biggest practical problem for Java teams. CPE matching is imprecise when the same library appears under different artifact coordinates or when shading changes the internal package structure. Teams spend significant time manually triaging false positives — time that isn't available for genuine vulnerabilities.
Deep dive: Java Dependency Security: What Maven Dependency-Check Misses
Go Modules
Go's native tool, govulncheck, is the strongest in this list — it performs call-graph analysis to check whether your code actually calls a vulnerable function, not just whether the package is present, which cuts false positives significantly. It still has no EPSS or KEV signal on top of that reachability check.
Native tool: govulncheck
govulncheck is the official Go vulnerability scanner, built by the Go security team and maintained alongside the language itself. It uses the Go Vulnerability Database and performs call-graph analysis — it can determine whether your code actually calls vulnerable functions, not just whether the package is in your go.sum.
What it does well: Call-graph analysis is unique among native ecosystem tools. Instead of flagging every package with a known CVE, govulncheck checks whether the vulnerable code path is reachable from your application. This reduces false positives significantly. The Go Vulnerability Database is authoritative, well-curated, and maintained by the Go team. Works offline with a local database copy.
What it misses:
| Limitation | Impact |
|---|---|
| No EPSS or KEV | Reachability analysis reduces noise, but not exploitation signal |
| Conservative advisory database | Go Vuln DB is smaller than NVD; some CVEs appear there first, others appear only in NVD |
| Call-graph requires source | Can't scan from lock file alone — needs full source for reachability analysis |
| Go ecosystem only | Doesn't cover your Python or Node.js services |
| Indirect dependency blindness | go.sum can have 1,000+ entries; transitive vulnerabilities are easy to miss in monitoring |
Go teams have the best native scanner in this list thanks to call-graph analysis. But "best native tool" still means no exploitation signals. A govulncheck finding at CVSS 7.5 could have EPSS 0.8 (patch immediately) or EPSS 0.02 (patch next quarter). Without that data, every finding gets the same triage priority.
Deep dive: Go Module Security: Beyond govulncheck for Production Dependencies
Rust / crates.io
Rust's cargo audit scans your Cargo.lock against the community-maintained RustSec Advisory Database — exceptionally well-curated, with a low false-positive rate, and uniquely willing to flag unmaintained crates alongside vulnerable ones. It shares the same gap as every other native tool here: no EPSS score, no KEV cross-reference.
Native tool: cargo audit
cargo audit scans your Cargo.lock against the RustSec Advisory Database, a community-maintained advisory feed specifically for the Rust ecosystem. The companion tool cargo-deny extends this with license compliance checking and duplicate dependency detection.
What it does well: RustSec is exceptionally well-curated — low false positive rate. Community-maintained with active participation from crate authors. Uniquely, cargo audit surfaces warnings for unmaintained crates, not just vulnerable ones. cargo-deny adds policy enforcement useful for enterprise teams. Fast execution, minimal dependencies.
What it misses:
| Limitation | Impact |
|---|---|
| No EPSS or KEV | Advisory severity without exploitation signal |
| Smaller advisory database | RustSec's conservative curation means some NVD CVEs aren't covered |
-sys crate blindness | C binding crates (FFI wrappers) can carry C library vulnerabilities that RustSec doesn't track |
| crates.io only | Doesn't see adjacent Node or Python dependencies |
| Unmaintained warnings are noise-heavy | Useful signal, but teams can't easily distinguish "unmaintained but stable" from "unmaintained and dangerous" |
Rust's memory safety guarantees eliminate entire vulnerability classes (buffer overflows, use-after-free, format string bugs) that dominate advisories in C and C++ ecosystems. But Rust applications still depend on network libraries, parsing code, cryptography implementations, and database drivers — all of which carry conventional security vulnerabilities. cargo audit catches these; it just can't tell you which ones are being actively weaponized.
Deep dive: Rust Dependency Security: What cargo audit Misses in 2026
Ruby / RubyGems
Ruby's bundler-audit scans your Gemfile.lock against the community-maintained Ruby Advisory Database, pulling in advisories from NVD, CVE, and vendor sources. It's lightweight, fast, and Bundler-aware, but it's a separate gem you must install explicitly, and — like the rest — it has no EPSS or KEV layer.
Native tool: bundler-audit
bundler-audit scans your Gemfile.lock against the Ruby Advisory Database, a community-maintained collection of vulnerabilities affecting RubyGems packages. It's a standalone gem, not a built-in bundle command, and requires explicit installation.
What it does well: Focuses specifically on the Ruby ecosystem. The Ruby Advisory Database includes advisories from multiple sources (NVD, CVE, vendor advisories). Understands Bundler's dependency resolution. Lightweight and fast.
What it misses:
| Limitation | Impact |
|---|---|
| No EPSS or KEV | CVSS-based severity only |
| Not built into Bundler | Requires separate installation; often not in base CI images |
| Advisory database size | Smaller than NVD; some CVEs take longer to appear in Ruby Advisory Database |
| RubyGems only | Doesn't surface vulnerabilities in JavaScript assets served by Rails apps |
Rails applications commonly bundle both a Gemfile.lock (Ruby dependencies) and a package-lock.json or yarn.lock (JavaScript dependencies via Webpack or Propshaft). bundler-audit sees one half of that picture. A cross-ecosystem scan catches both.
Deep dive: Ruby Dependency Security: What bundler-audit Misses
.NET / NuGet
.NET ships vulnerability checking natively via dotnet list package --vulnerable and the newer dotnet audit, both querying Microsoft's NuGet advisory database — strong for Microsoft-published packages, weaker for third-party ones, with 46.8% of NuGet OSV advisories carrying no CVE identifier at all. No EPSS or KEV context is included.
Native tool: dotnet list package --vulnerable
.NET ships a built-in vulnerability check via the dotnet CLI. Two commands cover this: the older dotnet list package --vulnerable and the newer dotnet audit (available in .NET 8+). Both query Microsoft's NuGet advisory database.
What it does well: Native to the .NET toolchain — no additional installation. Well-maintained NuGet advisory database, especially for Microsoft-published packages. Central Package Management support simplifies vulnerability reporting across large solution files with many projects.
What it misses:
| Limitation | Impact |
|---|---|
| No EPSS or KEV | Severity labels without exploitation context |
| Advisory coverage gaps | Third-party NuGet packages outside Microsoft's ecosystem are less consistently covered — 46.8% of NuGet OSV advisories carry no CVE identifier |
| NuGet ecosystem only | Doesn't see JavaScript bundles or Python scripts in .NET applications |
| No automated fix | Reports vulnerable packages but doesn't suggest upgrade paths or apply patches |
packages.config vs PackageReference | Behavior varies depending on project format; older projects using packages.config may get incomplete results |
.NET applications frequently embed JavaScript for frontend (via npm workspaces, Webpack, or client-side Blazor dependencies) and may invoke Python scripts in data-heavy pipelines. The NuGet scanner misses these entirely.
Deep dive: NuGet Vulnerability Scanning: What dotnet audit Misses
PHP / Packagist
PHP's composer audit (built into Composer 2.4+) checks your composer.lock against the PHP Security Advisories Database, covering major Packagist packages like Symfony, Laravel, Drupal, and WordPress components. It cross-references only that one advisory feed, though, and, like most native tools in this guide, reports advisories with a severity label but no exploit-probability ranking.
Native tool: composer audit
Composer 2.4 shipped a built-in composer audit command that checks your composer.lock against the PHP Security Advisories Database. Earlier Composer versions used the standalone local-php-security-checker tool. Both query the same advisory feed.
What it does well: Built into Composer — no extra installation from Composer 2.4+. The PHP Security Advisories Database is actively maintained by the PHP community. Covers the most widely used Packagist packages including Symfony, Laravel, Drupal, and WordPress components. Fast execution against a well-structured advisory format.
What it misses:
| Limitation | Impact |
|---|---|
| No EPSS or KEV | Advisory severity without exploitation signal |
| PHP Security Advisories only | Doesn't cross-reference NVD or GitHub Advisory; some CVEs appear only in those databases |
| Packagist ecosystem only | Doesn't see npm packages bundled with PHP applications |
| No transitive path detail | Lists vulnerable packages without showing the full dependency chain |
| No scheduled scanning | CI-only; no continuous monitoring between deploys |
PHP applications — especially WordPress, Drupal, and Laravel — often pair composer.lock with package-lock.json for JavaScript assets. Scanning only one side leaves frontend dependencies unchecked. Additionally, PHP has historically been a high-target ecosystem for web exploits, making the absence of KEV data particularly costly when an advisory hits a widely deployed package like Symfony HttpKernel or Laravel's illuminate/http.
Deep dive: PHP Dependency Security: What composer audit Misses
Multi-Ecosystem Projects: Scanning a Polyglot Stack
Most production teams aren't single-ecosystem — a typical SaaS platform mixes npm, PyPI, Maven, Go, and Packagist dependencies across its services, each covered by a different native scanner with a different severity scale, which makes comparing a "High" finding from one tool against a "High" from another meaningless.
Here's what a typical polyglot production application looks like:
| Service | Manifest | Ecosystem | Dependencies |
|---|---|---|---|
frontend/ | package-lock.json | npm | ~800 packages |
api/ | requirements.txt | PyPI | ~120 packages |
infra-service/ | go.sum | Go | ~60 modules |
analytics/ | pom.xml | Maven | ~200 artifacts |
cms/ | composer.lock | Packagist | ~90 packages |
That's 1,270 dependencies across 5 ecosystems, checked by 5 separate scanners, each producing output in a different format with different severity scales. When a new CVE drops, you're opening five tabs, running five commands, and mentally reconciling five lists.
The operational cost of this compounds at every step:
Triage cost: You can't compare a "High" from npm audit against a "High" from mvn dependency-check — they're calculated from different advisory sources with different CPE matching and CVSS interpretation policies.
Coverage gaps: Each native tool sees only its ecosystem. A vulnerability in lodash (npm) doesn't appear in pip-audit. A compromised Python package isn't flagged by govulncheck.
Monitoring blind spots: Most native tools run in CI on each commit. Between deploys, EPSS scores can change significantly as new exploit code appears or threat actors shift attention. A package that was EPSS 0.05 on Monday can reach EPSS 0.72 by Friday without any code change on your end.
Unified scanning across ecosystems: This is where cross-ecosystem tools like GeekWala close the gap. We scan all 8 ecosystems — npm, PyPI, Maven, Go modules, crates.io, RubyGems, NuGet, and Packagist — from a single dashboard. Upload any manifest or lock file (transitive coverage comes from lock files and SBOMs), or connect your GitHub repository to scan across all languages at once. Every finding is enriched with EPSS exploitation probability and CISA KEV status, and ranked CISA KEV first, then EPSS above 10% (30-day exploit probability), then severity, with EPSS breaking ties.
The practical workflow for polyglot teams:
- Local development: native tools (fast, zero setup).
- CI/CD pipeline: native tools plus a GeekWala API scan (gate deploys on EPSS > 0.8 or KEV).
- Production monitoring: GeekWala scheduled scans (daily or weekly) re-rank findings by the current EPSS between deploys.
- GeekWala dashboard: project scans from CI and on a schedule land in one unified view across all ecosystems, ranked by CISA KEV and EPSS.
The native tools stay in the pipeline for fast, ecosystem-specific checks during development. GeekWala works alongside them as a cross-ecosystem prioritization layer — giving your team one ranked list instead of five separate ones.
For a full comparison of how individual ecosystem scanners stack up against each other and GeekWala, see 5 Ecosystem-Native Dependency Scanners Compared.
Ecosystem Comparison at a Glance
| Ecosystem | Native Tool | Advisory Source | EPSS | KEV | Auto-Fix | Call-Graph |
|---|---|---|---|---|---|---|
| npm / Node.js | npm audit | npm + GHSA + NVD | No | No | Yes (npm audit fix) | No |
| Python / PyPI | pip-audit | PyPI (default) or OSV | No | No | Yes (--fix) | No |
| Java / Maven | OWASP Dependency-Check | NVD | No | Yes (flag only) | No | No |
| Go modules | govulncheck | Go Vuln DB | No | No | No | Yes |
| Rust / crates.io | cargo audit | RustSec | No | No | No | No |
| Ruby / RubyGems | bundler-audit | Ruby Advisory DB | No | No | No | No |
| .NET / NuGet | dotnet audit | NuGet + MSRC | No | No | No | No |
| PHP / Packagist | composer audit | PHP Security Advisories | No | No | No | No |
| All 8 ecosystems | GeekWala | OSV | Yes | Yes | No (guidance) | No |
The pattern is clear: every native tool reports findings from its own advisory source, with at most a severity label. None include EPSS, and only Dependency-Check flags CISA KEV entries. govulncheck is the sole exception to one column — its call-graph analysis filters false positives — but it still doesn't surface EPSS or KEV data.
Frequently Asked Questions
Do I need to replace my ecosystem's native scanner?
No. Keep npm audit, pip-audit, govulncheck, and the rest in your CI pipeline. They're fast, free, and catch issues at development time without requiring any account or API setup. They should be your first line of defense. The gap is prioritization: native tools give you a list labelled by severity (pip-audit has no severity column at all), but can't tell you which findings are being actively exploited right now. A cross-ecosystem tool with EPSS and KEV data adds that layer on top of — not instead of — your native scanners.
Why don't the native tools include EPSS?
EPSS is maintained by FIRST.org and updated daily — it scores every published CVE (355,000+ as of mid-2026, and growing as new CVE IDs are assigned). Integrating it requires fetching, caching, and correlating EPSS data with each advisory database. Most native tools are designed to be fast, dependency-light, and capable of running offline. Adding EPSS would require daily network calls and a caching layer that conflicts with these design goals. It's not that ecosystem tool maintainers think EPSS is unimportant — it's an architectural tradeoff.
Which ecosystem has the best native vulnerability scanning?
Go's govulncheck is the standout. Its call-graph analysis distinguishes between "this package is installed and has a CVE" versus "your application actually calls the vulnerable function." This meaningfully reduces false positives. RustSec (the advisory database behind cargo audit) is also exceptionally well-curated with low false positive rates. At the other end, Java's CPE-matching approach in OWASP Dependency-Check produces more noise and requires more manual triage, particularly with shaded JARs.
How should I scan a monorepo with multiple languages?
Scan each manifest or lock file separately. Most lock files are small (a few hundred KB even for large projects), so the overhead is minimal. In CI, run native tools in parallel: npm audit, pip-audit, govulncheck, etc. Run a GeekWala scan on all lock files to get a unified prioritized view. For monorepos with dozens of services, the GeekWala API lets you automate this — submit lock files programmatically and gate deploys on KEV status or EPSS threshold.
What's the best starting point for a team that's currently running no dependency scanning?
Start with the native tool for your primary language — it takes 5 minutes to add to CI and requires no account. Then add composer audit or pip-audit or cargo audit for any secondary languages. Once you're running native scans consistently and have a process for reviewing findings, add a cross-ecosystem scan to get EPSS and KEV prioritization. Don't try to do everything at once — running one ecosystem's native tool reliably is more valuable than running eight tools sporadically.
Does scanning dependency files expose our codebase or proprietary information?
Manifest and lock files (package-lock.json, go.sum, Cargo.lock, etc.) contain package names and version numbers — no source code. They're structured lists of public package identifiers. Submitting them to a scanner doesn't expose business logic, database schemas, or API keys. The only information disclosed is which open-source packages you use — the same information that would be visible in your node_modules directory or any compiled artifact.
How often should I run multi-ecosystem scans?
Run native ecosystem scans on every CI build. Run cross-ecosystem scans (with EPSS and KEV enrichment) at least weekly on your production lock files. EPSS scores change daily as exploit code appears, threat intelligence feeds update, and malware analysis data arrives. A package that was EPSS 0.04 last week can spike to 0.85 this week without any changes to your codebase. Scheduled scans re-rank findings by the latest EPSS between deploys.
Eight ecosystems, one dashboard.
Scan your dependencies across npm, PyPI, Maven, Go, Rust, Ruby, NuGet, and PHP → — upload any lock file and see which findings are being actively exploited, ranked by CISA KEV and EPSS. No account needed for up to 10 packages.


