diff --git a/cyclonedx/schema/_res/README.md b/cyclonedx/schema/_res/README.md index 5596be0f8..fcc5cd081 100644 --- a/cyclonedx/schema/_res/README.md +++ b/cyclonedx/schema/_res/README.md @@ -30,5 +30,5 @@ changes: 1. `https?://cyclonedx.org/schema/spdx` was replaced with `spdx.SNAPSHOT.xsd` 2. `spdx.schema.json` was replaced with `spdx.SNAPSHOT.schema.json` 3. `jsf-0.82.schema.json` was replaced with `jsf-0.82.SNAPSHOT.schema.json` -4. `properties.$schema.enum` was fixed to match `$id` +4. `properties.$schema.enum` was removed 5. `required.version` removed, as it is actually optional with default value diff --git a/cyclonedx/schema/_res/bom-1.2-strict.SNAPSHOT.schema.json b/cyclonedx/schema/_res/bom-1.2-strict.SNAPSHOT.schema.json index 682cf739d..a36fb4b6c 100644 --- a/cyclonedx/schema/_res/bom-1.2-strict.SNAPSHOT.schema.json +++ b/cyclonedx/schema/_res/bom-1.2-strict.SNAPSHOT.schema.json @@ -11,10 +11,7 @@ "additionalProperties": false, "properties": { "$schema": { - "type": "string", - "enum": [ - "http://cyclonedx.org/schema/bom-1.2b.schema.json" - ] + "type": "string" }, "bomFormat": { "$id": "#/properties/bomFormat", diff --git a/cyclonedx/schema/_res/bom-1.3-strict.SNAPSHOT.schema.json b/cyclonedx/schema/_res/bom-1.3-strict.SNAPSHOT.schema.json index a11426201..3f1b82d92 100644 --- a/cyclonedx/schema/_res/bom-1.3-strict.SNAPSHOT.schema.json +++ b/cyclonedx/schema/_res/bom-1.3-strict.SNAPSHOT.schema.json @@ -11,10 +11,7 @@ "additionalProperties": false, "properties": { "$schema": { - "type": "string", - "enum": [ - "http://cyclonedx.org/schema/bom-1.3a.schema.json" - ] + "type": "string" }, "bomFormat": { "$id": "#/properties/bomFormat", diff --git a/cyclonedx/schema/_res/bom-1.4.SNAPSHOT.schema.json b/cyclonedx/schema/_res/bom-1.4.SNAPSHOT.schema.json index 1a8858104..48a462e30 100644 --- a/cyclonedx/schema/_res/bom-1.4.SNAPSHOT.schema.json +++ b/cyclonedx/schema/_res/bom-1.4.SNAPSHOT.schema.json @@ -11,10 +11,7 @@ "additionalProperties": false, "properties": { "$schema": { - "type": "string", - "enum": [ - "http://cyclonedx.org/schema/bom-1.4.schema.json" - ] + "type": "string" }, "bomFormat": { "type": "string", diff --git a/cyclonedx/schema/_res/bom-1.5.SNAPSHOT.schema.json b/cyclonedx/schema/_res/bom-1.5.SNAPSHOT.schema.json index 7edeab0f0..72781e909 100644 --- a/cyclonedx/schema/_res/bom-1.5.SNAPSHOT.schema.json +++ b/cyclonedx/schema/_res/bom-1.5.SNAPSHOT.schema.json @@ -11,10 +11,7 @@ "additionalProperties": false, "properties": { "$schema": { - "type": "string", - "enum": [ - "http://cyclonedx.org/schema/bom-1.5.schema.json" - ] + "type": "string" }, "bomFormat": { "type": "string", diff --git a/tests/_data/own/json/1.2/bom_with_mixed_licenses.json b/tests/_data/own/json/1.2/bom_with_mixed_licenses.json index da4d3c54f..7408922a8 100644 --- a/tests/_data/own/json/1.2/bom_with_mixed_licenses.json +++ b/tests/_data/own/json/1.2/bom_with_mixed_licenses.json @@ -1,7 +1,7 @@ { - "$schema": "http://cyclonedx.org/schema/bom-1.4.schema.json", + "$schema": "http://cyclonedx.org/schema/bom-1.2.schema.json", "bomFormat": "CycloneDX", - "specVersion": "1.4", + "specVersion": "1.2", "metadata": { "component": { "description": "before CDX 1.5 it was allowed to mix `expression` and `license`", @@ -11,13 +11,9 @@ {"license": {"name": "foo license"}} ], "name": "app", - "type": "application" - }, - "licenses": [ - {"license": {"name": "foo license"}}, - {"license": {"id": "MIT"}}, - {"expression": "MIT OR Apache-2.0"} - ] + "type": "application", + "version": "1.2" + } }, "components": [ { @@ -28,7 +24,8 @@ {"expression": "MIT OR Apache-2.0"} ], "name": "comp", - "type": "library" + "type": "library", + "version": "1337" } ], "services": [ diff --git a/tests/_data/own/json/1.3/bom_with_mixed_licenses.json b/tests/_data/own/json/1.3/bom_with_mixed_licenses.json index da4d3c54f..270a246cd 100644 --- a/tests/_data/own/json/1.3/bom_with_mixed_licenses.json +++ b/tests/_data/own/json/1.3/bom_with_mixed_licenses.json @@ -1,7 +1,7 @@ { - "$schema": "http://cyclonedx.org/schema/bom-1.4.schema.json", + "$schema": "http://cyclonedx.org/schema/bom-1.3.schema.json", "bomFormat": "CycloneDX", - "specVersion": "1.4", + "specVersion": "1.3", "metadata": { "component": { "description": "before CDX 1.5 it was allowed to mix `expression` and `license`", @@ -11,7 +11,8 @@ {"license": {"name": "foo license"}} ], "name": "app", - "type": "application" + "type": "application", + "version": "1.3" }, "licenses": [ {"license": {"name": "foo license"}}, @@ -28,7 +29,8 @@ {"expression": "MIT OR Apache-2.0"} ], "name": "comp", - "type": "library" + "type": "library", + "version": "1337" } ], "services": [ diff --git a/tests/test_deserialize_json.py b/tests/test_deserialize_json.py index 23077858a..c85664b03 100644 --- a/tests/test_deserialize_json.py +++ b/tests/test_deserialize_json.py @@ -67,7 +67,8 @@ def test(ls: LicenseRepository) -> None: with open(json_file) as f: json = json_loads(f.read()) bom: Bom = Bom.from_json(json) - test(bom.metadata.licenses) + if sv is not SchemaVersion.V1_2: + test(bom.metadata.licenses) test(bom.metadata.component.licenses) test(list(bom.components)[0].licenses) test(list(bom.services)[0].licenses) diff --git a/tests/test_validation_json.py b/tests/test_validation_json.py index 043592b0f..7a2971898 100644 --- a/tests/test_validation_json.py +++ b/tests/test_validation_json.py @@ -16,6 +16,7 @@ # Copyright (c) OWASP Foundation. All Rights Reserved. from glob import iglob +from itertools import chain from os.path import join from typing import Generator from unittest import TestCase @@ -25,18 +26,25 @@ from cyclonedx.exception import MissingOptionalDependencyException from cyclonedx.schema import OutputFormat, SchemaVersion from cyclonedx.validation.json import JsonStrictValidator, JsonValidator -from tests import SCHEMA_TESTDATA_DIRECTORY, DpTuple +from tests import OWN_DATA_DIRECTORY, SCHEMA_TESTDATA_DIRECTORY, DpTuple UNSUPPORTED_SCHEMA_VERSIONS = {SchemaVersion.V1_0, SchemaVersion.V1_1, } -def _dp(prefix: str) -> Generator: +def _dp_sv_tf(prefix: str) -> Generator: return ( DpTuple((sv, tf)) for sv in SchemaVersion if sv not in UNSUPPORTED_SCHEMA_VERSIONS for tf in iglob(join(SCHEMA_TESTDATA_DIRECTORY, sv.to_version(), f'{prefix}-*.json')) ) +def _dp_sv_own() -> Generator: + return ( + DpTuple((sv, tf)) for sv in SchemaVersion if sv not in UNSUPPORTED_SCHEMA_VERSIONS + for tf in iglob(join(OWN_DATA_DIRECTORY, 'json', sv.to_version(), '*.json')) + ) + + @ddt class TestJsonValidator(TestCase): @@ -51,7 +59,10 @@ def test_throws_with_unsupported_schema_version(self, schema_version: SchemaVers with self.assertRaisesRegex(ValueError, 'Unsupported schema_version'): JsonValidator(schema_version) - @idata(_dp('valid')) + @idata(chain( + _dp_sv_tf('valid'), + _dp_sv_own() + )) @unpack def test_validate_no_none(self, schema_version: SchemaVersion, test_data_file: str) -> None: validator = JsonValidator(schema_version) @@ -63,7 +74,7 @@ def test_validate_no_none(self, schema_version: SchemaVersion, test_data_file: s self.skipTest('MissingOptionalDependencyException') self.assertIsNone(validation_error) - @idata(_dp('invalid')) + @idata(_dp_sv_tf('invalid')) @unpack def test_validate_expected_error(self, schema_version: SchemaVersion, test_data_file: str) -> None: validator = JsonValidator(schema_version) @@ -85,7 +96,10 @@ def test_throws_with_unsupported_schema_version(self, schema_version: SchemaVers with self.assertRaisesRegex(ValueError, 'Unsupported schema_version'): JsonStrictValidator(schema_version) - @idata(_dp('valid')) + @idata(chain( + _dp_sv_tf('valid'), + _dp_sv_own() + )) @unpack def test_validate_no_none(self, schema_version: SchemaVersion, test_data_file: str) -> None: validator = JsonStrictValidator(schema_version) @@ -97,7 +111,7 @@ def test_validate_no_none(self, schema_version: SchemaVersion, test_data_file: s self.skipTest('MissingOptionalDependencyException') self.assertIsNone(validation_error) - @idata(_dp('invalid')) + @idata(_dp_sv_tf('invalid')) @unpack def test_validate_expected_error(self, schema_version: SchemaVersion, test_data_file: str) -> None: validator = JsonStrictValidator(schema_version) diff --git a/tests/test_validation_xml.py b/tests/test_validation_xml.py index ac400c710..e5d914799 100644 --- a/tests/test_validation_xml.py +++ b/tests/test_validation_xml.py @@ -16,6 +16,7 @@ # Copyright (c) OWASP Foundation. All Rights Reserved. from glob import iglob +from itertools import chain from os.path import join from typing import Generator from unittest import TestCase @@ -25,18 +26,25 @@ from cyclonedx.exception import MissingOptionalDependencyException from cyclonedx.schema import OutputFormat, SchemaVersion from cyclonedx.validation.xml import XmlValidator -from tests import SCHEMA_TESTDATA_DIRECTORY, DpTuple +from tests import OWN_DATA_DIRECTORY, SCHEMA_TESTDATA_DIRECTORY, DpTuple UNSUPPORTED_SCHEMA_VERSIONS = set() -def _dp(prefix: str) -> Generator: +def _dp_sv_tf(prefix: str) -> Generator: return ( DpTuple((sv, tf)) for sv in SchemaVersion if sv not in UNSUPPORTED_SCHEMA_VERSIONS for tf in iglob(join(SCHEMA_TESTDATA_DIRECTORY, sv.to_version(), f'{prefix}-*.xml')) ) +def _dp_sv_own() -> Generator: + return ( + DpTuple((sv, tf)) for sv in SchemaVersion if sv not in UNSUPPORTED_SCHEMA_VERSIONS + for tf in iglob(join(OWN_DATA_DIRECTORY, 'xml', sv.to_version(), '*.xml')) + ) + + @ddt class TestXmlValidator(TestCase): @@ -51,7 +59,10 @@ def test_throws_with_unsupported_schema_version(self, schema_version: SchemaVers with self.assertRaisesRegex(ValueError, f'unsupported schema_version: {schema_version}'): XmlValidator(schema_version) - @idata(_dp('valid')) + @idata(chain( + _dp_sv_tf('valid'), + _dp_sv_own() + )) @unpack def test_validate_no_none(self, schema_version: SchemaVersion, test_data_file: str) -> None: validator = XmlValidator(schema_version) @@ -63,7 +74,7 @@ def test_validate_no_none(self, schema_version: SchemaVersion, test_data_file: s self.skipTest('MissingOptionalDependencyException') self.assertIsNone(validation_error) - @idata(_dp('invalid')) + @idata(_dp_sv_tf('invalid')) @unpack def test_validate_expected_error(self, schema_version: SchemaVersion, test_data_file: str) -> None: validator = XmlValidator(schema_version) diff --git a/tools/schema-downloader.py b/tools/schema-downloader.py index 3ef556d09..99def5784 100644 --- a/tools/schema-downloader.py +++ b/tools/schema-downloader.py @@ -29,13 +29,14 @@ ] } -# "version" is not required but optional with a default value! -# this is wrong in schema<1.5 +# "$schema" is not required but optional. +# that enum constraint value there is complicated -> remove it. +# See https://github.com/CycloneDX/specification/issues/402 +# See https://github.com/CycloneDX/specification/pull/403 _BOM_SCHEMA_ENUM_RE = re.compile( - r'("\$id": "(http://cyclonedx\.org/schema/bom.+?\.schema\.json)".*"enum": \[\s+")' - r'http://cyclonedx\.org/schema/bom.+?\.schema\.json"', + r',?\s*"enum":\s*\[\s*"http://cyclonedx\.org/schema/.+?\.schema\.json"\s*\]', re.DOTALL) -_BOM_SCHEMA_ENUM_REPL = r'\1\2"' +_BOM_SCHEMA_ENUM_REPL = r'' # "version" is not required but optional with a default value!