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

SPDX-2.2 validation #490

Merged
Merged
Show file tree
Hide file tree
Changes from 9 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: 16 additions & 3 deletions src/spdx/validation/checksum_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,33 @@
}


def validate_checksums(checksums: List[Checksum], parent_id: str) -> List[ValidationMessage]:
def validate_checksums(checksums: List[Checksum], parent_id: str, spdx_version: str) -> List[ValidationMessage]:
validation_messages = []
for checksum in checksums:
validation_messages.extend(validate_checksum(checksum, parent_id))
validation_messages.extend(validate_checksum(checksum, parent_id, spdx_version))

return validation_messages


def validate_checksum(checksum: Checksum, parent_id: str) -> List[ValidationMessage]:
def validate_checksum(checksum: Checksum, parent_id: str, spdx_version: str) -> List[ValidationMessage]:
validation_messages = []
algorithm = checksum.algorithm
context = ValidationContext(parent_id=parent_id, element_type=SpdxElementType.CHECKSUM,
full_element=checksum)

if spdx_version == "SPDX-2.2" and algorithm in [ChecksumAlgorithm.SHA3_512,
ChecksumAlgorithm.SHA3_384,
ChecksumAlgorithm.SHA3_256,
ChecksumAlgorithm.BLAKE3,
ChecksumAlgorithm.BLAKE2B_512,
ChecksumAlgorithm.BLAKE2B_384,
ChecksumAlgorithm.BLAKE2B_256,
ChecksumAlgorithm.ADLER32]:
validation_messages.append(
ValidationMessage(
f"{checksum.algorithm.name} is not supported in SPDX-2.2", context)
)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should return here as the algorithm is not valid.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

if not re.match("^[0-9a-f]{" + algorithm_length[algorithm] + "}$", checksum.value):
if algorithm == ChecksumAlgorithm.BLAKE3:
length = "at least 256"
Expand Down
4 changes: 2 additions & 2 deletions src/spdx/validation/creation_info_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType


def validate_creation_info(creation_info: CreationInfo) -> List[ValidationMessage]:
def validate_creation_info(creation_info: CreationInfo, spdx_version: str) -> List[ValidationMessage]:
validation_messages: List[ValidationMessage] = []

context = ValidationContext(spdx_id=creation_info.spdx_id, element_type=SpdxElementType.DOCUMENT)
Expand Down Expand Up @@ -48,6 +48,6 @@ def validate_creation_info(creation_info: CreationInfo) -> List[ValidationMessag

validation_messages.extend(validate_actors(creation_info.creators, creation_info.spdx_id))

validation_messages.extend(validate_external_document_refs(creation_info.external_document_refs, creation_info.spdx_id))
validation_messages.extend(validate_external_document_refs(creation_info.external_document_refs, creation_info.spdx_id, spdx_version))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Auto formatting inserts a line break here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


return validation_messages
14 changes: 7 additions & 7 deletions src/spdx/validation/document_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ def validate_full_spdx_document(document: Document, spdx_version: str = None) ->
if not spdx_version:
spdx_version = document_version

if not re.match(r"^SPDX-\d+.\d+$", document_version):
if document_version not in ["SPDX-2.2", "SPDX-2.3"]:
validation_messages.append(
ValidationMessage(
f'the document\'s spdx_version must be of the form "SPDX-[major].[minor]" but is: {document_version}',
f'only SPDX versions "SPDX-2.2" and "SPDX-2.3" are supported, but the document\'s spdx_version is: {document_version}',
context
)
)
Expand All @@ -53,12 +53,12 @@ def validate_full_spdx_document(document: Document, spdx_version: str = None) ->
"the validation process has been cancelled.", context))
return validation_messages

validation_messages.extend(validate_creation_info(document.creation_info))
validation_messages.extend(validate_packages(document.packages, document))
validation_messages.extend(validate_files(document.files, document))
validation_messages.extend(validate_snippets(document.snippets, document))
validation_messages.extend(validate_creation_info(document.creation_info, spdx_version))
validation_messages.extend(validate_packages(document.packages, spdx_version, document))
validation_messages.extend(validate_files(document.files, spdx_version, document))
validation_messages.extend(validate_snippets(document.snippets, spdx_version, document))
validation_messages.extend(validate_annotations(document.annotations, document))
validation_messages.extend(validate_relationships(document.relationships, document, spdx_version))
validation_messages.extend(validate_relationships(document.relationships, spdx_version, document))
validation_messages.extend(validate_extracted_licensing_infos(document.extracted_licensing_info))

document_id = document.creation_info.spdx_id
Expand Down
8 changes: 4 additions & 4 deletions src/spdx/validation/external_document_ref_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@
from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType


def validate_external_document_refs(external_document_refs: List[ExternalDocumentRef], parent_id: str) -> List[
def validate_external_document_refs(external_document_refs: List[ExternalDocumentRef], parent_id: str, spdx_version: str) -> List[
ValidationMessage]:
validation_messages = []
for external_document_ref in external_document_refs:
validation_messages.extend(validate_external_document_ref(external_document_ref, parent_id))
validation_messages.extend(validate_external_document_ref(external_document_ref, parent_id, spdx_version))

return validation_messages


def validate_external_document_ref(external_document_ref: ExternalDocumentRef, parent_id: str) -> List[ValidationMessage]:
def validate_external_document_ref(external_document_ref: ExternalDocumentRef, parent_id: str, spdx_version: str) -> List[ValidationMessage]:
validation_messages = []
context = ValidationContext(parent_id=parent_id, element_type=SpdxElementType.EXTERNAL_DOCUMENT_REF,
full_element=external_document_ref)
Expand All @@ -47,6 +47,6 @@ def validate_external_document_ref(external_document_ref: ExternalDocumentRef, p
)
)

validation_messages.extend(validate_checksum(external_document_ref.checksum, parent_id))
validation_messages.extend(validate_checksum(external_document_ref.checksum, parent_id, spdx_version))

return validation_messages
42 changes: 24 additions & 18 deletions src/spdx/validation/external_package_ref_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,17 @@
}


def validate_external_package_refs(external_package_refs: List[ExternalPackageRef], parent_id: str) -> List[
def validate_external_package_refs(external_package_refs: List[ExternalPackageRef], parent_id: str, spdx_version: str) -> List[
ValidationMessage]:
validation_messages = []
for external_package_ref in external_package_refs:
validation_messages.extend(validate_external_package_ref(external_package_ref, parent_id))
validation_messages.extend(validate_external_package_ref(external_package_ref, parent_id, spdx_version))

return validation_messages


def validate_external_package_ref(external_package_ref: ExternalPackageRef, parent_id: str) -> List[ValidationMessage]:
def validate_external_package_ref(external_package_ref: ExternalPackageRef, parent_id: str, spdx_version: str) -> List[ValidationMessage]:
validation_messages = []
context = ValidationContext(parent_id=parent_id, element_type=SpdxElementType.EXTERNAL_PACKAGE_REF,
full_element=external_package_ref)

Expand All @@ -59,31 +60,36 @@ def validate_external_package_ref(external_package_ref: ExternalPackageRef, pare

if category == ExternalPackageRefCategory.OTHER:
if " " in locator:
return [ValidationMessage(
validation_messages.append(ValidationMessage(
f"externalPackageRef locator in category OTHER must contain no spaces, but is: {locator}",
context)]
return []
context))

if reference_type not in CATEGORY_TO_EXTERNAL_PACKAGE_REF_TYPES[category]:
return [ValidationMessage(
elif reference_type not in CATEGORY_TO_EXTERNAL_PACKAGE_REF_TYPES[category]:
validation_messages.append(ValidationMessage(
f"externalPackageRef type in category {category.name} must be one of {CATEGORY_TO_EXTERNAL_PACKAGE_REF_TYPES[category]}, but is: {reference_type}",
context)]
context))

if reference_type in ["advisory", "fix", "url"]:
elif reference_type in ["advisory", "fix", "url"]:
if validate_url(locator):
return [ValidationMessage(
validation_messages.append(ValidationMessage(
f'externalPackageRef locator of type "{reference_type}" must be a valid URL, but is: {locator}',
context)]
return []
context))

if reference_type == "swid":
elif reference_type == "swid":
if not uritools.isuri(locator) or not locator.startswith("swid"):
return [ValidationMessage(
validation_messages.append(ValidationMessage(
f'externalPackageRef locator of type "swid" must be a valid URI with scheme swid, but is: {locator}',
context)]
return []
context))

return validate_against_regex(locator, reference_type, context)
else:
validation_messages.extend(validate_against_regex(locator, reference_type, context))

if spdx_version == "SPDX-2.2" and reference_type in ["advisory", "fix", "url", "swid"]:
validation_messages.append(
ValidationMessage(f'externalPackageRef type "{reference_type}" is not supported in SPDX-2.2', context)
)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would move this validation to the beginning and return. I don't think that it makes sense to first validate references on a lower level and afterwards check if they are valid in general.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

return validation_messages


def validate_against_regex(string_to_validate: str, reference_type: str, context: ValidationContext) -> List[
Expand Down
25 changes: 18 additions & 7 deletions src/spdx/validation/file_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,32 +20,32 @@
from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType


def validate_files(files: List[File], document: Optional[Document] = None) -> List[ValidationMessage]:
def validate_files(files: List[File], spdx_version: str, document: Optional[Document] = None) -> List[ValidationMessage]:
validation_messages = []
if document:
for file in files:
validation_messages.extend(validate_file_within_document(file, document))
validation_messages.extend(validate_file_within_document(file, spdx_version, document))
else:
for file in files:
validation_messages.extend(validate_file(file))
validation_messages.extend(validate_file(file, spdx_version))

return validation_messages


def validate_file_within_document(file: File, document: Document) -> List[ValidationMessage]:
def validate_file_within_document(file: File, spdx_version: str, document: Document) -> List[ValidationMessage]:
validation_messages: List[ValidationMessage] = []
context = ValidationContext(spdx_id=file.spdx_id, parent_id=document.creation_info.spdx_id,
element_type=SpdxElementType.FILE, full_element=file)

for message in validate_spdx_id(file.spdx_id, document):
validation_messages.append(ValidationMessage(message, context))

validation_messages.extend(validate_file(file, context))
validation_messages.extend(validate_file(file, spdx_version, context))

return validation_messages


def validate_file(file: File, context: Optional[ValidationContext] = None) -> List[ValidationMessage]:
def validate_file(file: File, spdx_version: str, context: Optional[ValidationContext] = None) -> List[ValidationMessage]:
validation_messages = []
if not context:
context = ValidationContext(spdx_id=file.spdx_id, element_type=SpdxElementType.FILE, full_element=file)
Expand All @@ -63,10 +63,21 @@ def validate_file(file: File, context: Optional[ValidationContext] = None) -> Li
context)
)

validation_messages.extend(validate_checksums(file.checksums, file.spdx_id))
validation_messages.extend(validate_checksums(file.checksums, file.spdx_id, spdx_version))

validation_messages.extend(validate_license_expression(file.license_concluded))

validation_messages.extend(validate_license_expressions(file.license_info_in_file))

if spdx_version == "SPDX-2.2":
if file.license_concluded is None:
validation_messages.append(
ValidationMessage(f"license_concluded is mandatory in SPDX-2.2", context))
if not file.license_info_in_file:
validation_messages.append(
ValidationMessage(f"license_info_in_file is mandatory in SPDX-2.2", context))
if file.copyright_text is None:
validation_messages.append(
ValidationMessage(f"copyright_text is mandatory in SPDX-2.2", context))

return validation_messages
42 changes: 34 additions & 8 deletions src/spdx/validation/package_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,20 @@
from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType


def validate_packages(packages: List[Package], document: Optional[Document] = None) -> List[ValidationMessage]:
def validate_packages(packages: List[Package], spdx_version: str, document: Optional[Document] = None) -> List[
ValidationMessage]:
validation_messages: List[ValidationMessage] = []
if document:
for package in packages:
validation_messages.extend(validate_package_within_document(package, document))
validation_messages.extend(validate_package_within_document(package, spdx_version, document))
else:
for package in packages:
validation_messages.extend(validate_package(package))
validation_messages.extend(validate_package(package, spdx_version))

return validation_messages


def validate_package_within_document(package: Package, document: Document) -> List[ValidationMessage]:
def validate_package_within_document(package: Package, spdx_version: str, document: Document) -> List[ValidationMessage]:
validation_messages: List[ValidationMessage] = []
context = ValidationContext(spdx_id=package.spdx_id, parent_id=document.creation_info.spdx_id,
element_type=SpdxElementType.PACKAGE, full_element=package)
Expand All @@ -59,12 +60,13 @@ def validate_package_within_document(package: Package, document: Document) -> Li
context)
)

validation_messages.extend(validate_package(package, context))
validation_messages.extend(validate_package(package, spdx_version, context))

return validation_messages


def validate_package(package: Package, context: Optional[ValidationContext] = None) -> List[ValidationMessage]:
def validate_package(package: Package, spdx_version: str, context: Optional[ValidationContext] = None) -> List[
ValidationMessage]:
validation_messages: List[ValidationMessage] = []
if not context:
context = ValidationContext(spdx_id=package.spdx_id, element_type=SpdxElementType.PACKAGE, full_element=package)
Expand All @@ -89,7 +91,7 @@ def validate_package(package: Package, context: Optional[ValidationContext] = No
else:
validation_messages.extend(validate_verification_code(verification_code, package.spdx_id))

validation_messages.extend(validate_checksums(package.checksums, package.spdx_id))
validation_messages.extend(validate_checksums(package.checksums, package.spdx_id, spdx_version))

validation_messages.extend(validate_license_expression(package.license_concluded))

Expand All @@ -106,6 +108,30 @@ def validate_package(package: Package, context: Optional[ValidationContext] = No

validation_messages.extend(validate_license_expression(package.license_declared))

validation_messages.extend(validate_external_package_refs(package.external_references, package.spdx_id))
validation_messages.extend(validate_external_package_refs(package.external_references, package.spdx_id, spdx_version))

if spdx_version == "SPDX-2.2":
if package.primary_package_purpose is not None:
validation_messages.append(
ValidationMessage(f"primary_package_purpose is not supported in SPDX-2.2", context))
if package.built_date is not None:
validation_messages.append(
ValidationMessage(f"built_date is not supported in SPDX-2.2", context))
if package.release_date is not None:
validation_messages.append(
ValidationMessage(f"release_date is not supported in SPDX-2.2", context))
if package.valid_until_date is not None:
validation_messages.append(
ValidationMessage(f"valid_until_date is not supported in SPDX-2.2", context))

if package.license_concluded is None:
validation_messages.append(
ValidationMessage(f"license_concluded is mandatory in SPDX-2.2", context))
if package.license_declared is None:
validation_messages.append(
ValidationMessage(f"license_declared is mandatory in SPDX-2.2", context))
if package.copyright_text is None:
validation_messages.append(
ValidationMessage(f"copyright_text is mandatory in SPDX-2.2", context))

return validation_messages
10 changes: 5 additions & 5 deletions src/spdx/validation/relationship_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@
from spdx.validation.validation_message import ValidationMessage, ValidationContext, SpdxElementType


def validate_relationships(relationships: List[Relationship], document: Document, spdx_version: str) -> List[ValidationMessage]:
def validate_relationships(relationships: List[Relationship], spdx_version: str, document: Document) -> List[ValidationMessage]:
validation_messages = []
for relationship in relationships:
validation_messages.extend(validate_relationship(relationship, document, spdx_version))
validation_messages.extend(validate_relationship(relationship, spdx_version, document))

return validation_messages


def validate_relationship(relationship: Relationship, document: Document, spdx_version: str) -> List[ValidationMessage]:
def validate_relationship(relationship: Relationship, spdx_version: str, document: Document) -> List[ValidationMessage]:
validation_messages = []
context = ValidationContext(element_type=SpdxElementType.RELATIONSHIP,
full_element=relationship)
Expand All @@ -43,9 +43,9 @@ def validate_relationship(relationship: Relationship, document: Document, spdx_v
for message in messages:
validation_messages.append(ValidationMessage(message, context))

if spdx_version != "SPDX-2.3":
if spdx_version == "SPDX-2.2":
if relationship_type == RelationshipType.SPECIFICATION_FOR or relationship_type == RelationshipType.REQUIREMENT_DESCRIPTION_FOR:
validation_messages.append(
ValidationMessage(f"{relationship_type} is not supported for SPDX versions below SPDX-2.3", context))
ValidationMessage(f"{relationship_type} is not supported in SPDX-2.2", context))

return validation_messages
Loading