For engineers

Too many false CVE alarms? Matching firmware versions right

By Manish SharmaPublished 8 min read

Founder and CEO, MAGDOX. Writes on firmware security and device regulation.

False vulnerability alarms waste engineering time and undermine a buyer's trust in your reports. Most false alarms and missed vulnerabilities in firmware reports come from three places: the wrong version, versions compared in the wrong order, and an advisory sentence read the wrong way. FDIE matches components by package URL against OSV ranges and otherwise against NVD CPE ranges, never by parsing advisory prose. It sorts test builds before their release, treats builds from a commit as not comparable with ranges, and records your team's decisions as VEX statements.

Results as of . We keep improving FDIE’s engine for accuracy, so later runs may score differently; this post records what we measured on that date.

In short

  • Most false alarms and missed vulnerabilities come from three places: the wrong version, versions compared in the wrong order and advisory text read the wrong way.
  • FDIE matches only against machine-readable OSV and NVD CPE ranges, never advisory prose. It sorts test builds before their release and keeps builds from a commit out of range matching.
  • Your team records each decision as a VEX statement, which FDIE keeps across data refreshes and exports as CycloneDX, OpenVEX or CSAF 2.0.

Ask a device maker what they think of vulnerability scanners and you will hear the same complaint. The list is long, half of it does not apply, and after a few releases nobody trusts it. False alarms cost engineering days. Missed vulnerabilities cost far more, and they hide in the same places the false alarms come from.

In October 2026 we rebuilt the analysis engine behind FDIE, vulnerability matcher included. We audited it by replaying the inputs of our previous engine's test suite through the new code. Of 3,890 inputs, 3,882 gave the same answer and the other 8 are explained. This post uses the real cases from that audit to show where matching goes wrong and how to check any tool, ours included.

Why do vulnerability scanners raise false alarms?

They come from three places: the wrong version, versions compared in the wrong order, and advisory text read the wrong way.

A fourth case is not a mistake at all. The component and version really are affected, but the vulnerable code cannot be reached or attacked in your product. That is not a matching problem but a decision your team records, and VEX exists to record it.

Start with the right version

Matching can be no better than the version it starts from. So FDIE takes versions from the strongest evidence it has, and shows which evidence that was.

A version read from the package database (opkg, dpkg or apk) is strong. A version string found inside a program is good. A version inferred from a library name or a code signature is weaker. FDIE shows the identification method and a confidence for each component, so your triage can treat them differently.

We measure this against lists we did not write. OpenWrt publishes a manifest of every package in each image. On four OpenWrt images, FDIE found every package at the manifest's version:

Start with the right version
OpenWrt imageManifest packages found at the same version
PowerPC144 of 144
MIPS64115 of 115
ARC116 of 116
RISC-V 64106 of 106

On an earlier set of 17 OpenWrt images, it found 194 of 194 components.

The components list for an OpenWrt release filtered to dnsmasq, with its version, where it was found, how it was identified and its confidence.

How should a component be matched to a vulnerability?

FDIE matches a component that has a package URL against OSV's affected ranges, and everything else against NVD's CPE ranges. It never decides from an advisory's free text.

A package URL names a component in a way tools agree on, such as an ecosystem, a name and a version. The OSV schema records affected versions as ranges of events: introduced, fixed, last_affected and limit, plus an explicit list of versions. The schema says a version is affected "if it lies within any one of the ranges or is listed in the versions list".

NVD records affected products as CPE names with start and end versions, each inclusive or exclusive. Both are machine-readable, and both can be checked.

How should a component be matched to a vulnerability?
IdentifierExample (name, then the version it carries)Matched against
Package URL (purl)pkg:deb/debian/openssl, version 3.0.11-1OSV affected ranges for that ecosystem
Package URL (purl)pkg:generic/busybox, version 1.36.1OSV, where a generic record exists
CPE namecpe:2.3:a:openssl:openssl:3.0.11:*:*:*:*:*:*:*NVD configurations with start and end versions
CPE namecpe:2.3:a:busybox:busybox:1.36.1:*:*:*:*:*:*:*NVD configurations

A component that carries neither has nothing a range can be checked against, which is why identification comes first.

Some matchers add a fallback for advisories without that data. They parse descriptions for phrases such as "before X", "up to X" or "A through B". Our previous engine had such fallbacks, and guards against misreading them. The new matcher has none, on purpose, and the next section is why.

Why is advisory text a trap?

A sentence about one part of a product can read like a sentence about the whole product. A matcher that parses prose will eventually get one wrong in the dangerous direction.

CVE-2025-9230, OpenSSL. The OpenSSL advisory of 30 September 2025 says the FIPS modules in 3.5, 3.4, 3.3, 3.2, 3.1 and 3.0 "are not affected by this issue". It then gives the reason: the CMS code is "outside the OpenSSL FIPS module boundary". The next line lists OpenSSL 3.5, 3.4, 3.3, 3.2, 3.0, 1.1.1 and 1.0.2 as vulnerable, fixed in 3.5.4 and 3.0.18 among others.

A text guard in our previous engine read the first sentence as being about the library, and dropped the CVE for OpenSSL 3.5.0 and 3.0.1. That is a missed vulnerability: an out-of-bounds read and write that OpenSSL rates moderate, in a library many firmware images carry.

CVE-2023-6237, OpenSSL. The advisory of 15 January 2024 says "The OpenSSL 3.0 and 3.1 FIPS providers are affected". The same kind of guard read that as naming the affected branches, and dropped the CVE for 0.9.8 and 1.1.1.

The outcome happened to be right, but for the wrong reason. The advisory lists 3.0.0 to 3.0.12, 3.1.0 to 3.1.4 and 3.2.0 as vulnerable, and says 1.1.1 and 1.0.2 are not affected. The new matcher reaches the same answer from the ranges.

When we replayed the 378 guard calls from the previous engine's tests, 372 gave the same answer as before. The other 6 are these two OpenSSL cases, where the advisories' own text shows the old reading was wrong.

Version order is not string order

A test build comes before its release, and a matcher that does not know the project's naming will sort it after.

dnsmasq publishes test builds before each release. Its test-releases directory, for example, lists 2.87test1 to 2.87test9 ahead of 2.87. A router running dnsmasq 2.86test3 runs code from before 2.86.

If the comparison does not recognise "test" as a pre-release marker, 2.86test3 sorts after 2.86. An advisory whose affected range ends before 2.86 then no longer matches the device. That is a missed vulnerability on one of the most common daemons in routers.

Our previous engine got this right, and the first version of the new matcher did not. The audit replay caught it, and the fix counts "test" as a pre-release word. All 89 version comparisons from the old test suite now agree. When you evaluate a tool, give it 2.86test3 and a range "before 2.86", and see what it says.

Builds from a commit

A program built from a source checkout carries a commit, not a release. Comparing it with release ranges produces noise in both directions.

Go is the clearest case. A Go main module built in place reports its version as "(devel)". FDIE derives Go's own pseudo-version from the commit recorded in the program's build information. The Go Modules Reference describes the form, for example vX.0.0-yyyymmddhhmmss-abcdefabcdef when there is no base version.

FDIE marks such a version as built from a commit and matches it only against explicit lists of affected versions, never against ranges. "A commit from some date" cannot honestly be placed inside "introduced at 0, fixed in 1.4". A dependency pinned at a pseudo-version, which the Go module proxy resolves like any release, is still matched normally.

When the version did not change but the fix is there

Vendors and distributions backport fixes without changing the version string, so FDIE also looks at the code.

Patch checks compare the disassembled code of the release with what a fix looks like. They give the vulnerability a state: fix verified, patch candidate or still vulnerable. So they can find a backported fix even when the version string stayed the same.

Function use, from the call graph, says whether your programs call the vulnerable function at all. Neither of these decides a VEX statement on its own. They are the evidence your engineer reads before deciding.

Recording the decision: VEX

Once your team has decided whether a matched vulnerability affects the product, a VEX statement records that decision. It does so in a form your customers and their tools can read.

The statuses are the same across the standards: not affected, affected, fixed and under investigation, as in CISA's minimum requirements for VEX. CERT-In's Technical Guidelines on SBOM (version 2.0, 9 July 2025) ask suppliers of software to government and public sector organisations to publish VEX with exactly these four. OpenVEX defines five justifications for "not affected", and requires either a justification or an impact statement:

  • component_not_present
  • vulnerable_code_not_present
  • vulnerable_code_not_in_execute_path
  • vulnerable_code_cannot_be_controlled_by_adversary
  • inline_mitigations_already_exist

In FDIE, people write VEX statements. The evidence the analysis has is shown next to the vulnerability: how the component was identified, function use and the patch check state. Decisions are kept when the vulnerability data refreshes, and you can export them as CycloneDX, OpenVEX or CSAF 2.0.

The vulnerabilities list for an OpenWrt release with severity, function use and decision columns, and the VEX export formats.

A checklist for your own triage

Whatever tool you use, these questions catch most bad matches before they reach a ticket or a customer.

  1. Where did this version come from: a package database, a string, or a guess from a name?
  2. Is it a test build, a pre-release or a build from a commit?
  3. Which range matched, and from which source: an OSV record or an NVD CPE entry?
  4. Is the CVE in the CISA KEV catalogue, and what is its EPSS probability?
  5. Does any program in the image call the vulnerable function?
  6. Is there a backported fix in the code even though the version did not change?
  7. Is the decision written down as a VEX statement with a justification, so the next release starts from it?

A matcher that answers the first three for every row, and a team that answers the last four, will produce a list people trust again.

Questions

Why does FDIE not match vulnerabilities from the advisory text?

Advisory sentences are written for people and are easy to misread. In the OpenSSL CVE-2025-9230 advisory, a sentence saying the FIPS modules are not affected led an older text rule to drop a vulnerability the library really has. So FDIE decides from machine-readable OSV and CPE ranges only.

How does FDIE compare a version like dnsmasq 2.86test3?

It treats "test" as a pre-release marker, so 2.86test3 sorts before 2.86 and falls inside an advisory range that ends before 2.86, as it should.

Which VEX statuses and formats does FDIE support?

The four standard statuses, not affected, affected, fixed and under investigation, with a justification for not affected. Statements are written by your people and exported as CycloneDX, OpenVEX or CSAF 2.0.

What if a vendor backported a fix without changing the version string?

FDIE's patch checks compare the disassembled code and give the vulnerability a state of fix verified, patch candidate or still vulnerable. So a backport can be found even when the version did not change.

Sources

  1. OSV schema (affected ranges and events) ossf.github.io
  2. NVD, Common Platform Enumeration (CPE) nvd.nist.gov
  3. Package URL (purl) specification github.com
  4. OpenSSL Security Advisory, 30 September 2025 (CVE-2025-9230) openssl-library.org
  5. OpenSSL Security Advisory, 15 January 2024 (CVE-2023-6237) openssl-library.org
  6. dnsmasq test releases thekelleys.org.uk
  7. Go Modules Reference, pseudo-versions go.dev
  8. OpenVEX specification github.com
  9. CISA, Minimum Requirements for Vulnerability Exploitability eXchange (VEX) cisa.gov
  10. OASIS CSAF 2.0 docs.oasis-open.org
  11. CERT-In Technical Guidelines on SBOM, QBOM and CBOM, AIBOM and HBOM, version 2.0 (9 July 2025) cert-in.org.in

OpenWrt is a registered trademark of Software Freedom Conservancy; see openwrt.org. Product and project names are trademarks of their owners, used only to identify the public firmware, software and standards discussed. MAGDOX is not affiliated with or endorsed by them.

  • For engineers

    What happens to your firmware after you upload it to FDIE

    Before you upload firmware anywhere, you should know what happens to it. FDIE unpacks a firmware image with its own readers, disassembles the programs inside and lists the components and their versions as an SBOM. It matches them to NVD and OSV with known exploited vulnerabilities first, and runs its device checks. Whatever it could not read or decide is shown as a coverage gap, never as a pass, and every result carries the evidence behind it.

    · 9 min read

  • For leaders

    EU CRA timeline and checklist for device makers

    The EU Cyber Resilience Act applies to connected products sold in the EU, wherever they are made. It has been in force since 10 December 2024. Reporting of actively exploited vulnerabilities and severe incidents applies from 11 September 2026, and the rest, from the Annex I requirements to the CE marking, from 11 December 2027. This guide puts the dates, the Article 14 clocks and the technical file on one page, with a checklist to work through.

    · 6 min read

  • For leaders

    Exporting devices to the EU? What the CRA asks of you

    Indian makers who sell into the EU are covered by the Cyber Resilience Act even without an EU office. The Cyber Resilience Act applies to Indian companies whose connected products are sold in the EU, through their own brand or through an importer. Since 11 September 2026 they must report actively exploited vulnerabilities within 24 hours. From 11 December 2027, every product they place on the EU market must meet the essential requirements, with a technical file kept for at least ten years.

    · 7 min read

See what is inside your next release

Book a 30 or 45 minute demo with the team that builds FDIE, or request access for your organisation.

Request access