### 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.