Can We Trust NVD’s Vulnerability-Affected Version Information As-Is?
Subtitle: How inaccurate affected-version metadata can distort vulnerability management
Before We Begin..
The National Vulnerability Database (NVD) provides vulnerability information for publicly disclosed security issues.
Among the various pieces of information it publishes, one of the most widely used is which products and versions are affected by a vulnerability. To represent this information, the NVD uses a standardized naming scheme called Common Platform Enumeration (CPE).
For example,
cpe:2.3:a:apache:http_server:2.4.49:*:*:*:*:*:*:*
represents Apache HTTP Server 2.4.49.
Based on CPEs, the NVD records:
- Which versions are affected by a vulnerability
- Which versions contain a fix
Most vulnerability scanners, asset management platforms, and vulnerability management systems rely on this information when determining whether a system may be affected by a given CVE.
0. Why affected-version information matters
Vulnerability response usually starts with a version check.
Check the CVE
-> Identify the product and version in use
-> Look up affected versions in NVD or a vendor advisory
-> Decide whether patching is required
-> Prioritize the response
At the center of this process is one simple question:
Is the version we use actually vulnerable?
NVD’s CPE-based affected software configuration is one of the most widely used clues for answering that question. Vulnerability scanners and vulnerability management systems commonly look up NVD data by product name and version, then check whether the installed version falls within an affected range.
The problem is that this metadata does not always match the actual state of the code.
If a safe version is classified as vulnerable, teams spend time on unnecessary patch reviews and validation. If a vulnerable version is classified as safe, it may be excluded from the patching process. The former increases operational noise; the latter leaves real risk in place.
This article is not an argument against using NVD. NVD is a critical source of vulnerability information and a foundation for many vulnerability management tools. The point is narrower: affected-version information in NVD should be treated as an important starting point, not the final answer. This matters especially for critical systems, old release branches, vendor forks, LTS packages, and software distributed through downstream ecosystems.
1. What does NVD CPE represent?
NVD uses CPE to describe software configurations affected by a CVE. CPE represents vendors, products, versions, and related attributes in a structured naming format.
NVD’s Vulnerability Detail Page displays affected configurations through CPE Match Strings and CPE Match String Ranges. A CPE Match String Range can express affected version intervals using start and end version constraints such as <, >, <=, and >=. For example, it can describe versions of a product from 1.2.0 up to, but not including, 1.2.5 as affected. See NVD’s explanation of Vulnerability Detail Pages.
product: libfoo
affected: >= 1.2.0 and < 1.2.5
This representation is extremely useful at scale. It is not realistic for every organization to inspect source code, patch history, and release branches for every CVE. Version-based metadata helps reduce the search space quickly.
However, real software distribution is often more complex than a simple version interval.
upstream release
-> release branch
-> LTS branch
-> vendor fork
-> distro package
-> backported patch
-> appliance / firmware build
The same version string may contain different code depending on the branch, vendor, distribution, or build process. Conversely, different version names may contain the same vulnerable code. This gap is where affected-version metadata can drift away from the actual code.

2. Common ways affected-version information can drift
Affected-version information can differ from the real code state in several ways. The following patterns are especially relevant in practice.
2.1 The affected range is too broad
Suppose a vulnerability is discovered in version 1.5.0. A database may record the affected range as follows:
affected: <= 1.5.0
But the vulnerable code may have been introduced only in version 1.3.0. In that case, versions 1.0.0, 1.1.0, and 1.2.0 are not actually affected.
The result is unnecessary work. Security teams process tickets that do not correspond to real exposure, and operations teams review upgrades that may not be needed. Repeated false alerts can reduce trust in scanner output and make prioritization harder.
Safe versions classified as affected
-> Unnecessary patch review
-> Alert fatigue
-> Confusing vulnerability prioritization
2.2 The affected range is too narrow
The opposite case is more dangerous.
affected: <= 2.4.1
fixed: 2.4.2
An operations team upgrades to 2.4.2 and considers the issue resolved. But the patch may have landed in the main branch while never being backported to release/2.4.x. Or the release notes may say that the issue was fixed, while the actual distributed package does not include the relevant patch.
The metadata says the version is safe, but the code remains vulnerable.
metadata: fixed
code: still vulnerable
This is more dangerous than a false positive because the vulnerable version can be removed from the response queue.
2.3 Backports are not fully reflected
Security patches are not always delivered only through the latest release. They are often backported to old LTS branches or distribution packages.
main: fixed in 2.0.0
lts/1.4: fixed in 1.4.8-lts
If the affected range is expressed only as:
affected: < 2.0.0
then 1.4.8-lts, which already contains the backported fix, may still be classified as vulnerable. The opposite can also happen: a fixed version may be recorded based on one branch, while another release branch remains unpatched.
Version numbers alone cannot fully describe whether a security patch is actually present.
2.4 Product names, package names, repositories, and binaries do not always align
CPE is product-name oriented. Real development and distribution, however, happen through many different units: Maven artifacts, npm packages, PyPI packages, Go modules, Linux distribution packages, container images, firmware bundles, and vendor-specific builds.
Google made a related point when launching OSV: existing vulnerability standards and versioning schemes, particularly CPE, do not map cleanly to the tag- and commit-hash-based versioning used in open source, which can cause downstream consumers to miss vulnerabilities that affect them. See Google’s announcement, Launching OSV - Better vulnerability triage for open source.
In practice, the following identifiers may not refer to exactly the same artifact:
CPE product name
!= package name
!= source repository
!= distro package
!= deployed binary
When this mapping is unstable, affected-version decisions become unstable as well.
3. This problem has been observed in prior research
The risk of inaccurate affected-version information has been studied for years.
Nguyen and Massacci’s The (Un)Reliability of NVD Vulnerable Versions Data examined NVD vulnerable-version data using Google Chrome vulnerabilities. The study showed that vulnerable-version records in NVD can contain errors and that those errors can affect downstream vulnerability analysis.
At USENIX Security 2019, Dong et al. studied inconsistencies among structured NVD data, CVE descriptions, and external vulnerability reports in Towards the Detection of Inconsistencies in Public Security Vulnerability Reports. Their system, VIEM, aimed to automatically identify mismatches in vulnerable software names and affected-version information across public security reports.
The message from these studies is straightforward:
Affected-version information in vulnerability databases is important, but it does not always perfectly reflect the actual patch and code state.
This does not make NVD unimportant. It means the opposite: because NVD is widely used, inaccuracies in affected-version metadata can propagate widely through vulnerability management workflows.
4. Why identifying affected versions is hard
At first glance, the problem looks simple.
Versions before the patch are vulnerable.
Versions after the patch are safe.
Real vulnerability histories are not that clean.
4.1 Security patches do not always delete vulnerable code
The easiest patch to understand looks like this:
- dangerous_call(input);
+ safe_call(input);
In this case, deleted lines may provide a useful clue about the vulnerable code.
But many security patches do not remove the dangerous operation. Instead, they add validation before it.
+ if (len > max_len)
+ return ERROR;
memcpy(buf, input, len);
The memcpy call remains. What changed is the control and data flow before it.
This makes the assumption “deleted patch lines equal vulnerable code” too narrow. A vulnerability may not be a single line; it may be a flow in which untrusted input reaches a dangerous operation without proper validation.
4.2 Branches and backports make version comparison fragile
Vulnerabilities are often patched upstream first. But real deployments may use an LTS branch, vendor fork, distribution package, or appliance firmware.
The questions that matter are not limited to the version number.
Does the upstream fixed commit exist?
Was that commit included in the release branch we use?
Was a separate backport applied?
Was the fix included in the actual distributed package?
A higher version number is not always safe, and a lower version number is not always vulnerable.
4.3 Code changes make simple matching unreliable
Software continues to evolve. Function names change, code moves across files, variables are renamed, and logic is refactored.
Code may look different as text while preserving the same vulnerable behavior. Conversely, code may look similar even though the vulnerable condition has been removed.
Affected-version identification is therefore not just string search. It requires looking at patch meaning, code history, release structure, and sometimes ecosystem-specific version semantics.
5. How research has tried to mitigate the problem
Research on affected-version identification approaches the same core question from different angles:
Which versions are actually affected by this CVE?
No single approach solves the entire problem. Each line of work tries to compensate for weaknesses in version-only metadata in a different way.
5.1 SZZ-style approaches: when was the vulnerability introduced?
SZZ was originally used to identify bug-introducing commits from bug-fixing commits. In vulnerability analysis, the idea can be adapted to ask: when did this vulnerability first enter the codebase?
A representative work is ICSE 2022’s V-SZZ: Automatic Identification of Version Ranges Affected by CVE Vulnerabilities. V-SZZ traces vulnerability-related lines from a vulnerability-fixing commit to identify a vulnerability-inducing commit, then uses that information to refine the affected version range.
This is meaningful because it does not simply accept the version range in NVD. It tries to recover the start of the vulnerable period from code history.
However, SZZ-style methods also have limits. They are harder to apply when the patch is add-only, when the vulnerable logic is not represented by a small set of deleted lines, or when refactoring significantly changes the code. These methods are useful for asking “when did the vulnerability enter?”, but they cannot cover every patch structure and code evolution pattern.
5.2 Origin tracking: where did the vulnerability start?
USENIX Security 2021’s V0Finder: Discovering the Correct Origin of Publicly Reported Software Vulnerabilities introduced the concept of “Vulnerability Zero.” The question is: in which software and version did the vulnerability first originate?
V0Finder analyzes code reuse relationships to track how a vulnerability propagated across software projects. This is directly related to the CPE problem because vulnerabilities do not stay inside one project.
upstream library
-> fork
-> downstream project
-> vendor SDK
-> product firmware
Product names and versions alone may miss this propagation path. V0Finder looks at the origin and reuse relationships from a code perspective.
Still, identifying the origin is not the same as deciding whether every downstream release branch is affected. Even if we know where a vulnerability began, we still need to check whether each downstream version actually contains the vulnerable code or the patch.
5.3 Directly identifying affected and unaffected versions
Another line of research asks a more direct question:
Is this specific version affected or unaffected?
ASE 2022’s Precise (Un)Affected Version Analysis for Web Vulnerabilities, also known as AFV, uses patch commits and vulnerability fingerprints to determine whether a target version is affected or unaffected. The important point is not only finding more affected versions. Correctly identifying unaffected versions is also necessary to reduce unnecessary response work.
In the Java ecosystem, VERJava: Vulnerable Version Identification for Java OSS with a Two-Stage Analysis combines function-level and patch-level analysis to identify vulnerable versions of Java open-source software. This reflects a broader trend: affected-version analysis often needs to be adapted to language and ecosystem characteristics.
ASE 2024’s VISION: Identifying Affected Library Versions for Open Source Software Vulnerabilities moves toward identifying affected library versions directly. It uses clues from vulnerability descriptions and patch information to decide which library versions still contain vulnerable logic.
A similar direction appears in C/C++ vulnerability analysis. For example, Clovery: Identifying Affected Versions in C/C++ Public Security Vulnerability Reports uses patch and repository information together, tracking function lineage and code-level semantic relationships to decide whether specific versions are affected. The broader point is not the individual tool, but the direction: instead of relying only on a version string, these studies try to check whether vulnerable behavior remains in the actual code.
The common theme is clear:
Do not rely only on the affected range written in a public report. Use patch and code evidence to reassess version-level impact.
This direction is still difficult. The patch commit may be hard to identify. Vulnerability descriptions may be incomplete. Analysis differs by language and ecosystem. Function renaming and refactoring can break simple matching. Add-only patches may fix a vulnerability without deleting the dangerous operation. Ultimately, affected-version identification is less about asking “is there a patch?” and more about understanding what vulnerable behavior the patch prevents.
5.4 Where the field stands now
Several tools and research lines now exist, but the conclusion should not be “automation has solved the NVD affected-range problem.” Recent work suggests the opposite: this remains a difficult problem.
ASE 2025’s Vulnerability-Affected Versions Identification: How Far Are We? surveys the current state of vulnerability-affected versions identification. It categorizes tools into tracing-based and matching-based approaches and evaluates multiple tools on a C/C++ vulnerability benchmark. Tracing-based methods infer affected versions by tracing vulnerability-inducing commits, as in SZZ-style approaches. Matching-based methods search for vulnerable logic or patch patterns across versions.
The fact that many approaches exist does not mean the problem is easy. Real-world vulnerability histories still involve add-only patches, cross-file changes, multi-branch development, limited semantic reasoning, and rigid matching logic. These factors continue to make automation fragile.
The research trend is therefore not just toward faster matching. It is toward understanding patch meaning, tracking how vulnerable logic evolves over version history, accounting for branches and backports, and respecting ecosystem-specific version semantics.
In one sentence:
NVD affected-version information is useful as a starting point, but important decisions should be supported by patch and code-history evidence.
6. How should practitioners use this information?
There is no need to abandon NVD CPE. In large-scale vulnerability response, it is necessary.
But its role should be understood correctly.
NVD CPE = fast candidate discovery
Patch, branch, vendor advisory, and actual code = decision support
Affected ranges alone are not enough in situations such as:
- You use an LTS branch.
- You use a vendor fork or distribution package.
- Your package version differs from the upstream version name.
- A backported patch has been applied.
- OSS is bundled inside firmware or an appliance.
- The CVE description and vendor advisory disagree.
- A version is marked as fixed, but it is unclear whether the actual patch commit is included.
In these cases, it is worth asking:
- What CPE match criteria does NVD use for this affected range?
- Does the vendor advisory describe the same range?
- Does the release marked as fixed actually include the patch commit?
- Did that patch reach the release branch or package we use?
- If a backport exists, should patch presence matter more than the version range?
- Does the code included in our product differ from the upstream release?
It is also useful to consult ecosystem-aware vulnerability databases such as OSV. OSV represents vulnerabilities using package ecosystems, introduced/fixed events, and commit or version ranges. The OpenSSF OSV Schema describes it as a standard interchange format for open-source package vulnerabilities. OSV itself describes the schema as a human- and machine-readable format designed to map vulnerabilities precisely to open-source package versions or commit hashes. See OSV.dev.
Good vulnerability response rarely depends on one source alone.
NVD CPE
+ vendor advisory
+ OSV / ecosystem advisory
+ patch commit
+ release branch
+ actual deployed artifact
These sources should be used together.
7. Conclusion: software is too complex for NVD alone
NVD’s CPE-based affected-version information is highly useful for vulnerability management. It helps organizations quickly identify candidate risks at scale.
But it does not always match the actual code state. The affected range may be too broad or too narrow. Backports and release branches may be missed. Product names and package names may not align. A version marked as fixed may still lack the patch in the environment where it is deployed.
Research has emerged to reduce this gap. V-SZZ tries to recover when a vulnerability entered the code history. V0Finder studies the origin and propagation of vulnerabilities. AFV, VERJava, VISION, and Clovery use patch and code evidence to determine whether specific versions or libraries are actually affected. Recent work such as Vulnerability-Affected Versions Identification: How Far Are We? organizes these tools into tracing-based and matching-based families and shows why affected-version identification remains difficult due to patch structure, branches, code evolution, and limited semantic reasoning.
The core point is simple:
Do not stop at NVD.
Use NVD as the starting point.
Then verify important decisions with patch and code evidence.
When deciding whether a version is safe, the question should be:
Is this conclusion based only on a version range, or does it reflect the actual patch and code state?
That difference can reduce unnecessary patching work and, more importantly, reduce the chance that a vulnerable version remains in production because the metadata said it was safe.