Skip to content

Support for CycloneDX schema version 1.4 #108

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

Merged
merged 59 commits into from
Jan 13, 2022
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
aabb3a1
added draft 1.4 schema documents as of 10-Nov-2021
madpah Nov 10, 2021
a1c3401
skeleton updates to define Schema Version 1.4
madpah Nov 10, 2021
27d1c4f
update to latest draft 1.4 XML XSD
madpah Dec 21, 2021
6d28d98
Merge branch 'main' into feat/67-schema-version-1.4-xml
madpah Dec 21, 2021
c06b273
Merge branch 'main' into feat/67-schema-version-1.4-xml
madpah Dec 21, 2021
2a94a1e
- Added XML Schema validation for XML tests
madpah Dec 21, 2021
09a14a0
- Made Component `version` optional for schema version 1.4
madpah Dec 21, 2021
7e6db9a
- New model objects for `ReleaseNotes` and associated sub-types
madpah Dec 22, 2021
49a6738
added `lxml` as dev dependency
madpah Dec 22, 2021
f0403b7
- Added XML schema validation to all tests in `test_output_xml.py` wi…
madpah Dec 22, 2021
4be4004
added jsonschema validation to unit tests
madpah Dec 23, 2021
4a05fe0
fixed typos
madpah Dec 23, 2021
05e0dde
tests and completing new models for IssueType and ReleaseNote
madpah Dec 23, 2021
9f6ada8
support for `bom.metadata.tools.tool.externalReferences`
madpah Dec 23, 2021
827b01e
altered model to allow `version==""` as the agreed fallback
madpah Dec 23, 2021
5350272
added support for `bom.component.releaseNotes` in Schema Version >= 1…
madpah Dec 23, 2021
d7f3b19
feat: re-worked for vulnerabilities becoming part of core schema (#109)
madpah Jan 5, 2022
d11c687
test: confirmed `bom.metadata.tools[].externalReferences` is populate…
madpah Jan 5, 2022
8862b51
test: added test to prove `Component.version` is now optional for 1.4…
madpah Jan 5, 2022
8a35309
updated to ensure support for `bom.component.releaseNotes` in JSON 1.4
madpah Jan 5, 2022
7281a91
fixed a bunch of typing / flake8 issues
madpah Jan 5, 2022
505fa81
JSON now supporting `bom.vulnerabilities`
madpah Jan 6, 2022
1ef36a9
feat: move documentation to readthedocs.org (#120)
madpah Jan 6, 2022
b7f7a2d
feat: added $schema to JSON BOMs (#121)
madpah Jan 6, 2022
7e6771f
doc: updates
madpah Jan 6, 2022
802a101
doc: updates
madpah Jan 6, 2022
518eca4
chore: attempt to produce manual GitHub action to release a RC version
madpah Jan 7, 2022
b664b6e
chore: attempt to produce manual GitHub action to release a RC version
madpah Jan 7, 2022
938f346
chore: attempt to produce manual GitHub action to release a RC version
madpah Jan 7, 2022
b703ce9
chore: attempt to produce manual GitHub action to release a RC version
madpah Jan 7, 2022
fe1470f
chore: attempt to produce manual GitHub action to release a RC version
madpah Jan 7, 2022
0a18734
chore: attempt to produce manual GitHub action to release a RC version
madpah Jan 7, 2022
d1aa272
chore: add publish to PyPi for manual pre-release pacakge
madpah Jan 7, 2022
c1b0065
chore: add build prior to publish to PyPi for manual pre-release pacakge
madpah Jan 7, 2022
f8e02b8
Merge branch 'main' into feat/schema-version-1.4
madpah Jan 7, 2022
a182d7f
fix: using index when potentially not available
madpah Jan 7, 2022
e701478
chore: missing license header
madpah Jan 7, 2022
1ff3ea9
Merge branch 'feat/schema-version-1.4' of github.com:CycloneDX/cyclon…
madpah Jan 7, 2022
eae6331
addressed some PR comments from @jkowalleck
madpah Jan 7, 2022
ec798d5
addressed PR comments from @sonatype-lift
madpah Jan 7, 2022
c7fb3cd
addressed PR comments from @sonatype-lift
madpah Jan 7, 2022
aef955b
addressed PR comments from @sonatype-lift
madpah Jan 7, 2022
4f5ccaf
addressed PR comments from @sonatype-lift
madpah Jan 7, 2022
b8d7615
doc: cleaned up README in favour of RTD.
madpah Jan 7, 2022
e21a079
doc: tweaked link to RTD
madpah Jan 7, 2022
f3ad678
removed capturing groups from regex as not required
madpah Jan 9, 2022
71c36d4
remove notion that purl will be used for Component bom-ref if bom-ref…
madpah Jan 9, 2022
3cb7745
feat: Relocate Parsers to `cyclonedx-python` (#132)
madpah Jan 10, 2022
cd05f79
doc: added changelog
madpah Jan 10, 2022
2ed766c
doc: moved Python-specific parser docs to `cyclonedx-python`
madpah Jan 10, 2022
285c872
doc: removed python-specific parser docs
madpah Jan 10, 2022
0dac5bf
chore: relocated issue-related models to separate file
madpah Jan 10, 2022
1c04448
doc: removed parser reference
madpah Jan 10, 2022
f95fd3f
updated Component.__repr__
madpah Jan 10, 2022
d0bff8c
fix: addressed how we compare Component objects after feedback;
madpah Jan 10, 2022
9be349c
chore: grabbed latest FINAL 1.4 schema changes
madpah Jan 10, 2022
5990436
cleanup
madpah Jan 10, 2022
eca9cc9
addressed `Component.purl` / PackageURL feedback from @jkowalleck
madpah Jan 10, 2022
f01be8a
doc: describe anaconda release process (#111)
jkowalleck Jan 12, 2022
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
46 changes: 46 additions & 0 deletions cyclonedx/exception/model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# encoding: utf-8

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

"""
Exceptions relating to specific conditions that occur when modelling CycloneDX BOM.
"""

from . import CycloneDxException


class InvalidLocaleTypeException(CycloneDxException):
"""
Raised when the supplied locale does not conform to ISD-639 specification.

Good examples:
- en
- en-US
- en-GB
- fr
- fr-CA

The language code MUST be lower case. If the country code is specified, the country code MUST be upper case.
The language code and country code MUST be separated by a minus sign.
"""
pass


class InvalidUriException(CycloneDxException):
"""
Raised when a `str` is provided that needs to be a valid URI, but isn't.
"""
pass
30 changes: 30 additions & 0 deletions cyclonedx/exception/output.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# encoding: utf-8

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

"""
Exceptions that are for specific error scenarios during the output of a Model to a SBOM.
"""

from . import CycloneDxException


class ComponentVersionRequiredException(CycloneDxException):
"""
Exception raised when attempting to output to an SBOM version that mandates a Component has a version,
but one is not available/present.
"""
pass
171 changes: 170 additions & 1 deletion cyclonedx/model/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@
#

import hashlib
import re
from enum import Enum
from typing import List, Union
from typing import List, Optional, Union

from ..exception.model import InvalidLocaleTypeException, InvalidUriException
from ..exception.parser import UnknownHashTypeException

"""
Expand All @@ -28,6 +30,8 @@
from a `cyclonedx.parser.BaseParser` implementation.
"""

LOCALE_TYPE_REGEX = re.compile(r'^([a-z]{2})(-[A-Z]{2})?$')


def sha1sum(filename: str) -> str:
"""
Expand All @@ -47,6 +51,16 @@ def sha1sum(filename: str) -> str:
return h.hexdigest()


class Encoding(Enum):
"""
This is out internal representation of the encoding simple type within the CycloneDX standard.

.. note::
See the CycloneDX Schema: https://cyclonedx.org/docs/1.4/#type_encoding
"""
BASE_64 = 'base64'


class HashAlgorithm(Enum):
"""
This is out internal representation of the hashAlg simple type within the CycloneDX standard.
Expand Down Expand Up @@ -154,6 +168,28 @@ class ExternalReferenceType(Enum):
WEBSITE = 'website'


class XsUri:
"""
Helper class that allows us to perform validation on data strings that are defined as xs:anyURI
in CycloneDX schema.

.. note::
See XSD definition for xsd:anyURI: http://www.datypic.com/sc/xsd/t-xsd_anyURI.html
"""

invalid_uri_regex = re.compile("(%(?![0-9A-F]{2})|#.*#)", re.IGNORECASE + re.MULTILINE)

def __init__(self, uri: str) -> None:
if re.search(XsUri.invalid_uri_regex, uri):
raise InvalidUriException(
f"Supplied value '{uri}' does not appear to be a valid URI."
)
self._uri = uri

def __repr__(self) -> str:
return self._uri


class ExternalReference:
"""
This is out internal representation of an ExternalReference complex type that can be used in multiple places within
Expand Down Expand Up @@ -218,3 +254,136 @@ def get_url(self) -> str:

def __repr__(self) -> str:
return f'<ExternalReference {self._reference_type.name}, {self._url}> {self._hashes}'


class IssueClassification(Enum):
"""
This is out internal representation of the enum `issueClassification`.

.. note::
See the CycloneDX Schema definition: hhttps://cyclonedx.org/docs/1.4/xml/#type_issueClassification
"""
DEFECT = 'defect'
ENHANCEMENT = 'enhancement'
SECURITY = 'security'


class IssueType:
"""
This is out internal representation of an IssueType complex type that can be used in multiple places within
a CycloneDX BOM document.

.. note::
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.4/xml/#type_issueType
"""

def __init__(self, classification: IssueClassification, id: Optional[str] = None, name: Optional[str] = None,
description: Optional[str] = None, source_name: Optional[str] = None,
source_url: Optional[XsUri] = None, references: Optional[List[XsUri]] = None) -> None:
self._classification: IssueClassification = classification
self._id: Optional[str] = id
self._name: Optional[str] = name
self._description: Optional[str] = description
self._source_name: Optional[str] = source_name
self._source_url: Optional[XsUri] = source_url
self._references: Optional[List[XsUri]] = references


class Property:
"""
This is out internal representation of `propertyType` complex type that can be used in multiple places within
a CycloneDX BOM document.

.. note::
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.4/xml/#type_propertyType

Specifies an individual property with a name and value.
"""

def __init__(self, name: str, value: str) -> None:
self._name = name
self._value = value

def get_name(self) -> str:
"""
Get the name of this Property.

Returns:
Name of this Property as `str`.
"""
return self._name

def get_value(self) -> str:
"""
Get the value of this Property.

Returns:
Value of this Property as `str`.
"""
return self._value


class Properties:
"""
This is out internal representation of `propertiesType` complex type that can be used in multiple places within
a CycloneDX BOM document.

.. note::
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.4/xml/#type_propertiesType

Provides the ability to document properties in a key/value store. This provides flexibility to include data not
officially supported in the standard without having to use additional namespaces or create extensions.
"""

def __init__(self, properties: Optional[List[Property]] = None) -> None:
if properties:
self._properties = properties
else:
self._properties = []

def add_property(self, prop: Property) -> None:
"""
Add a Property to this list of Properties.

Args:
prop:
`Property` to add

Returns:
None
"""
self._properties.append(prop)

def get_properties(self) -> List[Property]:
"""
Get all Property instances in this List.

Returns:
List of `Property` objects, or an empty List.
"""
return self._properties


class Note:
"""
This is out internal representation of the Note complex type that can be used in multiple places within
a CycloneDX BOM document.

.. note::
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.4/xml/#type_releaseNotesType
"""

def __init__(self, text: str, locale: Optional[str] = None, content_type: Optional[str] = None,
content_encoding: Optional[Encoding] = None) -> None:
self._text: str = text
self._locale: Optional[str] = None
self._content_type: Optional[str] = content_type
self._content_encoding: Optional[Encoding] = content_encoding
if locale:
if re.search(LOCALE_TYPE_REGEX, locale):
# Valid locale
self._locale = locale
else:
raise InvalidLocaleTypeException(
f"Supplied locale '{locale}' is not a valid locale according to ISO-639 format."
)
11 changes: 7 additions & 4 deletions cyclonedx/model/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,15 @@ def for_file(absolute_file_path: str, path_for_bom: Optional[str]) -> 'Component
package_url_type='generic'
)

def __init__(self, name: str, version: str, namespace: Optional[str] = None, qualifiers: Optional[str] = None,
subpath: Optional[str] = None, hashes: Optional[List[HashType]] = None, author: Optional[str] = None,
def __init__(self, name: str, version: Optional[str] = None, namespace: Optional[str] = None,
qualifiers: Optional[str] = None, subpath: Optional[str] = None,
hashes: Optional[List[HashType]] = None, author: Optional[str] = None,
description: Optional[str] = None, license_str: Optional[str] = None,
component_type: ComponentType = ComponentType.LIBRARY, package_url_type: str = 'pypi') -> None:
self._package_url_type: str = package_url_type
self._namespace: Optional[str] = namespace
self._name: str = name
self._version: str = version
self._version: Optional[str] = version
self._type: ComponentType = component_type
self._qualifiers: Optional[str] = qualifiers
self._subpath: Optional[str] = subpath
Expand Down Expand Up @@ -227,10 +228,12 @@ def get_type(self) -> ComponentType:
"""
return self._type

def get_version(self) -> str:
def get_version(self) -> Optional[str]:
"""
Get the version of this Component.

This is NOT optional for CycloneDX Schema Version <= 1.3.

Returns:
Declared version of this Component as `str`.
"""
Expand Down
48 changes: 48 additions & 0 deletions cyclonedx/model/release_notes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# encoding: utf-8

# This file is part of CycloneDX Python Lib
#
# 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 datetime
from typing import List, Optional

from ..model import IssueType, Note, Properties, XsUri


class ReleaseNotes:
"""
This is our internal representation of a `releaseNotesType` for a Component in a BOM.

.. note::
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.4/#type_releaseNotesType
"""

def __init__(self, type: str, title: Optional[str] = None, featured_image: Optional[XsUri] = None,
social_image: Optional[XsUri] = None, description: Optional[str] = None,
timestamp: Optional[datetime.datetime] = None, aliases: Optional[List[str]] = None,
tags: Optional[List[str]] = None, resolves: Optional[List[IssueType]] = None,
notes: Optional[List[Note]] = None, properties: Optional[Properties] = None) -> None:
self._type: str = type
self._title: Optional[str] = title
self._featured_image: Optional[XsUri] = featured_image
self._social_image: Optional[XsUri] = social_image
self._description: Optional[str] = description
self._timestamp: Optional[datetime.datetime] = timestamp
self._aliases: Optional[List[str]] = aliases
self._tags: Optional[List[str]] = tags
self._resolves: Optional[List[IssueType]] = resolves
self._notes: Optional[List[Note]] = notes
self._properties: Optional[Properties] = properties
1 change: 1 addition & 0 deletions cyclonedx/output/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class SchemaVersion(Enum):
V1_1: str = 'V1Dot1'
V1_2: str = 'V1Dot2'
V1_3: str = 'V1Dot3'
V1_4: str = 'V1Dot4'


DEFAULT_SCHEMA_VERSION = SchemaVersion.V1_3
Expand Down
Loading