Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Discussion options

NB: this is an extension of #540 and #677

Given the testdata in #677, set up a test for #540 but with clashing bom_refs and unique names instead:

>>> from json import load
>>> from cyclonedx.model.bom import Bom
>>> from cyclonedx.model.component import Component, ComponentType
>>> root_component = Component(
...     type=ComponentType.FRAMEWORK,
...     name="root 42",
...     version="42",
...     bom_ref="root",
... )
>>>
>>> bom = Bom()
>>> bom.metadata.component = root_component
>>> bom.validate()
True
>>> len(bom.components)
0
>>> len(bom.dependencies)
1
>>>
>>> with open('out.json', mode="r") as testfile:
...  json_bom = load(testfile)
...
>>> len(json_bom['components'])
4
>>> len(json_bom['dependencies'])
3
>>>
>>> loaded_bom = Bom.from_json(json_bom)
>>> loaded_bom.validate()
True
>>> len(loaded_bom.components)
4
>>> len(loaded_bom.dependencies)
5
>>>
>>> loaded_bom.metadata.component
<Component bom-ref=<BomRef 'root' id=139844009673936>, group=None, name=test, version=None, type=ComponentType.APPLICATION>
>>> bom.metadata.component
<Component bom-ref=<BomRef 'root' id=139844025859088>, group=None, name=root 42, version=42, type=ComponentType.FRAMEWORK>
>>>
>>> loaded_bom.metadata.component.bom_ref
<BomRef 'root' id=139844009673936>
>>> bom.metadata.component.bom_ref
<BomRef 'root' id=139844025859088>
>>>
>>> bom.metadata.component.bom_ref == loaded_bom.metadata.component.bom_ref
True
>>> loaded_bom.metadata.component == bom.metadata.component
False
>>>
>>> bom.components.add(loaded_bom.metadata.component)
>>> bom.register_dependency(root_component, [loaded_bom.metadata.component])
>>> bom.components |= loaded_bom.components
>>> bom.dependencies |= loaded_bom.dependencies
>>>
>>> bom.validate()
True
>>>
>>> len(bom.components)
5
>>> len(bom.dependencies)
6

Note that the Bom says it is valid, while it is actually not - although this is not apparent at a glance.
When outputting to json (e.g. with the reported JsonV1Dot5), dependencies is shown to be as follows:

        [
            {"ref": "root", "dependsOn": ["BomRef.23942051401152453.4412489734431171"]},
            {"ref": "root", "dependsOn": ["test11", "test21"]},
            {"ref": "test11", "dependsOn": ["test12"]},
            {"ref": "test12"},
            {"ref": "test21", "dependsOn": ["test22"]},
            {"ref": "test22"}
        ]

This is actually invalid and would not validate when dumped to file and re-read by the library

You must be logged in to vote

Replies: 3 comments

Comment options

The problem is a lot more clear when merging multiple files, as it seems to compound in some manner.
A second testfile with the same structure, but modified (non-clashing) bom-ref components:

>>> with open('out2.json', mode="r") as testfile:
...  json_bom = load(testfile)
...
>>> loaded_bom2 = Bom.from_json(json_bom)
>>>
>>> loaded_bom.metadata.component
<Component bom-ref=<BomRef 'root' id=140246182363600>, group=None, name=test, version=None, type=ComponentType.APPLICATION>
>>> loaded_bom2.metadata.component
<Component bom-ref=<BomRef 'root' id=140246180662288>, group=None, name=test2, version=None, type=ComponentType.APPLICATION>
>>>
>>> loaded_bom.metadata.component == loaded_bom2.metadata.component
False
>>> loaded_bom.metadata.component.bom_ref == loaded_bom2.metadata.component.bom_ref
True
>>>
>>> bom.components.add(loaded_bom2.metadata.component)
>>> bom.register_dependency(root_component, [loaded_bom2.metadata.component])
>>> bom.components |= loaded_bom2.components
>>> bom.dependencies |= loaded_bom2.dependencies
>>> bom.validate()
True
>>>
>>> len(bom.components)
10
>>> len(bom.dependencies)
12

This provides the following dependencies json output:

        [
            {"ref": "root", "dependsOn": ["BomRef.19783006151644178.9868708899859131"]},
            {"ref": "root", "dependsOn": ["test11", "test21"]},
            {"ref": "root", "dependsOn": ["test411", "test421"]},
            {"ref": "test11", "dependsOn": ["test12"]},
            {"ref": "test12"},
            {"ref": "test21", "dependsOn": ["test22"]},
            {"ref": "test22"},
            {"ref": "test411", "dependsOn": ["test412"]},
            {"ref": "test412"},
            {"ref": "test421", "dependsOn": ["test422"]},
            {"ref": "test422"}
        ]

Because the first dependency object is the root_component, the entire dependency tree is actually disconnected from root.
It seems to happen when register_dependency is called on the root object, which isn't a member of the bom components.

Note that the root BomRef string seems to be regenerated each time the json output is generated, this DependsOn is not stable.
Inspecting the data object shows that dependencies were not correctly linked (see targets), although the bom_ref is correctly stored:

>>> for dep in bom.dependencies:
...     print(dep)
...
<Dependency ref=<BomRef 'root' id=140246198533136>, targets=1>
<Dependency ref=<BomRef 'root' id=140246182437328>, targets=2>
<Dependency ref=<BomRef 'root' id=140246180673744>, targets=2>
<Dependency ref=<BomRef 'test11' id=140246182406736>, targets=1>
<Dependency ref=<BomRef 'test12' id=140246182363664>, targets=0>
<Dependency ref=<BomRef 'test21' id=140246182440080>, targets=1>
<Dependency ref=<BomRef 'test22' id=140246182376016>, targets=0>
<Dependency ref=<BomRef 'test411' id=140246180704336>, targets=1>
<Dependency ref=<BomRef 'test412' id=140246180662352>, targets=0>
<Dependency ref=<BomRef 'test421' id=140246180721232>, targets=1>
<Dependency ref=<BomRef 'test422' id=140246180673616>, targets=0>
>>> bom.dependencies[0]._dependencies[0]._ref._value
'root'
You must be logged in to vote
0 replies
Comment options

Are you aware, that per CycloneDX specification, Bom-refs must be unique per document?
This library IS aware of it, and it is actually enforced by assigning unique values when serializing Json/xml.

Furthermore, since you are struggling with merging again, you maybe want to try out https://github.com/CycloneDX/cyclonedx-cli and read CycloneDX/specification#320

You must be logged in to vote
0 replies
Comment options

Yes, I agree. The problem arises with merging of multiple documents, which are all valid on their own, but may contain duplicates.
I will have a look again at the cyclonedx-cli implementation, but CycloneDX/cyclonedx-cli#399 is still unaddressed.

The examples above show that this library is not correctly assigning unique values, and doesn't throw warning/exception.
For instance, the dependency "root" is mentioned three times in the json output and the backrefs are not remembered.
The metadata component ref is probably the only value that does not need to be altered for the structure to remain valid.

However, every call to .validate() is stating that the produced Bom is valid - while the library recognises that the data is not unique.
Producing json output also runs an extra validation, which does not recognise that the produced output is wrong.

You must be logged in to vote
0 replies
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
2 participants
Converted from issue

This discussion was converted from issue #807 on April 14, 2025 13:47.

Morty Proxy This is a proxified and sanitized view of the page, visit original site.