Dependencies & CVEs
Vulkro matches every dependency in your manifests and lockfiles, direct and transitive, against a local CVE bundle.
vulkro update # refresh the local CVE bundle
vulkro scan . # dependency CVEs are part of every scan
vulkro match-cve --sbom sbom.json # match an existing SBOM (CycloneDX or SPDX)
What is parsed
| Ecosystem | Files |
|---|---|
| npm | package.json, package-lock.json, pnpm-lock.yaml, yarn.lock, bun.lock |
| PyPI | requirements*.txt, Pipfile, pyproject.toml, poetry.lock, Pipfile.lock |
| Go | go.mod, go.sum |
| crates.io | Cargo.toml, Cargo.lock |
| Maven | pom.xml (including <dependencyManagement> and parent POMs inside the repository), gradle.lockfile |
| RubyGems | Gemfile.lock |
| Packagist | composer.lock |
| NuGet | *.csproj, packages.lock.json |
A Gradle project without gradle.lockfile gets source analysis but no dependency CVEs. Transitive hits are reported at Medium confidence and marked (transitive dependency); set VULKRO_SCA_NO_TRANSITIVE=1 to match declared dependencies only. A CVE in a dev-only dependency is held at Low confidence and tagged (dev dependency).
The CVE bundle
The bundle (OSV, NVD, CISA KEV and EPSS) lives in ~/.vulkro/data/cves/. Matching is offline by default. VULKRO_CVE_LIVE=1 adds a live lookup at api.osv.dev that sends only ecosystem, name and version (never for transitive packages). For air-gapped machines, apply a signed file with vulkro update --bundle <file>.vkbundle (see bundle format). --max-bundle-age <HOURS> warns on a stale bundle; add --max-bundle-age-fail to exit 2 instead.
Reachability
Two signals answer "does my code use the vulnerable part?":
- Call-shape tag (npm and PyPI): a finding is tagged
[reachable]when a known call shape for the package (for example_.merge(oryaml.load() appears in your source, else[unreachable]. Unreachable findings drop one severity tier, never removed.--reachable-onlydrops them;--no-reachability-filterturns the pass off. - Call-graph verdict: each finding with known vulnerable symbols gets
reachability_verdict(reachable,unreachableorunknown) plus the entry-point call chain. This is a label only. Add--fail-on-reachable-onlyto stop unreachable CVEs from failing the build.VULKRO_SCA_REACHABLE=0turns it off.
Both are name-based joins and can miss dynamic dispatch, reflection and aliases. Treat reachable as "look here first", not as proof.
Exploitability
Every finding carries an exploitability grade in JSON and SARIF:
| Grade | Meaning |
|---|---|
exploitable | Tainted, unsanitised, user-reachable path to the sink. |
likely | Strong signal, one precondition unproven. |
theoretical | Pattern present, exploitation not shown. |
The grade does not change severity. It orders triage, and feeds the "Fix these first" list (--top N).
KEV and EPSS
| Signal | Effect |
|---|---|
| Listed in CISA KEV (actively exploited) | Severity raised to Critical, tagged [CISA KEV - actively exploited, added <date>] |
| EPSS 0.9 or higher | Severity raised to at least High, tagged [EPSS NN%] |
| EPSS 0.5 to 0.9 | Tagged only |
A dependency matched by a known-malicious-package advisory is reported Critical.
See also: Attack paths, vulkro sbom, vulkro update