spdx_tools.spdx.validation.document_validator

 1# SPDX-FileCopyrightText: 2022 spdx contributors
 2#
 3# SPDX-License-Identifier: Apache-2.0
 4from beartype.typing import List
 5
 6from spdx_tools.spdx.model import Document, RelationshipType
 7from spdx_tools.spdx.model.relationship_filters import filter_by_type_and_origin, filter_by_type_and_target
 8from spdx_tools.spdx.validation.annotation_validator import validate_annotations
 9from spdx_tools.spdx.validation.creation_info_validator import validate_creation_info
10from spdx_tools.spdx.validation.extracted_licensing_info_validator import validate_extracted_licensing_infos
11from spdx_tools.spdx.validation.file_validator import validate_files
12from spdx_tools.spdx.validation.package_validator import validate_packages
13from spdx_tools.spdx.validation.relationship_validator import validate_relationships
14from spdx_tools.spdx.validation.snippet_validator import validate_snippets
15from spdx_tools.spdx.validation.spdx_id_validators import get_list_of_all_spdx_ids
16from spdx_tools.spdx.validation.validation_message import SpdxElementType, ValidationContext, ValidationMessage
17
18
19def validate_full_spdx_document(document: Document, spdx_version: str = None) -> List[ValidationMessage]:
20    validation_messages: List[ValidationMessage] = []
21
22    # SPDX version validation has to happen here because subsequent validators rely on it
23    document_version: str = document.creation_info.spdx_version
24    context = ValidationContext(spdx_id=document.creation_info.spdx_id, element_type=SpdxElementType.DOCUMENT)
25    if not spdx_version:
26        spdx_version = document_version
27
28    if document_version not in ["SPDX-2.2", "SPDX-2.3"]:
29        validation_messages.append(
30            ValidationMessage(
31                f'only SPDX versions "SPDX-2.2" and "SPDX-2.3" are supported, but the document\'s spdx_version is: '
32                f"{document_version}",
33                context,
34            )
35        )
36    elif spdx_version != document_version:
37        validation_messages.append(
38            ValidationMessage(
39                f"provided SPDX version {spdx_version} does not match "
40                f"the document's SPDX version {document_version}",
41                context,
42            )
43        )
44
45    if validation_messages:
46        validation_messages.append(
47            ValidationMessage(
48                "There are issues concerning the SPDX version of the document. "
49                "As subsequent validation relies on the correct version, "
50                "the validation process has been cancelled.",
51                context,
52            )
53        )
54        return validation_messages
55
56    validation_messages.extend(validate_creation_info(document.creation_info, spdx_version))
57    validation_messages.extend(validate_packages(document.packages, spdx_version, document))
58    validation_messages.extend(validate_files(document.files, spdx_version, document))
59    validation_messages.extend(validate_snippets(document.snippets, spdx_version, document))
60    validation_messages.extend(validate_annotations(document.annotations, document))
61    validation_messages.extend(validate_relationships(document.relationships, spdx_version, document))
62    validation_messages.extend(validate_extracted_licensing_infos(document.extracted_licensing_info))
63
64    document_id = document.creation_info.spdx_id
65    document_describes_relationships = filter_by_type_and_origin(
66        document.relationships, RelationshipType.DESCRIBES, document_id
67    )
68    described_by_document_relationships = filter_by_type_and_target(
69        document.relationships, RelationshipType.DESCRIBED_BY, document_id
70    )
71
72    only_a_single_package = len(document.packages) == 1 and not document.files and not document.snippets
73    if not only_a_single_package and not document_describes_relationships + described_by_document_relationships:
74        validation_messages.append(
75            ValidationMessage(
76                f'there must be at least one relationship "{document_id} DESCRIBES ..." or "... DESCRIBED_BY '
77                f'{document_id}" when there is not only a single package present',
78                ValidationContext(spdx_id=document_id, element_type=SpdxElementType.DOCUMENT),
79            )
80        )
81
82    all_spdx_ids: List[str] = get_list_of_all_spdx_ids(document)
83    auxiliary_set = set()
84    duplicated_spdx_ids = set(
85        spdx_id for spdx_id in all_spdx_ids if spdx_id in auxiliary_set or auxiliary_set.add(spdx_id)
86    )
87
88    if duplicated_spdx_ids:
89        validation_messages.append(
90            ValidationMessage(
91                f"every spdx_id must be unique within the document, but found the following duplicates: "
92                f"{sorted(duplicated_spdx_ids)}",
93                context,
94            )
95        )
96
97    return validation_messages
def validate_full_spdx_document( document: spdx_tools.spdx.model.document.Document, spdx_version: str = None) -> list[spdx_tools.spdx.validation.validation_message.ValidationMessage]:
20def validate_full_spdx_document(document: Document, spdx_version: str = None) -> List[ValidationMessage]:
21    validation_messages: List[ValidationMessage] = []
22
23    # SPDX version validation has to happen here because subsequent validators rely on it
24    document_version: str = document.creation_info.spdx_version
25    context = ValidationContext(spdx_id=document.creation_info.spdx_id, element_type=SpdxElementType.DOCUMENT)
26    if not spdx_version:
27        spdx_version = document_version
28
29    if document_version not in ["SPDX-2.2", "SPDX-2.3"]:
30        validation_messages.append(
31            ValidationMessage(
32                f'only SPDX versions "SPDX-2.2" and "SPDX-2.3" are supported, but the document\'s spdx_version is: '
33                f"{document_version}",
34                context,
35            )
36        )
37    elif spdx_version != document_version:
38        validation_messages.append(
39            ValidationMessage(
40                f"provided SPDX version {spdx_version} does not match "
41                f"the document's SPDX version {document_version}",
42                context,
43            )
44        )
45
46    if validation_messages:
47        validation_messages.append(
48            ValidationMessage(
49                "There are issues concerning the SPDX version of the document. "
50                "As subsequent validation relies on the correct version, "
51                "the validation process has been cancelled.",
52                context,
53            )
54        )
55        return validation_messages
56
57    validation_messages.extend(validate_creation_info(document.creation_info, spdx_version))
58    validation_messages.extend(validate_packages(document.packages, spdx_version, document))
59    validation_messages.extend(validate_files(document.files, spdx_version, document))
60    validation_messages.extend(validate_snippets(document.snippets, spdx_version, document))
61    validation_messages.extend(validate_annotations(document.annotations, document))
62    validation_messages.extend(validate_relationships(document.relationships, spdx_version, document))
63    validation_messages.extend(validate_extracted_licensing_infos(document.extracted_licensing_info))
64
65    document_id = document.creation_info.spdx_id
66    document_describes_relationships = filter_by_type_and_origin(
67        document.relationships, RelationshipType.DESCRIBES, document_id
68    )
69    described_by_document_relationships = filter_by_type_and_target(
70        document.relationships, RelationshipType.DESCRIBED_BY, document_id
71    )
72
73    only_a_single_package = len(document.packages) == 1 and not document.files and not document.snippets
74    if not only_a_single_package and not document_describes_relationships + described_by_document_relationships:
75        validation_messages.append(
76            ValidationMessage(
77                f'there must be at least one relationship "{document_id} DESCRIBES ..." or "... DESCRIBED_BY '
78                f'{document_id}" when there is not only a single package present',
79                ValidationContext(spdx_id=document_id, element_type=SpdxElementType.DOCUMENT),
80            )
81        )
82
83    all_spdx_ids: List[str] = get_list_of_all_spdx_ids(document)
84    auxiliary_set = set()
85    duplicated_spdx_ids = set(
86        spdx_id for spdx_id in all_spdx_ids if spdx_id in auxiliary_set or auxiliary_set.add(spdx_id)
87    )
88
89    if duplicated_spdx_ids:
90        validation_messages.append(
91            ValidationMessage(
92                f"every spdx_id must be unique within the document, but found the following duplicates: "
93                f"{sorted(duplicated_spdx_ids)}",
94                context,
95            )
96        )
97
98    return validation_messages