Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ from siemens_standard_bom.model import StandardBom, Component, ComponentType
from cyclonedx.model.contact import OrganizationalContact

bom = StandardBom()
bom.component=Component(name='Sample Application', version='1.0.0', type=ComponentType.APPLICATION)
bom.add_author(OrganizationalContact(name='John Doe'))
bom.add_tool(Component(name='Sample Tool', version='1.0.0', type=ComponentType.APPLICATION))
bom.add_component(Component(name='Sample Component', version='1.2.3', type=ComponentType.LIBRARY))
Expand All @@ -91,6 +92,7 @@ from siemens_standard_bom.model import StandardBom, Component, ComponentType, Sb
from cyclonedx.model.contact import OrganizationalContact

bom = StandardBom()
bom.component=SbomComponent(Component(name='Sample Application', version='1.0.0', type=ComponentType.APPLICATION))
bom.add_author(OrganizationalContact(name='John Doe'))
bom.add_tool(SbomComponent(Component(name='Sample Tool', version='1.0.0', type=ComponentType.APPLICATION)))
bom.add_component(SbomComponent(Component(name='Sample Component', version='1.2.3', type=ComponentType.LIBRARY)))
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "siemens-standard-bom"
version = "4.1.0"
version = "4.2.0"
description = "Standard BOM Format Library"
keywords = ["sbom", "software-bill-of-materials", "cyclonedx", "cdx"]
authors = [
Expand Down
14 changes: 14 additions & 0 deletions siemens_standard_bom/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,7 @@ def __init__(self, bom: Optional[Bom] = None) -> None:
self.bom = bom
self._insert_standard_bom_tools_entry_if_missing()
self._insert_standard_bom_definitions_entry_if_missing()
self._insert_standard_bom_metadata_component_entry_if_missing()
self._set_supplier_if_missing()

def _insert_standard_bom_tools_entry_if_missing(self) -> None:
Expand Down Expand Up @@ -604,6 +605,19 @@ def _set_supplier_if_missing(self) -> None:
if not self.bom.metadata.supplier:
self.bom.metadata.supplier = OrganizationalEntity(name='Siemens or its Affiliates')

def _insert_standard_bom_metadata_component_entry_if_missing(self) -> None:
"""
Ensures that self.bom.metadata.component exists.
If it doesn't exist, creates a minimal Component object with required information.
Users should update the name and version with actual project values.
"""
if self.bom.metadata.component is None:
self.bom.metadata.component = Component(
name='Unknown Name',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see the benefit of this, on the contrary as this defaults a certain value set, we'll end up with lots of SBOMs for the same Unknown Name@0.0.0 application.

version='0.0.0',
type=ComponentType.APPLICATION
)

def _set_metadata_property(self, property_name: str, value: Optional[str | None]) -> None:
existing = next(filter(lambda p: p.name == property_name,
self.bom.metadata.properties), None)
Expand Down
66 changes: 66 additions & 0 deletions tests/test_model_standard_bom.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from importlib.metadata import version

from cyclonedx.model import ExternalReference, ExternalReferenceType, XsUri
from cyclonedx.model.bom import Bom
from cyclonedx.model.component import ComponentType, Component
from cyclonedx.model.contact import OrganizationalContact
from sortedcontainers import SortedSet
Expand Down Expand Up @@ -235,3 +236,68 @@ def test_sbom_external_components_is_iterable(self) -> None:
sbom.add_external_component(external)
for comp in sbom.external_components:
self.assertEqual(comp.reference, external)

def test_metadata_component_is_created_when_missing(self) -> None:
bom = Bom()
bom.metadata.component = None

sbom = StandardBom(bom)
sbom._insert_standard_bom_metadata_component_entry_if_missing() # noqa: SLF001
self.assertIsNotNone(sbom.bom.metadata.component)
self.assertEqual('Unknown Name', sbom.bom.metadata.component.name)
self.assertEqual('0.0.0', sbom.bom.metadata.component.version)
self.assertEqual(ComponentType.APPLICATION, sbom.bom.metadata.component.type)

def test_metadata_component_is_not_overwritten_when_exists(self) -> None:
bom = Bom()
existing_component = Component(
name='MyApplication',
version='1.2.3',
type=ComponentType.APPLICATION
)
bom.metadata.component = existing_component

sbom = StandardBom(bom)
sbom._insert_standard_bom_metadata_component_entry_if_missing() # noqa: SLF001
self.assertIsNotNone(sbom.bom.metadata.component)
self.assertEqual('MyApplication', sbom.bom.metadata.component.name)
self.assertEqual('1.2.3', sbom.bom.metadata.component.version)
self.assertEqual(ComponentType.APPLICATION, sbom.bom.metadata.component.type)

def test_metadata_component_property_get_when_exists(self) -> None:
bom = Bom()
test_component = Component(
name='TestApp',
version='2.0.0',
type=ComponentType.LIBRARY
)
bom.metadata.component = test_component

sbom = StandardBom(bom)
self.assertIsNotNone(sbom.component)
self.assertEqual('TestApp', sbom.component.name)
self.assertEqual('2.0.0', sbom.component.version)
self.assertEqual(ComponentType.LIBRARY, sbom.component.type)

def test_metadata_component_property_get_when_missing(self) -> None:
bom = Bom()
bom.metadata.component = None

sbom = StandardBom(bom)
self.assertIsNotNone(sbom.component)
self.assertEqual('Unknown Name', sbom.component.name)
self.assertEqual('0.0.0', sbom.component.version)

def test_metadata_component_property_set(self) -> None:
sbom = StandardBom()
new_component = SbomComponent(Component(
name='SetApp',
version='3.0.0',
type=ComponentType.APPLICATION
))

sbom.component = new_component
self.assertIsNotNone(sbom.component)
self.assertEqual('SetApp', sbom.component.name)
self.assertEqual('3.0.0', sbom.component.version)
self.assertEqual(ComponentType.APPLICATION, sbom.component.type)