CVEs & SBOM
The fourth security surface answers a different question from the other three. Findings asks what is wrong with the code; indicators asks what the binary does; malware asks whether it is malicious. This surface asks whether the binary contains a known-vulnerable version of a known component.
3 detection layers · 21 rule entries · 22 distinct CVE IDs · one pinned wire schema (openbinary.package-sbom-v1).
The mechanism is version comparison, not code-shape analysis. The engine unpacks a firmware blob or container, identifies the components inside, extracts each component's version string, and compares against curated rules that map (component, max_vulnerable_version) pairs to MITRE CVE IDs. The output is a software bill of materials with CVE attribution per component.
This is the right tool for firmware audits, supply-chain reviews, and any context where the binary in question is a wrapper around a tree of upstream packages. It is the wrong tool for the binary itself — does your code contain a vulnerability? is what Findings answers.
CVE detail attached to each component is upstream fields only: name, severity, description. No patch guidance, no "upgrade to version X" recommendation, no remediation advice. The no-remediation policy is intentional and applies across the entire engine.
Coverage at a glance
| Layer | Rules | CVEs | What it catches |
|---|---|---|---|
| L1 path patterns | 6 | 3 | Vendor binaries without version banners (Zyxel, generic Apache / Samba / CUPS); 3 rules are presence-only without CVE attribution |
| L2 version banners | 7 | 17 | BusyBox, OpenSSL, Dropbear (×2), dnsmasq, Linux kernel (presence-only), OpenSSH |
| L3 firmware version | 8 | 4 | Zyxel NAS326 (AAZF.4/5/6), VMG8825, ZyWALL / USG (V4.6/70/71/72) — vendor version ranges share CVEs |
| Total | 21 | 22 unique (CVE-2020-9054 and CVE-2023-28771 each appear in both L1 and L3) |
Coverage is intentionally narrow and high-precision. The curation rule for a new entry: it has to be either (a) an upstream open-source package with a stable version banner and a tracked CVE list, or (b) a vendor firmware variant with a published advisory that names the version range. New entries land via PR with the advisory reference in the commit message; nothing is auto-imported from NVD.
The three-layer stack
Three independent layers run in sequence and aggregate into one SBOM document. Each layer trades a different mix of precision and recall.
L1 — Path patterns
Substring-matching against the extracted filesystem tree. Six entries flagging vendor-specific binaries that don't carry a version banner — /zysh (Zyxel shell, CVE-2022-30525), weblogin.cgi (CVE-2020-9054), and generic component paths like samba, apache-httpd, cups, and /pluto (IPsec). The weakest layer by design: confirms presence but not version. False-positive rate is high for vendor-specific entries that already shipped a patched build, but the false-negative rate is low — anything that ships the named binary at all gets flagged for review.
L2 — Per-binary version banners
The strongest layer for upstream open-source packages. Seven rules across six upstream components — BusyBox, OpenSSL, Dropbear (two rules), dnsmasq, Linux kernel, OpenSSH. Each rule names a regex prefix ("BusyBox v", "OpenSSL ", "dropbear_") and a maximum vulnerable version. The pipeline walks each extracted binary's NUL-terminated printable runs, matches the prefix, parses the version, and does a semver-style comparison.
Example matches:
- BusyBox
1.34.0→ CVE-2018-1000517, CVE-2021-42378, CVE-2022-30065. - OpenSSL ≤
1.1.1k→ CVE-2021-3711, CVE-2021-3712, CVE-2022-0778, CVE-2019-1559. - dnsmasq ≤
2.82→ the seven-CVE DNSpooq cluster (CVE-2020-25681 through CVE-2020-25687).
When an upstream package ships a version banner, the match is precise and the version-floor check is exact. The trade-off: custom builds that strip the banner (or vendors that fork without preserving it) are misses. The version comparator is conservative on parse failure — if the string doesn't parse, the rule does not fire.
L3 — Firmware-version lookup
The strongest layer for vendor-specific CVEs that map directly to a firmware version. Eight rules covering vendor firmware version strings — Zyxel NAS326 V5.21(AAZF.4) and adjacent AAZF variants, VMG8825 7.3.245.300, ZyWALL and USG V4.x releases. The pipeline reads the container header's version string and substring-matches against the rule table.
Example matches:
- NAS326
V5.21(AAZF.4)→ CVE-2020-9054 (the unauthenticated RCE viaweblogin.cgi). - ZyWALL
V4.x→ CVE-2023-28771 (the unauthenticated IKE RCE). - VMG8825
7.3.245.300→ CVE-2024-40891, CVE-2025-0890 (recent Zyxel router vulnerabilities).
The firmware-level signal is decisive — a router shipping NAS326 V5.21(AAZF.4) has CVE-2020-9054 regardless of which individual binary the analyst is looking at. Requires manual curation per vendor; the rule table grows as new vendor advisories land.
The SBOM schema
Every consumer reads the same wire shape: openbinary.package-sbom-v1.
Per-component row.
| Field | Meaning |
|---|---|
name | Component name (busybox, openssl, nas326-firmware) |
version | Extracted version string, or null for L1 (path-pattern, no version) |
category | crypto, network, core, vendor, os, etc. — coarse bucket for grouping in the UI |
supplier | Upstream project or vendor (OpenSSL Project, Zyxel, BusyBox) |
evidence | Which layer matched: binary_version, path_pattern, or firmware_version |
binaries | Count of extracted files referencing this component (multiple ELFs linking the same OpenSSL count once at the component level) |
example_paths | Up to three sample paths into the extracted tree |
cves | Array of CVE IDs matched to this (component, version) pair |
Top-level aggregation.
| Field | Meaning |
|---|---|
firmware_version | Container-header version string, nullable |
firmware_cves | CVE IDs matched at the firmware-version layer (L3) |
components | Sorted: CVE count desc, then binary count desc, then name asc |
stats | files_extracted, components_total, components_versioned, components_with_cves, components_vendor, unique_cve_ids |
Worked example — NAS326 firmware V5.21(AAZF.4).
firmware_version: "V5.21(AAZF.4)"
firmware_cves: ["CVE-2020-9054"]
top component: openssl 1.0.2h
4 CVEs across 17 binaries
CVE-2019-1559, CVE-2021-3711, CVE-2021-3712, CVE-2022-0778
stats: 6513 files extracted, 10 components total,
5 versioned, 7 with CVEs, 3 vendor,
11 unique CVE IDs
How it integrates
The producer unpacks a firmware image into a temp dir, runs all three CVE scanners over the extracted tree, aggregates per (name, version) key, serializes to JSON, and drops the temp dir before return. No on-disk extracted tree survives the call. Per-file read errors are tolerated (capped at 4 MiB per file); a single unreadable file does not abort the SBOM build.
The consumer side in openbinary accepts the produced JSON at POST /api/v1/package/{sha256}/sbom, validates the schema header, and persists to its package store. The frontend renders the row table with no shape transformation — the schema is the contract.
openbinary.package-sbom-v1 is the pinned name. Field additions within v1 are non-breaking (the frontend drops unknown fields); field-semantics changes bump to v2.
What this surface does not do
- No live NVD joins. The
cves[]array is raw CVE IDs. Consumers that want CVSS, EPSS, KEV catalog membership, or vendor advisory text must join against an external source themselves. A live NVD lookup would tie output correctness to an external service the engine cannot guarantee, and the no-remediation policy means the engine has no use for the patch metadata that the enrichment would mostly add. - No CPE matching. L2 and L3 are hand-curated rules, not algorithmic CPE-string lookups. The trade-off is precision: a CPE lookup catches more components but produces enough false positives that triage cost outweighs the recall gain.
- No exploitability scoring. Severity per finding is the upstream CVE's published severity, nothing more. Whether the vulnerable version is reachable from a network-facing surface in this particular deployment is a question the engine does not have the context to answer.
- No remediation guidance. Project-wide policy. Upstream fields only.
- No function-fingerprint layer yet. The
evidence: "fingerprint"discriminant is reserved in the schema for a future layer that fingerprints individual functions against a known-vulnerable corpus. The openbinary function pipeline already produces the fingerprints; the SBOM layer is not yet wired to consume them. When it lands, it closes the version-stripped-binary gap that L2 misses today. - No license metadata. Detected components are inventory only. SPDX license assignment is downstream of identification, and the engine does not ship a license catalog.
Where this fits
CVE/SBOM is the parallel-but-orthogonal fourth surface. The other three security surfaces (Findings, Indicators, Malware) run on the binary's own code; this one runs on the inventory of upstream components the binary bundles. A firmware audit needs all four — bundled OpenSSL with a known CVE is a different problem from the firmware's own custom HTTP daemon having a stack-overflow CWE, and both are different from the firmware shipping a backdoor that scores Malicious on the verdict surface.
See Findings for the code-shape CWE catalog, Indicators for capability tagging, Malware for the verdict composer, and Architectures for the unpack-format coverage matrix.