### Install Development Dependencies Source: https://github.com/cyclonedx/cyclonedx-python-lib/blob/main/docs/contributing.md Installs development dependencies and tools required for contributing to the project using poetry. ```shell poetry install --all-extras ``` -------------------------------- ### Install CycloneDX Python Lib Source: https://github.com/cyclonedx/cyclonedx-python-lib/blob/main/docs/install.md Use this command to install the CycloneDX Python library from PyPI. ```sh pip install cyclonedx-python-lib ``` -------------------------------- ### Generate Documentation Locally Source: https://github.com/cyclonedx/cyclonedx-python-lib/blob/main/docs/contributing.md Generates the project documentation locally using Sphinx. Requires navigating to the docs directory and installing requirements. ```shell cd docs pip install -r requirements.txt make html ``` -------------------------------- ### Install Pre-commit Hooks Source: https://github.com/cyclonedx/cyclonedx-python-lib/blob/main/docs/contributing.md Installs pre-commit hooks to automatically run checks before each commit, ensuring code quality and adherence to project standards. ```shell pre-commit install ``` -------------------------------- ### Install CycloneDX Python Lib with Extras Source: https://github.com/cyclonedx/cyclonedx-python-lib/blob/main/docs/install.md Install the library with optional dependencies for validation by specifying the 'validation' extra. This includes dependencies for both JSON and XML validation. ```sh pip install 'cyclonedx-python-lib[validation]' ``` -------------------------------- ### Output SBOM to XML File (Specific Schema) Source: https://github.com/cyclonedx/cyclonedx-python-lib/blob/main/docs/outputting.md Use `make_outputter` to write an XML SBOM to a file. This example specifies schema version 1.2. ```python from cyclonedx.output import make_outputter, BaseOutput, OutputFormat, SchemaVersion outputter: BaseOutput = make_outputter(bom=bom, output_format=OutputFormat.XML, schema_version=SchemaVersion.V1_2) outputter.output_to_file(filename='/tmp/sbom-v1.2.xml') ``` -------------------------------- ### Output SBOM to JSON String (Latest Schema) Source: https://github.com/cyclonedx/cyclonedx-python-lib/blob/main/docs/outputting.md Use `make_outputter` to generate a JSON SBOM string. This example defaults to the latest supported schema version. ```python from cyclonedx.output import make_outputter, BaseOutput, OutputFormat, SchemaVersion outputter: BaseOutput = make_outputter(bom=bom, output_format=OutputFormat.JSON, schema_version=SchemaVersion.V1_7) bom_json: str = outputter.output_as_string() ``` -------------------------------- ### Build and Serialize Complex SBOM to JSON Source: https://github.com/cyclonedx/cyclonedx-python-lib/blob/main/docs/examples.md Builds a complex CycloneDX SBOM with custom components, licenses, and dependencies, then serializes it to JSON format with explicit schema version and outputter configuration. Includes validation of the generated JSON. ```python # This file is part of CycloneDX Python Library # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # SPDX-License-Identifier: Apache-2.0 # Copyright (c) OWASP Foundation. All Rights Reserved. import sys from typing import TYPE_CHECKING from packageurl import PackageURL from cyclonedx.contrib.license.factories import LicenseFactory from cyclonedx.contrib.this.builders import this_component as cdx_lib_component from cyclonedx.exception import MissingOptionalDependencyException from cyclonedx.model import XsUri from cyclonedx.model.bom import Bom from cyclonedx.model.component import Component, ComponentType from cyclonedx.model.contact import OrganizationalEntity from cyclonedx.output import make_outputter from cyclonedx.output.json import JsonV1Dot5 from cyclonedx.schema import OutputFormat, SchemaVersion from cyclonedx.validation import make_schemabased_validator from cyclonedx.validation.json import JsonStrictValidator if TYPE_CHECKING: from cyclonedx.output.json import Json as JsonOutputter from cyclonedx.output.xml import Xml as XmlOutputter from cyclonedx.validation.xml import XmlValidator lc_factory = LicenseFactory() # region build the BOM bom = Bom() bom.metadata.tools.components.add(cdx_lib_component()) bom.metadata.tools.components.add(Component( name='my-own-SBOM-generator', type=ComponentType.APPLICATION, )) bom.metadata.component = root_component = Component( name='myApp', type=ComponentType.APPLICATION, licenses=[lc_factory.make_from_string('MIT')], bom_ref='myApp', ) component1 = Component( type=ComponentType.LIBRARY, name='some-component', group='acme', version='1.33.7-beta.1', licenses=[lc_factory.make_from_string('(c) 2021 Acme inc.')], supplier=OrganizationalEntity( name='Acme Inc', urls=[XsUri('https://www.acme.org')] ), bom_ref='myComponent@1.33.7-beta.1', purl=PackageURL('generic', 'acme', 'some-component', '1.33.7-beta.1') ) bom.components.add(component1) bom.register_dependency(root_component, [component1]) component2 = Component( type=ComponentType.LIBRARY, name='some-library', licenses=[lc_factory.make_from_string('GPL-3.0-only WITH Classpath-exception-2.0')] ) bom.components.add(component2) bom.register_dependency(component1, [component2]) # endregion build the BOM # region JSON """demo with explicit instructions for SchemaVersion, outputter and validator""" my_json_outputter: 'JsonOutputter' = JsonV1Dot5(bom) serialized_json = my_json_outputter.output_as_string(indent=2) print(serialized_json) my_json_validator = JsonStrictValidator(SchemaVersion.V1_7) try: json_validation_errors = my_json_validator.validate_str(serialized_json) if json_validation_errors: print('JSON invalid', 'ValidationError:', repr(json_validation_errors), sep='\n', file=sys.stderr) sys.exit(2) print('JSON valid') except MissingOptionalDependencyException as error: print('JSON-validation was skipped due to', error) # endregion JSON print('', '=' * 30, '', sep='\n') # region XML """demo with implicit instructions for SchemaVersion, outputter and validator. TypeCheckers will catch errors.""" my_xml_outputter: 'XmlOutputter' = make_outputter(bom, OutputFormat.XML, SchemaVersion.V1_7) serialized_xml = my_xml_outputter.output_as_string(indent=2) print(serialized_xml) my_xml_validator: 'XmlValidator' = make_schemabased_validator( my_xml_outputter.output_format, my_xml_outputter.schema_version) try: xml_validation_errors = my_xml_validator.validate_str(serialized_xml) if xml_validation_errors: print('XML invalid', 'ValidationError:', repr(xml_validation_errors), sep='\n', file=sys.stderr) sys.exit(2) print('XML valid') except MissingOptionalDependencyException as error: print('XML-validation was skipped due to', error) ``` -------------------------------- ### Migrate Tool Import Path Source: https://github.com/cyclonedx/cyclonedx-python-lib/blob/main/docs/upgrading.md The `cyclonedx.model.Tool` class has been moved. Update your import statements accordingly. ```python from cyclonedx.model.tool import Tool ``` -------------------------------- ### Output SBOM to XML File (Specific Class) Source: https://github.com/cyclonedx/cyclonedx-python-lib/blob/main/docs/outputting.md Instantiate a specific XML outputter class for a given schema version to write an XML SBOM to a file. ```python from cyclonedx.output.xml import XmlV1Dot2 outputter = XmlV1Dot2(bom=bom) outputter.output_to_file(filename='/tmp/sbom-v1.2.xml') ``` -------------------------------- ### Update Metadata Tools Addition Source: https://github.com/cyclonedx/cyclonedx-python-lib/blob/main/docs/upgrading.md The `cyclonedx.model.bom.BomMetaData.tools` property is now a `ToolRepository`. Adjust how you add tools to the metadata. ```python my_bom.metadata.tools.tools.add(my_tool) ``` -------------------------------- ### Apply Code Style and Formatting Source: https://github.com/cyclonedx/cyclonedx-python-lib/blob/main/docs/contributing.md Applies code style checks and formatting using tox for pyupgrade, isort, and autopep8. ```shell poetry run -- tox r -e pyupgrade -- --exit-zero-even-if-changed poetry run -- tox r -e isort poetry run -- tox r -e autopep8 ``` -------------------------------- ### Run All Tests with Tox Source: https://github.com/cyclonedx/cyclonedx-python-lib/blob/main/docs/contributing.md Executes all defined test environments using tox. ```shell poetry run tox run ``` -------------------------------- ### Set LicenseExpression Acknowledgement Source: https://github.com/cyclonedx/cyclonedx-python-lib/blob/main/docs/upgrading.md The `LicenseExpression()` constructor now requires optional arguments to be passed as keyword arguments, specifically for acknowledgements. ```python LicenseExpression(my_exp, acknowledgement=my_acknowledgement) ``` -------------------------------- ### Output SBOM to JSON String (Specific Class) Source: https://github.com/cyclonedx/cyclonedx-python-lib/blob/main/docs/outputting.md Instantiate a specific JSON outputter class for a given schema version to generate a JSON SBOM string. ```python from cyclonedx.output.json import JsonV1Dot7 outputter = JsonV1Dot7(bom=bom) bom_json: str = outputter.output_as_string() ``` -------------------------------- ### Re-create Snapshots with Shell Command Source: https://github.com/cyclonedx/cyclonedx-python-lib/blob/main/tests/_data/snapshots/README.md Use this command to automatically re-create test assets by setting the CDX_TEST_RECREATE_SNAPSHOTS environment variable. It's recommended to also set PYTHONHASHSEED=0 for consistent results. The generated files may require manual reformatting. ```shell CDX_TEST_RECREATE_SNAPSHOTS=1 poetry run tox -e py ``` -------------------------------- ### Update Vulnerability Tools Addition Source: https://github.com/cyclonedx/cyclonedx-python-lib/blob/main/docs/upgrading.md The `cyclonedx.model.vulnerability.Vulnerability.tools` property is now a `ToolRepository`. Adjust how you add tools to vulnerabilities. ```python my_vulnerability.tools.tools.add(my_tool) ``` -------------------------------- ### Add Library to Metadata Tools Source: https://github.com/cyclonedx/cyclonedx-python-lib/blob/main/docs/upgrading.md Downstream users should add this to their BOM build processes to track the used library. This is required because the library no longer adds itself to the metadata. ```python from cyclonedx.builder.this import this_component as cdx_lib_component from cyclonedx.model.bom import Bom bom = Bom() bom.metadata.tools.components.add(cdx_lib_component()) ``` -------------------------------- ### Deserialize JSON SBOM Source: https://github.com/cyclonedx/cyclonedx-python-lib/blob/main/docs/examples.md Demonstrates deserializing a CycloneDX SBOM from a JSON string. Includes validation using JsonStrictValidator and conversion to a Bom object. Handles MissingOptionalDependencyException if JSON validation dependencies are not met. ```python # This file is part of CycloneDX Python Library # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # SPDX-License-Identifier: Apache-2.0 # Copyright (c) OWASP Foundation. All Rights Reserved. import sys import warnings from json import loads as json_loads from typing import TYPE_CHECKING from defusedxml import ElementTree as SafeElementTree # type:ignore[import-untyped] from cyclonedx.exception import MissingOptionalDependencyException from cyclonedx.model.bom import Bom from cyclonedx.schema import OutputFormat, SchemaVersion from cyclonedx.schema.deprecation import BaseSchemaDeprecationWarning from cyclonedx.validation import make_schemabased_validator from cyclonedx.validation.json import JsonStrictValidator if TYPE_CHECKING: from cyclonedx.validation.xml import XmlValidator # region JSON json_data = """ { "$schema": "http://cyclonedx.org/schema/bom-1.7.schema.json", "bomFormat": "CycloneDX", "specVersion": "1.7", "serialNumber": "urn:uuid:88fabcfa-7529-4ba2-8256-29bec0c03900", "version": 1, "metadata": { "timestamp": "2024-02-10T21:38:53.313120+00:00", "tools": [ { "vendor": "CycloneDX", "name": "cyclonedx-python-lib", "version": "6.4.1", "externalReferences": [ { "type": "build-system", "url": "https://github.com/CycloneDX/cyclonedx-python-lib/actions" }, { "type": "distribution", "url": "https://pypi.org/project/cyclonedx-python-lib/" }, { "type": "documentation", "url": "https://cyclonedx-python-library.readthedocs.io/" }, { "type": "issue-tracker", "url": "https://github.com/CycloneDX/cyclonedx-python-lib/issues" }, { "type": "license", "url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE" }, { "type": "release-notes", "url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md" }, { "type": "vcs", "url": "https://github.com/CycloneDX/cyclonedx-python-lib" }, { "type": "website", "url": "https://github.com/CycloneDX/cyclonedx-python-lib/#readme" } ] } ], "component": { "bom-ref": "myApp", "name": "myApp", "type": "application", "licenses": [ { "license": { "id": "MIT" } } ] } }, "components": [ { "bom-ref": "myComponent@1.33.7-beta.1", "type": "library", "group": "acme", "name": "some-component", "version": "1.33.7-beta.1", "purl": "pkg:generic/acme/some-component@1.33.7-beta.1", "licenses": [ { "license": { "name": "(c) 2021 Acme inc." } } ], "supplier": { "name": "Acme Inc", "url": [ "https://www.acme.org" ] } }, { "bom-ref": "some-lib", "type": "library", "name": "some-library", "licenses": [ { "expression": "GPL-3.0-only WITH Classpath-exception-2.0" } ] } ], "dependencies": [ { "ref": "some-lib" }, { "dependsOn": [ "myComponent@1.33.7-beta.1" ], "ref": "myApp" }, { "dependsOn": [ "some-lib" ], "ref": "myComponent@1.33.7-beta.1" } ] } """ my_json_validator = JsonStrictValidator(SchemaVersion.V1_7) try: json_validation_errors = my_json_validator.validate_str(json_data) if json_validation_errors: print('JSON invalid', 'ValidationError:', repr(json_validation_errors), sep='\n', file=sys.stderr) sys.exit(2) print('JSON valid') except MissingOptionalDependencyException as error: print('JSON-validation was skipped due to', error) with warnings.catch_warnings(): warnings.filterwarnings('ignore', category=BaseSchemaDeprecationWarning) bom_from_json = Bom.from_json( # type: ignore[attr-defined] json_loads(json_data)) print('bom_from_json', repr(bom_from_json)) # endregion JSON print('', '=' * 30, '', sep='\n') ``` -------------------------------- ### Validate SBOM with Dynamic Format Detection Source: https://github.com/cyclonedx/cyclonedx-python-lib/blob/main/docs/examples.md This function validates an SBOM by first detecting its format (JSON or XML) and schema version using helper functions. It then uses the detected information to create a schema-based validator and checks for validation errors. It handles missing optional dependencies required for validation. ```python import sys from cyclonedx.exception import MissingOptionalDependencyException from cyclonedx.schema import SchemaVersion from cyclonedx.validation import make_schemabased_validator # Assume _detect_json_format and _detect_xml_format are defined as above # Assume JSON_SBOM and XML_SBOM are defined strings containing SBOM data def validate_sbom(raw_data: str) -> bool: """Validate an SBOM by detecting its format and version.""" # Detect format and version format_info = _detect_json_format(raw_data) or _detect_xml_format(raw_data) if not format_info: return False input_format, schema_version = format_info try: validator = make_schemabased_validator(input_format, schema_version) errors = validator.validate_str(raw_data) if errors: print(f'Validation failed ({input_format.name} {schema_version.to_version()}): {errors}', file=sys.stderr) return False print(f'Valid {input_format.name} SBOM (schema {schema_version.to_version()})') return True except MissingOptionalDependencyException as e: print(f'Validation skipped (missing dependencies): {e}') return False # Execute dynamic validation # validate_sbom(JSON_SBOM) # validate_sbom(XML_SBOM) ``` -------------------------------- ### Detect XML SBOM Format and Version Source: https://github.com/cyclonedx/cyclonedx-python-lib/blob/main/docs/examples.md This function attempts to detect if raw SBOM data is in XML format and determines its schema version by inspecting the XML namespace. It requires the 'lxml' library and handles XML syntax errors and namespace parsing issues. ```python import sys from typing import Optional, Tuple from cyclonedx.output import OutputFormat from cyclonedx.schema import SchemaVersion try: from lxml import etree # type: ignore[import-untyped] except ImportError: etree = None def _detect_xml_format(raw_data: str) -> Optional[Tuple[OutputFormat, SchemaVersion]]: if etree is None: return None try: xml_tree = etree.fromstring(raw_data.encode('utf-8')) except etree.XMLSyntaxError: return None for ns in xml_tree.nsmap.values(): if ns and ns.startswith('http://cyclonedx.org/schema/bom/'): version_str = ns.split('/')[-1] try: return (OutputFormat.XML, SchemaVersion.from_version(version_str)) except Exception: print('failed to detect schema_version from namespace', repr(ns), file=sys.stderr) return None print('failed to detect CycloneDX namespace in XML document', file=sys.stderr) return None ``` -------------------------------- ### Validate and Parse XML SBOM Data Source: https://github.com/cyclonedx/cyclonedx-python-lib/blob/main/docs/examples.md This snippet demonstrates how to validate an XML SBOM string against a schema and then parse it into a Bom object. It includes error handling for missing dependencies required for validation. ```python xml_data = """ 2024-02-10T21:38:53.313120+00:00 CycloneDX cyclonedx-python-lib 6.4.1 https://github.com/CycloneDX/cyclonedx-python-lib/actions https://pypi.org/project/cyclonedx-python-lib/ https://cyclonedx-python-library.readthedocs.io/ https://github.com/CycloneDX/cyclonedx-python-lib/issues https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md https://github.com/CycloneDX/cyclonedx-python-lib https://github.com/CycloneDX/cyclonedx-python-lib/#readme myApp MIT Acme Inc https://www.acme.org acme some-component 1.33.7-beta.1 (c) 2021 Acme inc. pkg:generic/acme/some-component@1.33.7-beta.1 some-library GPL-3.0-only WITH Classpath-exception-2.0 """ my_xml_validator: 'XmlValidator' = make_schemabased_validator(OutputFormat.XML, SchemaVersion.V1_7) try: xml_validation_errors = my_xml_validator.validate_str(xml_data) if xml_validation_errors: print('XML invalid', 'ValidationError:', repr(xml_validation_errors), sep='\n', file=sys.stderr) sys.exit(2) print('XML valid') except MissingOptionalDependencyException as error: print('XML-validation was skipped due to', error) with warnings.catch_warnings(): warnings.filterwarnings('ignore', category=BaseSchemaDeprecationWarning) bom_from_xml = Bom.from_xml( # type: ignore[attr-defined] SafeElementTree.fromstring(xml_data)) print('bom_from_xml', repr(bom_from_xml)) ``` -------------------------------- ### Detect JSON SBOM Format and Version Source: https://github.com/cyclonedx/cyclonedx-python-lib/blob/main/docs/examples.md Use this function to parse raw SBOM data, detect if it's in JSON format, and extract the schema version. It handles JSON decoding errors and schema version parsing issues. ```python import json import sys from typing import Optional, Tuple from cyclonedx.output import OutputFormat from cyclonedx.schema import SchemaVersion def _detect_json_format(raw_data: str) -> Optional[Tuple[OutputFormat, SchemaVersion]]: """Detect JSON format and extract schema version.""" try: data = json.loads(raw_data) except json.JSONDecodeError: return None spec_version_str = data.get('specVersion') try: schema_version = SchemaVersion.from_version(spec_version_str) except Exception: print('failed to detect schema_version from', repr(spec_version_str), file=sys.stderr) return None return (OutputFormat.JSON, schema_version) ``` -------------------------------- ### Sign Commits Source: https://github.com/cyclonedx/cyclonedx-python-lib/blob/main/docs/contributing.md Signs commits to indicate agreement with the project's terms and licenses, adhering to the Developer Certificate of Origin (DCO). ```shell git commit -s ... ``` -------------------------------- ### Deserialize CycloneDX JSON BOM Source: https://github.com/cyclonedx/cyclonedx-python-lib/blob/main/docs/modelling.md Use this method to read and deserialize a JSON CycloneDX document. Ensure the file is read and loaded as JSON by the programmer. ```python import json from cyclonedx.model.bom import Bom with open('/path/to/my/cyclonedx.json') as input_json: deserialized_bom = Bom.from_json(data=json.loads(input_json.read())) ``` -------------------------------- ### Compare JSON and XML BOM Objects Source: https://github.com/cyclonedx/cyclonedx-python-lib/blob/main/docs/examples.md This snippet asserts that a Bom object created from JSON data is equal to a Bom object created from XML data. This is useful for verifying data consistency between different serialization formats. ```python print('', '=' * 30, '', sep='\n') print('assert bom_from_json equals bom_from_xml') assert bom_from_json == bom_from_xml, 'expected to have equal BOMs from JSON and XML' ``` -------------------------------- ### Deserialize CycloneDX XML BOM Source: https://github.com/cyclonedx/cyclonedx-python-lib/blob/main/docs/modelling.md Use this method to read and deserialize an XML CycloneDX document. Be cautious of XML vulnerabilities and consider using libraries like defusedxml. ```python from xml.etree import ElementTree from cyclonedx.model.bom import Bom with open('/path/to/my/cyclonedx.xml') as input_xml: deserialized_bom = cast(Bom, Bom.from_xml(data=ElementTree.fromstring(input_xml.read()))) ``` -------------------------------- ### Validate XML CycloneDX SBOM Source: https://github.com/cyclonedx/cyclonedx-python-lib/blob/main/docs/examples.md Similar to JSON validation, an XML validator can be created using `make_schemabased_validator` with `OutputFormat.XML`. The `validate_str` method then checks the provided XML SBOM string for validity against the specified schema version. ```python import json import sys from typing import TYPE_CHECKING, Optional from cyclonedx.exception import MissingOptionalDependencyException from cyclonedx.schema import OutputFormat, SchemaVersion from cyclonedx.validation import make_schemabased_validator if TYPE_CHECKING: from cyclonedx.validation.json import JsonValidator from cyclonedx.validation.xml import XmlValidator XML_SBOM = """ my-app 1.0.0 """ print('\n' + '=' * 30 + '\n') # region XML Validation print('--- XML Validation ---') xml_validator: 'XmlValidator' = make_schemabased_validator(OutputFormat.XML, SchemaVersion.V1_5) try: xml_validation_errors = xml_validator.validate_str(XML_SBOM) if xml_validation_errors: print('XML SBOM is invalid!', file=sys.stderr) else: print('XML SBOM is valid') except MissingOptionalDependencyException as error: print('XML validation was skipped:', error) # endregion XML Validation ``` -------------------------------- ### Validate JSON CycloneDX SBOM Source: https://github.com/cyclonedx/cyclonedx-python-lib/blob/main/docs/examples.md Use `make_schemabased_validator` to create a JSON validator for a specific schema version. The `validate_str` method can then be used to check the validity of a JSON SBOM string. If the SBOM is invalid, it returns validation errors. ```python import json import sys from typing import TYPE_CHECKING, Optional from cyclonedx.exception import MissingOptionalDependencyException from cyclonedx.schema import OutputFormat, SchemaVersion from cyclonedx.validation import make_schemabased_validator if TYPE_CHECKING: from cyclonedx.validation.json import JsonValidator from cyclonedx.validation.xml import XmlValidator JSON_SBOM = """ { "bomFormat": "CycloneDX", "specVersion": "1.5", "version": 1, "metadata": { "component": { "type": "application", "name": "my-app", "version": "1.0.0" } }, "components": [] } """ INVALID_JSON_SBOM = """ { "bomFormat": "CycloneDX", "specVersion": "1.5", "metadata": { "component": { "type": "invalid-type", "name": "my-app" } } } """ print('--- JSON Validation ---') # Create a JSON validator for a specific schema version json_validator: 'JsonValidator' = make_schemabased_validator(OutputFormat.JSON, SchemaVersion.V1_5) # 1. Validate valid SBOM try: validation_errors = json_validator.validate_str(JSON_SBOM) except MissingOptionalDependencyException as error: print('JSON validation was skipped:', error) else: if validation_errors: print('JSON SBOM is unexpectedly invalid!', file=sys.stderr) else: print('JSON SBOM is valid') # 2. Validate invalid SBOM and inspect details print('\nChecking invalid JSON SBOM...') try: validation_errors = json_validator.validate_str(INVALID_JSON_SBOM) except MissingOptionalDependencyException as error: print('JSON validation was skipped:', error) else: if validation_errors: print('Validation failed as expected.') print(f'Error Message: {validation_errors.data.message}') print(f'JSON Path: {validation_errors.data.json_path}') print(f'Invalid Data: {validation_errors.data.instance}') ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.