An SBOM is only the beginning: does a vulnerable library mean a vulnerable product?
From a scanner match to a defensible firmware assessment: SBOM, vulnerability conditions, evidence, CycloneDX VEX, and changes that invalidate earlier conclusions.

A scanner reports a vulnerable library in two firmware variants. Both contain the same parser version, so their findings look identical. One accepts data over a network interface. The other disables compressed input. The team would like to close the second finding as irrelevant, but currently has only a setting in a configuration file.
That is insufficient for a defensible conclusion. The flag may not describe the released image, another library may retain the code, or a service tool may expose the function. Conversely, a component name alone does not establish that every product containing that library is vulnerable in the same way.
We will work from software inventory to an assessment of a particular release and a VEX statement. The example uses a fictional example-parser version 2.3.0 and the invented vulnerability DEMO-PARSER-001. This is not a CVE identifier, a real vulnerability report, or an audit of a BBest24 product. Evidence packages are assumptions in a teaching scenario. Local checks validate the decision logic and sample documents, not a physical device.
Three artifacts answer different questions
An SBOM, or software bill of materials, describes software composition within a stated scope. A vulnerability advisory describes a component defect and known conditions for triggering it. VEX, vulnerability exploitability exchange, communicates an assessment connecting that vulnerability to a particular product or configuration. All three are useful; none replaces the others.
The CycloneDX model supports components, dependencies, and metadata. A list does not become complete merely because it is valid JSON. Package manager output may omit a manually copied library, the base operating system, vendor code, or another image running on the device. Scope and completeness must be explicit properties of the result.
CycloneDX VEX addresses the significance of a vulnerability in the context of component use. It is not a remediation mechanism and does not change firmware. A status should communicate what is known, which release it concerns, and the justification. Otherwise, it can become a permanent filter hiding a finding that becomes relevant again after a product change.
This separation also improves customer discussions. “Do you use parser 2.3.0?” concerns composition. “Can this defect affect my release?” requires analysis. Having an SBOM does not answer the second question. Saying “not affected” without identifying the release prevents the recipient from matching the assessment to their installation.
One library, two images
The fictional vulnerability affects compressed input handling and can cause denial of service. We do not need a concrete triggering payload for this example. The required conditions are vulnerable code and a path that delivers suitable input to it. Both firmware variants retain other functions from the parser library.
Scroll horizontally if needed.
| Release | Assumed product function | Initial observation |
|---|---|---|
| 1.0-network | Data import through a network interface | Compression support is enabled |
| 1.0-local | Operation with a simple local format | Configuration declares compression disabled |
For 1.0-network, assume a subsequent evidence package, E-NET: analysis identifies a call from network input to the vulnerable function, and a controlled test confirms the defect conditions in that image. Within the example, this supports exploitable. We do not need evidence of an actual attack to treat a confirmed exploitation path as a problem requiring action.
For 1.0-local, initially there is no linker map or inspection of the released artifact. The status remains in_triage. Only the hypothetical E-LOCAL package, connecting build configuration to the image and confirming that the vulnerable code is absent, supports not_affected with the justification code_not_present. The parser component remains in the SBOM because its other code is present.
This distinction matters. A scanner finding is not false merely because the product excludes one vulnerable function. The library match may be correct; product analysis establishes the limited impact. false_positive means something different from a justified lack of impact in a particular configuration. Keeping these reasons separate makes later reassessment much easier.
Release identity is part of the evidence
“Version 1.0” can cover several build variants, processor architectures, and vendor images. An assessment needs a more exact identity: product, variant, release identifier, and a connection to the artifact. In a real process, an image cryptographic hash and build identifier are useful. A version shown in a settings screen may not distinguish two different files.
The sample documents use the distinct identifiers 1.0-network and 1.0-local. They do not include an invented hash pretending to identify actual firmware. The local files explicitly state that they are training material and that E-NET and E-LOCAL are scenario assumptions.
Image boundaries must also be defined. Does the assessment cover the main firmware, bootloader, separate communications processor, and recovery partition? Does the module download additional code after initial startup? Does an update replace only the application while retaining the base system? If these answers are unknown, absence from one file does not support a conclusion about the complete product.
Retain the identity of the vulnerability information as well. An advisory may expand to include another function or different input conditions. An assessment based on its previous description may require review even if firmware has not changed by a single byte. Evidence therefore relates both to the product release and to the state of knowledge about the defect.
Two scanners may be comparing different things
Before assessing impact, establish whether the component match itself is correct. The same short name can identify different packages. One component may appear under both its supplier name and its upstream project name. Package identity, version, provenance, and patch information help determine whether an advisory actually concerns the assessed code. Similar text alone is a poor basis for matching.
Tool disagreements can reflect differences in scope. One scanner reads source dependency files; another examines the finished image. The former may list a library used only by a build tool, while the latter detects a statically linked copy in the application. Comparing their alarm counts does not resolve this difference. First establish which artifacts and dependency classes each tool covers.
Both images in our scenario really contain parser 2.3.0, so impact analysis is appropriate. If the finding instead concerned another project with a similar name, the issue would be identification, not an unreachable vulnerable function. That is a different reason and should leave a different assessment record. Precise records support later corrections without suppressing valid findings along with invalid matches.
Keep the original scanner report, its database version, and the matching rule. When a database update produces a new alarm for an unchanged image, the team can determine whether new vulnerability knowledge appeared, the affected version range changed, or component identification improved. Each may require a different response. A change in warning count alone does not establish that a product became more or less secure.
A decision tree needs an unknown branch
For the two unpatched images, first ask whether their identity and composition are known. Next, determine whether vulnerable code was actually excluded. If it is present, or absence has not been established, examine the input path and conditions needed to trigger the defect.
The diagram intentionally does not cover every possible VEX justification. An assessment can also establish unreachable code or effective protection, but that needs a separate argument covering the product’s operating conditions. Failing to observe a call during one test is weaker than establishing that no path exists within the assessed scope, including error and service states.
Failure to reproduce the defect is similarly inconclusive. A test may use the wrong configuration, another architecture, incomplete input, or omit the service path. A negative test result can be valuable evidence, but should not automatically change the status to not_affected.
An unresolved state needs an owner and a next action. “Waiting for the supplier” should identify the missing information: image composition, affected version range, patch confirmation, or triggering conditions. Otherwise, a status can remain unchanged for months while nobody remembers what would complete the investigation. An explicit unknown is useful only when it leads to work that can resolve it.
How strong is evidence of absent code?
A compile flag describes intent. A build record shows whether the flag was used for the assessed release. A linker map and artifact inspection help establish what entered the image. The team must also check whether the same function was copied or supplied through another component. No single file replaces this chain of evidence.
E-LOCAL assumes such a coherent package. Configuration disables compression, the build record identifies 1.0-local, and image analysis confirms exclusion of the vulnerable implementation. This is stronger than searching source files for a function name. Optimization, static linking, and symbol renaming mean that the presence or absence of a string in a binary is not a universal test.
Evidence should be understandable to a second person. It needs tools and versions, the artifact examined, conditions, results, and limitations. “Checked, looks fine” cannot support repetition after a compiler change. A screenshot of an empty symbol list does not explain whether it covered the complete image or whether optimization incorporated the function into other code.
For a closed supplier component, independent analysis may not be possible. A supplier statement tied to an exact release, together with its technical basis, may be necessary. Preserve that evidence’s provenance and limitations in the assessment. A general commercial assurance should not become a technical confirmation that a vulnerability is absent.
The format’s vocabulary matters
The examples use CycloneDX 1.7. Its JSON schema defines states including in_triage, exploitable, not_affected, resolved, and false_positive. The justification code_not_present concerns vulnerable code that has been removed or eliminated. Names cannot be copied arbitrarily from another format.
OpenVEX 0.2.0 uses states including under_investigation, affected, fixed, and not_affected, with its own justification names such as vulnerable_code_not_present. Similar intent does not imply identical fields or exact equivalence in every case. Conversion must preserve scope, time, and justification rather than merely replacing a string.
The relevant part of the local variant’s CycloneDX assessment is:
{
"id": "DEMO-PARSER-001",
"analysis": {
"state": "not_affected",
"justification": "code_not_present",
"detail": "Fictional E-LOCAL: vulnerable code absent in 1.0-local."
},
"affects": [
{"ref": "urn:example:firmware:1.0-local"}
]
}
This is a fragment, not a complete standalone document. A full document also needs metadata, identifiers, the parser dependency, and an assessment timestamp. During preparation, the complete example documents were checked locally against the 1.7 schema and for reference consistency.
Format validation does not establish that a justification is true. It can detect an invalid state or field, but cannot prove that E-LOCAL actually excludes vulnerable code. A separate test deliberately inserts under_investigation into a CycloneDX document and expects rejection. This checks document interoperability, not device security. A valid document can still contain an unsupported technical conclusion.
A status must lead to action
For 1.0-network, exploitable starts work on a fix or another documented risk reduction. The team needs an owner, priority, a way to identify affected devices, and a verification plan. Distinguish the final solution from temporary measures: disabling an interface while preparing an update is different from removing the defect from code.
Compensating controls have conditions. A network rule may restrict an input in one installation and be absent in another. A function requiring authentication can still accept data from a user whose permissions should not allow them to stop the entire service. Authentication alone does not establish that vulnerability conditions are impossible.
If the justification depends on configuration or protection, identify who maintains that condition and what happens when it changes. A VEX recipient must be able to determine whether their installation falls within scope. One status for an entire product family can be too broad when the result depends on a local firewall rule or an active service mode.
After a fix, the new release needs regression testing and a new assessment. Existing devices do not become repaired when an update file is published. A fleet may simultaneously contain vulnerable, fixed, and unresolved releases. Reporting should preserve that distinction instead of assigning one product status based on the newest available version.
A variant that invalidates the earlier conclusion
Suppose 1.1-local introduces compressed file import through a service tool. Previously excluded code returns to the image. E-LOCAL for 1.0-local is no longer evidence of absent code in the new release, even if the package manager still reports parser 2.3.0.
The new release returns to in_triage while the changed path is investigated. If file input reaches the vulnerable function and meets the defect conditions, there is a basis for exploitable. The input vector may differ from the network path, which affects risk analysis, but does not automatically preserve the earlier not_affected conclusion.
Retain the assessment history for 1.0-local. Do not overwrite it as though the original decision had always been wrong. It may have been correct for the older image. New information identifies the boundary of its applicability. This lets recipients distinguish a product change from correction of an error in the previous analysis.
The same mechanism applies to new supplier information. If the defect also affects another parser mode, excluding one function may no longer be sufficient. Reopen the assessment even for an unchanged image. Here, the trigger is a change in knowledge about the vulnerability, not a change in the component list.
A fix without a library version change
A supplier may backport a fix while retaining the main version number. A name-and-version scanner will continue to report a match. Neither automatic dismissal nor blind reliance on the version number is appropriate. Connect the code change to the particular artifact and verify that it addresses the assessed defect conditions.
Keep the patch identifier, provenance, code difference, and regression result. resolved describes removal of the issue within the assessed scope. CycloneDX also supports resolved_with_pedigree when the corresponding component change history accompanies the assessment. Do not select the more specific state without the required material merely because it sounds stronger.
Functional compatibility is a separate concern. A repaired parser may change malformed-file handling or reject previously tolerated input. Security testing should accompany tests of valid inputs and recovery after errors. Closing one finding does not justify an unnoticed regression in the product’s primary function.
Preventing VEX from becoming a suppression file
The first warning sign is an imprecise scope. The second is a justification based only on memory: “we do not use that function.” The third is no event that triggers reassessment. If release review never asks about configuration changes, a status can outlive the condition supporting it.
Connect assessments to dependency, build-flag, input-interface, and service-function changes. Not every modification invalidates every analysis. However, the team must be able to find evidence that depends on the changed element. This is the same traceability principle that supports functional verification.
A document needs an author or responsible team, date, version, and update channel. A signature or trusted distribution channel helps establish provenance, but does not replace technical analysis. Recipients should also check whether the statement covers their release and whether a newer assessment supersedes it. An old, correctly signed document can still be outdated.
A decision record for one finding
Start acceptance with the following record. An empty field should produce an explicit unknown and an action, not default closure.
Scroll horizontally if needed.
| Field | Minimum content |
|---|---|
| Subject | Product, variant, release, image scope, artifact identity |
| Match | Component, version, provenance, reason for vulnerability association |
| Defect conditions | Function, input, configuration, required privileges |
| Evidence | Material, method, tool versions, result, limitations |
| Conclusion | State and justification appropriate to the selected format |
| Action | Fix, mitigation, missing investigation, responsible owner |
| Reassessment | Product or information changes that invalidate the argument |
The result of our example is not “the parser is secure.” It is that 1.0-network has a confirmed path to the fictional defect, while 1.0-local can be considered unaffected only after obtaining appropriate evidence of absent code. The next release needs those conditions checked again. Connecting a conclusion to its scope and evidence makes a concrete decision possible and provides a basis for defending it later.
Separate internal evidence from the recipient-facing statement. VEX needs sufficient scope and justification, but does not need build secrets or access credentials. The complete evidence package can remain in a controlled repository with an unambiguous reference. This separation preserves reviewability without turning a distributed assessment into a container for unrelated sensitive material.
Sources
- CycloneDX: Specification Overview, component and dependency model; examples pin version 1.7.
- CycloneDX: Vulnerability Exploitability eXchange, vulnerability significance in product context.
- CycloneDX 1.7 JSON schema, assessment states, justifications, and references; retained with dependencies for offline validation.
- OpenVEX Specification 0.2.0, separate state and justification vocabulary and statement timing.
Sources checked on 21 October 2026. The sample CycloneDX documents passed schema and reference validation; this does not establish the security of a real product. All components, releases, the vulnerability, and evidence packages in the scenario are fictional.