Skip to content
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

BomRef.value default None #505

Merged
merged 3 commits into from
Dec 4, 2023
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
19 changes: 9 additions & 10 deletions cyclonedx/model/bom_ref.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
# Copyright (c) OWASP Foundation. All Rights Reserved.

from typing import Any, Optional
from uuid import uuid4


class BomRef:
Expand All @@ -30,31 +29,31 @@ class BomRef:
"""

def __init__(self, value: Optional[str] = None) -> None:
self.value = value or str(uuid4())
self.value = value

@property
def value(self) -> str:
def value(self) -> Optional[str]:
return self._value

@value.setter
def value(self, value: str) -> None:
self._value = value
def value(self, value: Optional[str]) -> None:
self._value = value or None

def __eq__(self, other: object) -> bool:
if isinstance(other, BomRef):
return other.value == self.value
return str(other) == str(self)
return False

def __lt__(self, other: Any) -> bool:
if isinstance(other, BomRef):
return self.value < other.value
return str(self) < str(other)
return NotImplemented

def __hash__(self) -> int:
return hash(self.value)
return hash(str(self))

def __repr__(self) -> str:
return f'<BomRef {self.value}>'
return f'<BomRef {self._value!r}>'

def __str__(self) -> str:
return self.value
return self.value or ''
6 changes: 3 additions & 3 deletions cyclonedx/output/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,13 +150,13 @@ def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
self.reset()

def discriminate(self) -> None:
known_values = set()
known_values = []
for bomref, _ in self._bomrefs:
value = bomref.value
if value in known_values:
if value is None or value in known_values:
value = self._make_unique()
bomref.value = value
known_values.add(value)
known_values.append(value)

def reset(self) -> None:
for bomref, original_value in self._bomrefs:
Expand Down
2 changes: 1 addition & 1 deletion cyclonedx/serialization/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
class BomRefHelper(BaseHelper):

@classmethod
def serialize(cls, o: Any) -> str:
def serialize(cls, o: Any) -> Optional[str]:
if isinstance(o, BomRef):
return o.value
raise SerializationOfUnexpectedValueException(
Expand Down
4 changes: 3 additions & 1 deletion tests/_data/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,9 @@ def get_bom_with_nested_services() -> Bom:
bom_ref='my-specific-bom-ref-for-my-second-service',
services=[
Service(
name='yet-another-nested-service', provider=get_org_entity_1(), group='what-group', version='6.5.4'
name='yet-another-nested-service',
bom_ref='yet-another-nested-service',
provider=get_org_entity_1(), group='what-group', version='6.5.4'
),
Service(
name='another-nested-service',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@
"name": "my-second-service",
"services": [
{
"bom-ref": "00000000-0000-4000-8000-000000000004",
"bom-ref": "yet-another-nested-service",
"group": "what-group",
"name": "yet-another-nested-service",
"provider": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
<service bom-ref="my-specific-bom-ref-for-my-second-service">
<name>my-second-service</name>
<services>
<service bom-ref="00000000-0000-4000-8000-000000000004">
<service bom-ref="yet-another-nested-service">
<provider>
<name>CycloneDX</name>
<url>https://cyclonedx.org</url>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@
"name": "my-second-service",
"services": [
{
"bom-ref": "00000000-0000-4000-8000-000000000003",
"bom-ref": "yet-another-nested-service",
"group": "what-group",
"name": "yet-another-nested-service",
"provider": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
<service bom-ref="my-specific-bom-ref-for-my-second-service">
<name>my-second-service</name>
<services>
<service bom-ref="00000000-0000-4000-8000-000000000003">
<service bom-ref="yet-another-nested-service">
<provider>
<name>CycloneDX</name>
<url>https://cyclonedx.org</url>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@
"name": "my-second-service",
"services": [
{
"bom-ref": "00000000-0000-4000-8000-000000000002",
"bom-ref": "yet-another-nested-service",
"group": "what-group",
"name": "yet-another-nested-service",
"provider": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@
<service bom-ref="my-specific-bom-ref-for-my-second-service">
<name>my-second-service</name>
<services>
<service bom-ref="00000000-0000-4000-8000-000000000002">
<service bom-ref="yet-another-nested-service">
<provider>
<name>CycloneDX</name>
<url>https://cyclonedx.org</url>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@
"name": "my-second-service",
"services": [
{
"bom-ref": "00000000-0000-4000-8000-000000000001",
"bom-ref": "yet-another-nested-service",
"group": "what-group",
"name": "yet-another-nested-service",
"provider": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@
<service bom-ref="my-specific-bom-ref-for-my-second-service">
<name>my-second-service</name>
<services>
<service bom-ref="00000000-0000-4000-8000-000000000001">
<service bom-ref="yet-another-nested-service">
<provider>
<name>CycloneDX</name>
<url>https://cyclonedx.org</url>
Expand Down
3 changes: 1 addition & 2 deletions tests/test_deserialize_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from cyclonedx.model.bom import Bom
from cyclonedx.model.license import DisjunctiveLicense, LicenseExpression, LicenseRepository
from cyclonedx.schema import OutputFormat, SchemaVersion
from tests import OWN_DATA_DIRECTORY, DeepCompareMixin, SnapshotMixin, mksname, uuid_generator
from tests import OWN_DATA_DIRECTORY, DeepCompareMixin, SnapshotMixin, mksname
from tests._data.models import all_get_bom_funct_valid_immut, all_get_bom_funct_with_incomplete_deps


Expand All @@ -36,7 +36,6 @@ class TestDeserializeJson(TestCase, SnapshotMixin, DeepCompareMixin):

@named_data(*all_get_bom_funct_valid_immut)
@patch('cyclonedx.model.ThisTool._version', 'TESTING')
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=uuid_generator(0, version=4))
def test_prepared(self, get_bom: Callable[[], Bom], *_: Any, **__: Any) -> None:
# only latest schema will have all data populated in serialized form
snapshot_name = mksname(get_bom, SchemaVersion.V1_5, OutputFormat.JSON)
Expand Down
3 changes: 1 addition & 2 deletions tests/test_deserialize_xml.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

from cyclonedx.model.bom import Bom
from cyclonedx.schema import OutputFormat, SchemaVersion
from tests import DeepCompareMixin, SnapshotMixin, mksname, uuid_generator
from tests import DeepCompareMixin, SnapshotMixin, mksname
from tests._data.models import all_get_bom_funct_valid_immut, all_get_bom_funct_with_incomplete_deps


Expand All @@ -33,7 +33,6 @@ class TestDeserializeXml(TestCase, SnapshotMixin, DeepCompareMixin):

@named_data(*all_get_bom_funct_valid_immut)
@patch('cyclonedx.model.ThisTool._version', 'TESTING')
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=uuid_generator(0, version=4))
def test_prepared(self, get_bom: Callable[[], Bom], *_: Any, **__: Any) -> None:
# only latest schema will have all data populated in serialized form
snapshot_name = mksname(get_bom, SchemaVersion.V1_5, OutputFormat.XML)
Expand Down
16 changes: 1 addition & 15 deletions tests/test_enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
from cyclonedx.schema import OutputFormat, SchemaVersion
from cyclonedx.schema._res import BOM_JSON as SCHEMA_JSON, BOM_XML as SCHEMA_XML
from cyclonedx.validation import make_schemabased_validator
from tests import SnapshotMixin, uuid_generator
from tests import SnapshotMixin
from tests._data.models import _make_bom

# region SUT: all the enums
Expand Down Expand Up @@ -164,7 +164,6 @@ def test_knows_value(self, value: str) -> None:

@named_data(*NAMED_OF_SV)
@patch('cyclonedx.model.ThisTool._version', 'TESTING')
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=uuid_generator(0, version=4))
def test_cases_render_valid(self, of: OutputFormat, sv: SchemaVersion, *_: Any, **__: Any) -> None:
bom = _make_bom(services=[Service(name='dummy', bom_ref='dummy', data=(
DataClassification(flow=df, classification=df.name)
Expand All @@ -185,7 +184,6 @@ def test_knows_value(self, value: str) -> None:

@named_data(*NAMED_OF_SV)
@patch('cyclonedx.model.ThisTool._version', 'TESTING')
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=uuid_generator(0, version=4))
def test_cases_render_valid(self, of: OutputFormat, sv: SchemaVersion, *_: Any, **__: Any) -> None:
bom = _make_bom(components=[Component(name='dummy', type=ComponentType.LIBRARY, bom_ref='dummy', licenses=(
DisjunctiveLicense(name=f'att.encoding: {encoding.name}', text=AttachedText(
Expand All @@ -207,7 +205,6 @@ def test_knows_value(self, value: str) -> None:

@named_data(*NAMED_OF_SV)
@patch('cyclonedx.model.ThisTool._version', 'TESTING')
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=uuid_generator(0, version=4))
def test_cases_render_valid(self, of: OutputFormat, sv: SchemaVersion, *_: Any, **__: Any) -> None:
bom = _make_bom(components=[
Component(name='dummy', type=ComponentType.LIBRARY, bom_ref='dummy', external_references=(
Expand All @@ -230,7 +227,6 @@ def test_knows_value(self, value: str) -> None:

@named_data(*NAMED_OF_SV)
@patch('cyclonedx.model.ThisTool._version', 'TESTING')
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=uuid_generator(0, version=4))
def test_cases_render_valid(self, of: OutputFormat, sv: SchemaVersion, *_: Any, **__: Any) -> None:
bom = _make_bom(components=[Component(name='dummy', type=ComponentType.LIBRARY, bom_ref='dummy', hashes=(
HashType(alg=alg, content='ae2b1fca515949e5d54fb22b8ed95575')
Expand All @@ -251,7 +247,6 @@ def test_knows_value(self, value: str) -> None:

@named_data(*NAMED_OF_SV)
@patch('cyclonedx.model.ThisTool._version', 'TESTING')
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=uuid_generator(0, version=4))
def test_cases_render_valid(self, of: OutputFormat, sv: SchemaVersion, *_: Any, **__: Any) -> None:
bom = _make_bom(components=(
Component(bom_ref=f'scoped-{scope.name}', name=f'dummy-{scope.name}',
Expand Down Expand Up @@ -291,7 +286,6 @@ def test_knows_value(self, value: str) -> None:

@named_data(*NAMED_OF_SV)
@patch('cyclonedx.model.ThisTool._version', 'TESTING')
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=uuid_generator(0, version=4))
def test_cases_render_valid(self, of: OutputFormat, sv: SchemaVersion, *_: Any, **__: Any) -> None:
if OutputFormat.XML is of:
schema_cases = set(dp_cases_from_xml_schema(SCHEMA_XML[sv], _DP_ComponentType.XML_SCHEMA_XPATH))
Expand Down Expand Up @@ -329,7 +323,6 @@ def test_knows_value(self, value: str) -> None:

@named_data(*NAMED_OF_SV)
@patch('cyclonedx.model.ThisTool._version', 'TESTING')
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=uuid_generator(0, version=4))
def test_cases_render_valid(self, of: OutputFormat, sv: SchemaVersion, *_: Any, **__: Any) -> None:
bom = _make_bom(components=[
Component(name='dummy', type=ComponentType.LIBRARY, bom_ref='dummy', pedigree=Pedigree(patches=(
Expand All @@ -352,7 +345,6 @@ def test_knows_value(self, value: str) -> None:

@named_data(*NAMED_OF_SV)
@patch('cyclonedx.model.ThisTool._version', 'TESTING')
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=uuid_generator(0, version=4))
def test_cases_render_valid(self, of: OutputFormat, sv: SchemaVersion, *_: Any, **__: Any) -> None:
bom = _make_bom(vulnerabilities=[Vulnerability(
bom_ref='dummy', id='dummy', affects=[BomTarget(ref='urn:cdx:bom23/1#comp42', versions=(
Expand All @@ -375,7 +367,6 @@ def test_knows_value(self, value: str) -> None:

@named_data(*NAMED_OF_SV)
@patch('cyclonedx.model.ThisTool._version', 'TESTING')
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=uuid_generator(0, version=4))
def test_cases_render_valid(self, of: OutputFormat, sv: SchemaVersion, *_: Any, **__: Any) -> None:
bom = _make_bom(vulnerabilities=(
Vulnerability(
Expand All @@ -399,7 +390,6 @@ def test_knows_value(self, value: str) -> None:

@named_data(*NAMED_OF_SV)
@patch('cyclonedx.model.ThisTool._version', 'TESTING')
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=uuid_generator(0, version=4))
def test_cases_render_valid(self, of: OutputFormat, sv: SchemaVersion, *_: Any, **__: Any) -> None:
bom = _make_bom(vulnerabilities=[Vulnerability(
bom_ref='dummy', id='dummy',
Expand All @@ -422,7 +412,6 @@ def test_knows_value(self, value: str) -> None:

@named_data(*NAMED_OF_SV)
@patch('cyclonedx.model.ThisTool._version', 'TESTING')
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=uuid_generator(0, version=4))
def test_cases_render_valid(self, of: OutputFormat, sv: SchemaVersion, *_: Any, **__: Any) -> None:
bom = _make_bom(vulnerabilities=(
Vulnerability(
Expand All @@ -445,7 +434,6 @@ def test_knows_value(self, value: str) -> None:

@named_data(*NAMED_OF_SV)
@patch('cyclonedx.model.ThisTool._version', 'TESTING')
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=uuid_generator(0, version=4))
def test_cases_render_valid(self, of: OutputFormat, sv: SchemaVersion, *_: Any, **__: Any) -> None:
bom = _make_bom(components=[
Component(name='dummy', type=ComponentType.LIBRARY, bom_ref='dummy', pedigree=Pedigree(patches=[
Expand All @@ -470,7 +458,6 @@ def test_knows_value(self, value: str) -> None:

@named_data(*NAMED_OF_SV)
@patch('cyclonedx.model.ThisTool._version', 'TESTING')
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=uuid_generator(0, version=4))
def test_cases_render_valid(self, of: OutputFormat, sv: SchemaVersion, *_: Any, **__: Any) -> None:
bom = _make_bom(vulnerabilities=[Vulnerability(bom_ref='dummy', id='dummy', ratings=(
VulnerabilityRating(method=vss)
Expand All @@ -491,7 +478,6 @@ def test_knows_value(self, value: str) -> None:

@named_data(*NAMED_OF_SV)
@patch('cyclonedx.model.ThisTool._version', 'TESTING')
@patch('cyclonedx.model.bom_ref.uuid4', side_effect=uuid_generator(0, version=4))
def test_cases_render_valid(self, of: OutputFormat, sv: SchemaVersion, *_: Any, **__: Any) -> None:
bom = _make_bom(vulnerabilities=[Vulnerability(bom_ref='dummy', id='dummy', ratings=(
VulnerabilityRating(severity=vs)
Expand Down
10 changes: 4 additions & 6 deletions tests/test_model_component.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@
# Copyright (c) OWASP Foundation. All Rights Reserved.

import datetime
from typing import Any, List
from typing import List
from unittest import TestCase
from unittest.mock import patch

from cyclonedx.exception.model import NoPropertiesProvidedException
from cyclonedx.model import (
Expand All @@ -42,7 +41,7 @@
Pedigree,
)
from cyclonedx.model.issue import IssueClassification, IssueType
from tests import reorder, uuid_generator
from tests import reorder
from tests._data.models import (
get_component_setuptools_simple,
get_component_setuptools_simple_no_version,
Expand Down Expand Up @@ -104,13 +103,12 @@ def test_sort(self) -> None:

class TestModelComponent(TestCase):

@patch('cyclonedx.model.bom_ref.uuid4', side_effect=uuid_generator(version=4))
def test_empty_basic_component(self, *_: Any, **__: Any) -> None:
def test_empty_basic_component(self) -> None:
c = Component(name='test-component')
self.assertEqual(c.name, 'test-component')
self.assertEqual(c.type, ComponentType.LIBRARY)
self.assertIsNone(c.mime_type)
self.assertEqual(str(c.bom_ref), '00000000-0000-4000-8000-000000000001')
self.assertIsNone(c.bom_ref.value)
self.assertIsNone(c.supplier)
self.assertIsNone(c.author)
self.assertIsNone(c.publisher)
Expand Down
17 changes: 7 additions & 10 deletions tests/test_model_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,20 @@
#
# SPDX-License-Identifier: Apache-2.0
# Copyright (c) OWASP Foundation. All Rights Reserved.
from typing import Any


from unittest import TestCase
from unittest.mock import Mock, patch

from cyclonedx.model.service import Service
from tests import reorder, uuid_generator
from tests import reorder


class TestModelService(TestCase):

@patch('cyclonedx.model.bom_ref.uuid4', side_effect=uuid_generator(version=4))
def test_minimal_service(self, mock_uuid: Mock) -> None:
def test_minimal_service(self) -> None:
s = Service(name='my-test-service')
mock_uuid.assert_called()
self.assertEqual(s.name, 'my-test-service')
self.assertEqual(str(s.bom_ref), '00000000-0000-4000-8000-000000000001')
self.assertIsNone(s.bom_ref.value)
self.assertIsNone(s.provider)
self.assertIsNone(s.group)
self.assertIsNone(s.version)
Expand All @@ -44,15 +42,14 @@ def test_minimal_service(self, mock_uuid: Mock) -> None:
self.assertFalse(s.release_notes)
self.assertFalse(s.properties)

@patch('cyclonedx.model.bom_ref.uuid4', side_effect=uuid_generator(version=4))
def test_service_with_services(self, *_: Any, **__: Any) -> None:
def test_service_with_services(self) -> None:
parent_service = Service(name='parent-service')
parent_service.services = [
Service(name='child-service-1'),
Service(name='child-service-2'),
]
self.assertEqual(parent_service.name, 'parent-service')
self.assertEqual(str(parent_service.bom_ref), '00000000-0000-4000-8000-000000000001')
self.assertIsNone(parent_service.bom_ref.value)
self.assertIsNone(parent_service.provider)
self.assertIsNone(parent_service.group)
self.assertIsNone(parent_service.version)
Expand Down
Loading