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
29 changes: 27 additions & 2 deletions cyclonedx/model/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
SchemaVersion1Dot6,
SchemaVersion1Dot7,
)
from ..serialization import PackageUrl as PackageUrlSH
from ..serialization import PackageUrl as PackageUrlSH, XmlBoolAttribute as _XmlBoolAttributeSH
from . import (
AttachedText,
ExternalReference,
Expand Down Expand Up @@ -993,6 +993,7 @@ def __init__(
version: Optional[str] = None,
description: Optional[str] = None,
scope: Optional[ComponentScope] = None,
is_external: Optional[bool] = None,
hashes: Optional[Iterable[HashType]] = None,
licenses: Optional[Iterable[License]] = None,
copyright: Optional[str] = None,
Expand Down Expand Up @@ -1026,6 +1027,7 @@ def __init__(
self.name = name
self.description = description
self.scope = scope
self.is_external = is_external
self.hashes = hashes or []
self.licenses = licenses or []
self.copyright = copyright
Expand Down Expand Up @@ -1304,6 +1306,29 @@ def scope(self) -> Optional[ComponentScope]:
def scope(self, scope: Optional[ComponentScope]) -> None:
self._scope = scope

@property
@serializable.json_name('isExternal')
@serializable.xml_name('isExternal')
@serializable.xml_attribute()
@serializable.type_mapping(_XmlBoolAttributeSH)
@serializable.view(SchemaVersion1Dot7)
def is_external(self) -> Optional[bool]:
"""
Determine whether this component is external. An external component is one that is not part of an assembly,
but is expected to be provided by the environment, regardless of the component's scope. This setting can be
useful for distinguishing which components are bundled with the product and which can be relied upon to be
present in the deployment environment. This may be set to true for runtime components only. For
metadata.component, it must be set to false.

Returns:
`bool` if set else `None`
"""
return self._is_external

@is_external.setter
def is_external(self, is_external: Optional[bool]) -> None:
self._is_external = is_external

@property
@serializable.type_mapping(_HashTypeRepositorySerializationHelper)
@serializable.xml_sequence(11)
Expand Down Expand Up @@ -1683,7 +1708,7 @@ def __comparable_tuple(self) -> _ComparableTuple:
self.swid, self.cpe, _ComparableTuple(self.swhids),
self.supplier, self.author, self.publisher,
self.description,
self.mime_type, self.scope, _ComparableTuple(self.hashes),
self.mime_type, self.scope, self.is_external, _ComparableTuple(self.hashes),
_ComparableTuple(self.licenses), self.copyright,
self.pedigree,
_ComparableTuple(self.external_references), _ComparableTuple(self.properties),
Expand Down
57 changes: 57 additions & 0 deletions cyclonedx/serialization/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,63 @@ def deserialize(cls, o: Any) -> UUID:
) from err


class XmlBoolAttribute(BaseHelper):
"""Helper for serializing boolean values as XML attribute-compatible 'true'/'false' strings,
while keeping native boolean values for JSON."""

@classmethod
def json_serialize(cls, o: Any) -> Optional[bool]:
if o is None:
return None
if isinstance(o, bool):
return o
raise SerializationOfUnexpectedValueException(
f'Attempt to serialize a non-boolean: {o!r}')

@classmethod
def json_deserialize(cls, o: Any) -> Optional[bool]:
if o is None:
return None
if isinstance(o, bool):
return o
raise CycloneDxDeserializationException(
f'Invalid boolean value: {o!r}'
)

@classmethod
def xml_serialize(cls, o: Any) -> Optional[str]:
if o is None:
return None
if isinstance(o, bool):
return 'true' if o else 'false'
raise SerializationOfUnexpectedValueException(
f'Attempt to serialize a non-boolean: {o!r}')

@classmethod
def xml_deserialize(cls, o: Any) -> Optional[bool]:
if o is None:
return None
if isinstance(o, bool):
return o
if isinstance(o, str):
o_lower = o.lower()
if o_lower in ('1', 'true'):
return True
if o_lower in ('0', 'false'):
return False
raise CycloneDxDeserializationException(
f'Invalid boolean value: {o!r}'
)

@classmethod
def serialize(cls, o: Any) -> Any:
return cls.xml_serialize(o)

@classmethod
def deserialize(cls, o: Any) -> Any:
return cls.xml_deserialize(o)


@deprecated('No public API planned for replacing this,')
class LicenseRepositoryHelper(_LicenseRepositorySerializationHelper):
"""**DEPRECATED**
Expand Down
16 changes: 16 additions & 0 deletions tests/_data/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,11 @@ def get_bom_with_external_references() -> Bom:
return bom


def get_bom_with_external_component_1_7() -> Bom:
bom = _make_bom(components=[get_component_external()])
return bom


def get_bom_with_services_simple() -> Bom:
bom = _make_bom(services=[
Service(name='my-first-service', bom_ref='my-specific-bom-ref-for-my-first-service'),
Expand Down Expand Up @@ -853,6 +858,16 @@ def get_component_setuptools_simple(
)


def get_component_external() -> Component:
return Component(
name='external-lib', version='1.0.0',
type=ComponentType.LIBRARY,
is_external=True,
scope=ComponentScope.REQUIRED,
bom_ref='external-lib-1.0.0',
)


def get_component_setuptools_simple_no_version(bom_ref: Optional[str] = None) -> Component:
return Component(
name='setuptools', bom_ref=bom_ref or 'pkg:pypi/setuptools?extension=tar.gz',
Expand Down Expand Up @@ -1611,6 +1626,7 @@ def get_bom_for_issue540_duplicate_components() -> Bom:
get_bom_with_licenses,
get_bom_with_multiple_licenses,
get_bom_for_issue_497_urls,
get_bom_with_external_component_1_7,
get_bom_for_issue_598_multiple_components_with_purl_qualifiers,
get_bom_with_component_setuptools_with_v16_fields,
get_bom_for_issue_630_empty_property,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" ?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.0" version="1">
<components>
<component type="library">
<name>external-lib</name>
<version>1.0.0</version>
<scope>required</scope>
<modified>false</modified>
</component>
</components>
</bom>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" ?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.1" serialNumber="urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac" version="1">
<components>
<component type="library" bom-ref="external-lib-1.0.0">
<name>external-lib</name>
<version>1.0.0</version>
<scope>required</scope>
</component>
</components>
</bom>
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"components": [
{
"bom-ref": "external-lib-1.0.0",
"name": "external-lib",
"scope": "required",
"type": "library",
"version": "1.0.0"
}
],
"dependencies": [
{
"ref": "external-lib-1.0.0"
}
],
"metadata": {
"timestamp": "2023-01-07T13:44:32.312678+00:00"
},
"serialNumber": "urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac",
"version": 1,
"$schema": "http://cyclonedx.org/schema/bom-1.2b.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.2"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" ?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.2" serialNumber="urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac" version="1">
<metadata>
<timestamp>2023-01-07T13:44:32.312678+00:00</timestamp>
</metadata>
<components>
<component type="library" bom-ref="external-lib-1.0.0">
<name>external-lib</name>
<version>1.0.0</version>
<scope>required</scope>
</component>
</components>
<dependencies>
<dependency ref="external-lib-1.0.0"/>
</dependencies>
</bom>
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"components": [
{
"bom-ref": "external-lib-1.0.0",
"name": "external-lib",
"scope": "required",
"type": "library",
"version": "1.0.0"
}
],
"dependencies": [
{
"ref": "external-lib-1.0.0"
}
],
"metadata": {
"timestamp": "2023-01-07T13:44:32.312678+00:00"
},
"serialNumber": "urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac",
"version": 1,
"$schema": "http://cyclonedx.org/schema/bom-1.3a.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.3"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" ?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.3" serialNumber="urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac" version="1">
<metadata>
<timestamp>2023-01-07T13:44:32.312678+00:00</timestamp>
</metadata>
<components>
<component type="library" bom-ref="external-lib-1.0.0">
<name>external-lib</name>
<version>1.0.0</version>
<scope>required</scope>
</component>
</components>
<dependencies>
<dependency ref="external-lib-1.0.0"/>
</dependencies>
</bom>
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"components": [
{
"bom-ref": "external-lib-1.0.0",
"name": "external-lib",
"scope": "required",
"type": "library",
"version": "1.0.0"
}
],
"dependencies": [
{
"ref": "external-lib-1.0.0"
}
],
"metadata": {
"timestamp": "2023-01-07T13:44:32.312678+00:00"
},
"serialNumber": "urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac",
"version": 1,
"$schema": "http://cyclonedx.org/schema/bom-1.4.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.4"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" ?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.4" serialNumber="urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac" version="1">
<metadata>
<timestamp>2023-01-07T13:44:32.312678+00:00</timestamp>
</metadata>
<components>
<component type="library" bom-ref="external-lib-1.0.0">
<name>external-lib</name>
<version>1.0.0</version>
<scope>required</scope>
</component>
</components>
<dependencies>
<dependency ref="external-lib-1.0.0"/>
</dependencies>
</bom>
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"components": [
{
"bom-ref": "external-lib-1.0.0",
"name": "external-lib",
"scope": "required",
"type": "library",
"version": "1.0.0"
}
],
"dependencies": [
{
"ref": "external-lib-1.0.0"
}
],
"metadata": {
"timestamp": "2023-01-07T13:44:32.312678+00:00"
},
"properties": [
{
"name": "key1",
"value": "val1"
},
{
"name": "key2",
"value": "val2"
}
],
"serialNumber": "urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac",
"version": 1,
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.5"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" ?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.5" serialNumber="urn:uuid:1441d33a-e0fc-45b5-af3b-61ee52a88bac" version="1">
<metadata>
<timestamp>2023-01-07T13:44:32.312678+00:00</timestamp>
</metadata>
<components>
<component type="library" bom-ref="external-lib-1.0.0">
<name>external-lib</name>
<version>1.0.0</version>
<scope>required</scope>
</component>
</components>
<dependencies>
<dependency ref="external-lib-1.0.0"/>
</dependencies>
<properties>
<property name="key1">val1</property>
<property name="key2">val2</property>
</properties>
</bom>
Loading
Loading