From cc6d8c5987057734bfa2cf39ac26bb3a0c1d7421 Mon Sep 17 00:00:00 2001 From: SHIVANSH-ux-ys Date: Thu, 29 Oct 2026 11:56:52 +0530 Subject: [PATCH] Validate package supplier and originator actor types (#848) SPDX 2.2 and 2.3 clauses 7.5 and 7.6 disallow TOOL as an actor type for package supplier and originator. This adds validation ensuring that neither supplier nor originator is of type TOOL and delegates actor validation to validate_actor. Signed-off-by: SHIVANSH-ux-ys --- .../spdx/validation/package_validator.py | 25 ++++++++++- .../spdx/validation/test_package_validator.py | 41 ++++++++++++++++++- 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/spdx_tools/spdx/validation/package_validator.py b/src/spdx_tools/spdx/validation/package_validator.py index 25cd6147f..37c74a394 100644 --- a/src/spdx_tools/spdx/validation/package_validator.py +++ b/src/spdx_tools/spdx/validation/package_validator.py @@ -4,9 +4,10 @@ from beartype.typing import List, Optional -from spdx_tools.spdx.model import Document, File, Package, Relationship, RelationshipType +from spdx_tools.spdx.model import Actor, ActorType, Document, File, Package, Relationship, RelationshipType from spdx_tools.spdx.model.relationship_filters import filter_by_type_and_origin, filter_by_type_and_target from spdx_tools.spdx.spdx_element_utils import get_element_type_from_spdx_id +from spdx_tools.spdx.validation.actor_validator import validate_actor from spdx_tools.spdx.validation.checksum_validator import validate_checksums from spdx_tools.spdx.validation.external_package_ref_validator import validate_external_package_refs from spdx_tools.spdx.validation.license_expression_validator import ( @@ -121,6 +122,28 @@ def validate_package( for message in validate_url(homepage): validation_messages.append(ValidationMessage("homepage " + message, context)) + if isinstance(package.supplier, Actor): + if package.supplier.actor_type == ActorType.TOOL: + validation_messages.append( + ValidationMessage( + f"package supplier must not be of actor type TOOL, but is: {package.supplier}", + context, + ) + ) + else: + validation_messages.extend(validate_actor(package.supplier, package.spdx_id)) + + if isinstance(package.originator, Actor): + if package.originator.actor_type == ActorType.TOOL: + validation_messages.append( + ValidationMessage( + f"package originator must not be of actor type TOOL, but is: {package.originator}", + context, + ) + ) + else: + validation_messages.extend(validate_actor(package.originator, package.spdx_id)) + verification_code = package.verification_code if verification_code: if not package.files_analyzed: diff --git a/tests/spdx/validation/test_package_validator.py b/tests/spdx/validation/test_package_validator.py index d0516fff2..120c350ff 100644 --- a/tests/spdx/validation/test_package_validator.py +++ b/tests/spdx/validation/test_package_validator.py @@ -9,7 +9,7 @@ from spdx_tools.common.spdx_licensing import spdx_licensing from spdx_tools.spdx.constants import DOCUMENT_SPDX_ID -from spdx_tools.spdx.model import Relationship, RelationshipType, SpdxNoAssertion, SpdxNone +from spdx_tools.spdx.model import Actor, ActorType, Relationship, RelationshipType, SpdxNoAssertion, SpdxNone from spdx_tools.spdx.validation.package_validator import validate_package, validate_package_within_document from spdx_tools.spdx.validation.validation_message import SpdxElementType, ValidationContext, ValidationMessage from tests.spdx.fixtures import document_fixture, file_fixture, package_fixture, package_verification_code_fixture @@ -154,3 +154,42 @@ def test_v2_2mandatory_fields(): expected = [ValidationMessage(f"{field} is mandatory in SPDX-2.2", context) for field in mandatory_fields] TestCase().assertCountEqual(validation_messages, expected) + + +@pytest.mark.parametrize( + "actor_field, actor_type_name", + [ + ("supplier", "supplier"), + ("originator", "originator"), + ], +) +def test_invalid_package_actor_tool(actor_field, actor_type_name): + tool_actor = Actor(actor_type=ActorType.TOOL, name="ToolName") + package = package_fixture(**{actor_field: tool_actor}) + validation_messages = validate_package(package, "SPDX-2.3") + + context = ValidationContext(spdx_id=package.spdx_id, element_type=SpdxElementType.PACKAGE, full_element=package) + expected = [ + ValidationMessage( + f"package {actor_type_name} must not be of actor type TOOL, but is: Tool: ToolName", + context, + ) + ] + + assert validation_messages == expected + + +@pytest.mark.parametrize( + "actor", + [ + Actor(actor_type=ActorType.PERSON, name="Jane Doe", email="jane@example.com"), + Actor(actor_type=ActorType.ORGANIZATION, name="Acme Corp"), + SpdxNoAssertion(), + None, + ], +) +def test_valid_package_supplier_and_originator(actor): + package = package_fixture(supplier=actor, originator=actor) + validation_messages = validate_package(package, "SPDX-2.3") + + assert validation_messages == []