Skip to content

feat: add BomRefs for known models according to CycloneDX 1.5 #859

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 23, 2025
Merged
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
54 changes: 47 additions & 7 deletions cyclonedx/model/contact.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

from .._internal.bom_ref import bom_ref_from_str as _bom_ref_from_str
from .._internal.compare import ComparableTuple as _ComparableTuple
from ..schema.schema import SchemaVersion1Dot6
from ..schema.schema import SchemaVersion1Dot5, SchemaVersion1Dot6
from . import XsUri
from .bom_ref import BomRef

Expand All @@ -49,7 +49,7 @@ def __init__(
postal_code: Optional[str] = None,
street_address: Optional[str] = None,
) -> None:
self._bom_ref = _bom_ref_from_str(bom_ref, optional=True)
self._bom_ref = _bom_ref_from_str(bom_ref)
self.country = country
self.region = region
self.locality = locality
Expand All @@ -58,11 +58,11 @@ def __init__(
self.street_address = street_address

@property
@serializable.json_name('bom-ref')
@serializable.type_mapping(BomRef)
@serializable.xml_attribute()
@serializable.xml_name('bom-ref')
def bom_ref(self) -> Optional[BomRef]:
@serializable.json_name('bom-ref')
def bom_ref(self) -> BomRef:
"""
An optional identifier which can be used to reference the component elsewhere in the BOM. Every bom-ref MUST be
unique within the BOM.
Expand Down Expand Up @@ -167,7 +167,7 @@ def __comparable_tuple(self) -> _ComparableTuple:
self.country, self.region, self.locality, self.postal_code,
self.post_office_box_number,
self.street_address,
None if self.bom_ref is None else self.bom_ref.value,
self._bom_ref.value,
))

def __eq__(self, other: object) -> bool:
Expand Down Expand Up @@ -199,14 +199,33 @@ class OrganizationalContact:

def __init__(
self, *,
bom_ref: Optional[Union[str, BomRef]] = None,
name: Optional[str] = None,
phone: Optional[str] = None,
email: Optional[str] = None,
) -> None:
self._bom_ref = _bom_ref_from_str(bom_ref)
self.name = name
self.email = email
self.phone = phone

@property
@serializable.view(SchemaVersion1Dot5)
@serializable.view(SchemaVersion1Dot6)
@serializable.type_mapping(BomRef)
@serializable.xml_attribute()
@serializable.xml_name('bom-ref')
@serializable.json_name('bom-ref')
def bom_ref(self) -> BomRef:
"""
An optional identifier which can be used to reference the component elsewhere in the BOM. Every bom-ref MUST be
unique within the BOM.

Returns:
`BomRef`
"""
return self._bom_ref

@property
@serializable.xml_sequence(1)
@serializable.xml_string(serializable.XmlStringSerializationType.NORMALIZED_STRING)
Expand Down Expand Up @@ -257,7 +276,8 @@ def phone(self, phone: Optional[str]) -> None:

def __comparable_tuple(self) -> _ComparableTuple:
return _ComparableTuple((
self.name, self.email, self.phone
self.name, self.email, self.phone,
self._bom_ref.value,
))

def __eq__(self, other: object) -> bool:
Expand Down Expand Up @@ -289,16 +309,35 @@ class OrganizationalEntity:

def __init__(
self, *,
bom_ref: Optional[Union[str, BomRef]] = None,
name: Optional[str] = None,
urls: Optional[Iterable[XsUri]] = None,
contacts: Optional[Iterable[OrganizationalContact]] = None,
address: Optional[PostalAddress] = None,
) -> None:
self._bom_ref = _bom_ref_from_str(bom_ref)
self.name = name
self.address = address
self.urls = urls or []
self.contacts = contacts or []

@property
@serializable.view(SchemaVersion1Dot5)
@serializable.view(SchemaVersion1Dot6)
@serializable.type_mapping(BomRef)
@serializable.xml_attribute()
@serializable.xml_name('bom-ref')
@serializable.json_name('bom-ref')
def bom_ref(self) -> BomRef:
"""
An optional identifier which can be used to reference the component elsewhere in the BOM. Every bom-ref MUST be
unique within the BOM.

Returns:
`BomRef`
"""
return self._bom_ref

@property
@serializable.xml_sequence(10)
@serializable.xml_string(serializable.XmlStringSerializationType.NORMALIZED_STRING)
Expand Down Expand Up @@ -367,7 +406,8 @@ def contacts(self, contacts: Iterable[OrganizationalContact]) -> None:

def __comparable_tuple(self) -> _ComparableTuple:
return _ComparableTuple((
self.name, _ComparableTuple(self.urls), _ComparableTuple(self.contacts)
self.name, _ComparableTuple(self.urls), _ComparableTuple(self.contacts),
self._bom_ref.value,
))

def __eq__(self, other: object) -> bool:
Expand Down
64 changes: 43 additions & 21 deletions cyclonedx/model/license.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,13 @@
import py_serializable as serializable
from sortedcontainers import SortedSet

from .._internal.bom_ref import bom_ref_from_str as _bom_ref_from_str
from .._internal.compare import ComparableTuple as _ComparableTuple
from ..exception.model import MutuallyExclusivePropertiesException
from ..exception.serialization import CycloneDxDeserializationException
from ..schema.schema import SchemaVersion1Dot6
from ..schema.schema import SchemaVersion1Dot5, SchemaVersion1Dot6
from . import AttachedText, XsUri
from .bom_ref import BomRef


@serializable.serializable_enum
Expand Down Expand Up @@ -75,6 +77,7 @@ class DisjunctiveLicense:

def __init__(
self, *,
bom_ref: Optional[Union[str, BomRef]] = None,
id: Optional[str] = None, name: Optional[str] = None,
text: Optional[AttachedText] = None, url: Optional[XsUri] = None,
acknowledgement: Optional[LicenseAcknowledgement] = None,
Expand All @@ -86,12 +89,30 @@ def __init__(
'Both `id` and `name` have been supplied - `name` will be ignored!',
category=RuntimeWarning, stacklevel=1
)
self._bom_ref = _bom_ref_from_str(bom_ref)
self._id = id
self._name = name if not id else None
self._text = text
self._url = url
self._acknowledgement = acknowledgement

@property
@serializable.view(SchemaVersion1Dot5)
@serializable.view(SchemaVersion1Dot6)
@serializable.type_mapping(BomRef)
@serializable.xml_attribute()
@serializable.xml_name('bom-ref')
@serializable.json_name('bom-ref')
def bom_ref(self) -> BomRef:
"""
An optional identifier which can be used to reference the component elsewhere in the BOM. Every bom-ref MUST be
unique within the BOM.

Returns:
`BomRef`
"""
return self._bom_ref

@property
@serializable.xml_sequence(1)
def id(self) -> Optional[str]:
Expand Down Expand Up @@ -186,16 +207,6 @@ def url(self, url: Optional[XsUri]) -> None:
# def properties(self, ...) -> None:
# ... # TODO since CDX1.5

# @property
# @serializable.json_name('bom-ref')
# @serializable.type_mapping(BomRefHelper)
# @serializable.view(SchemaVersion1Dot5)
# @serializable.view(SchemaVersion1Dot6)
# @serializable.xml_attribute()
# @serializable.xml_name('bom-ref')
# def bom_ref(self) -> BomRef:
# ... # TODO since CDX1.5

@property
@serializable.view(SchemaVersion1Dot6)
@serializable.xml_attribute()
Expand Down Expand Up @@ -227,6 +238,7 @@ def __comparable_tuple(self) -> _ComparableTuple:
self._id, self._name,
self._url,
self._text,
self._bom_ref.value,
))

def __eq__(self, other: object) -> bool:
Expand Down Expand Up @@ -264,11 +276,30 @@ class LicenseExpression:

def __init__(
self, value: str, *,
bom_ref: Optional[Union[str, BomRef]] = None,
acknowledgement: Optional[LicenseAcknowledgement] = None,
) -> None:
self._bom_ref = _bom_ref_from_str(bom_ref)
self._value = value
self._acknowledgement = acknowledgement

@property
@serializable.view(SchemaVersion1Dot5)
@serializable.view(SchemaVersion1Dot6)
@serializable.type_mapping(BomRef)
@serializable.xml_attribute()
@serializable.xml_name('bom-ref')
@serializable.json_name('bom-ref')
def bom_ref(self) -> BomRef:
"""
An optional identifier which can be used to reference the component elsewhere in the BOM. Every bom-ref MUST be
unique within the BOM.

Returns:
`BomRef`
"""
return self._bom_ref

@property
@serializable.xml_name('.')
@serializable.xml_string(serializable.XmlStringSerializationType.NORMALIZED_STRING)
Expand All @@ -286,16 +317,6 @@ def value(self) -> str:
def value(self, value: str) -> None:
self._value = value

# @property
# @serializable.json_name('bom-ref')
# @serializable.type_mapping(BomRefHelper)
# @serializable.view(SchemaVersion1Dot5)
# @serializable.view(SchemaVersion1Dot6)
# @serializable.xml_attribute()
# @serializable.xml_name('bom-ref')
# def bom_ref(self) -> BomRef:
# ... # TODO since CDX1.5

@property
@serializable.view(SchemaVersion1Dot6)
@serializable.xml_attribute()
Expand Down Expand Up @@ -325,6 +346,7 @@ def __comparable_tuple(self) -> _ComparableTuple:
return _ComparableTuple((
self._acknowledgement,
self._value,
self._bom_ref.value,
))

def __hash__(self) -> int:
Expand Down
Loading