From aa46c16aa9bfebe1c5e101b14e33cac7eb6b7d1f Mon Sep 17 00:00:00 2001 From: saquibsaifee Date: Mon, 31 Aug 2026 13:04:33 -0400 Subject: [PATCH] feat: implement SPDX schema validation using spdx-tools Implements _validate_spdx_schema_version inside CLIController with proper SPDX validation logic via the official spdx-tools package. - Modifies src/controllers/cli_controller.py to import spdx-tools as a hard dependency. - Uses JsonLikeDictParser and validate_full_spdx_document to validate incoming SPDX dicts. - Supports SPDX 2.2 and 2.3 schema versions. - Adds comprehensive unit tests for both success and failure cases. - Updates pyproject.toml with the spdx-tools dependency. Signed-off-by: saquibsaifee --- pyproject.toml | 1 + src/controllers/cli_controller.py | 29 ++++++++++++++++++-- tests/test_cli_controller.py | 45 +++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index e9fba77..ad9e2a1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,6 +36,7 @@ dependencies = [ "transformers>=4.36.0", "uvicorn>=0.24.0", "safetensors>=0.4.0", + "spdx-tools>=0.8.0", ] [project.optional-dependencies] diff --git a/src/controllers/cli_controller.py b/src/controllers/cli_controller.py index ffb9294..33cc3d6 100644 --- a/src/controllers/cli_controller.py +++ b/src/controllers/cli_controller.py @@ -8,6 +8,10 @@ import os import shutil +from spdx_tools.spdx.parser.jsonlikedict.json_like_dict_parser import JsonLikeDictParser +from spdx_tools.spdx.validation.document_validator import validate_full_spdx_document +from spdx_tools.spdx.parser.error import SPDXParsingError + logger = logging.getLogger(__name__) @@ -49,9 +53,30 @@ def __init__(self): def _validate_spdx_schema_version(self, aibom_data: dict, spec_version: str): """ - TODO: Implement SPDX schema validation. + Validates the AIBOM data using the official spdx-tools package. """ - pass + try: + # Map aibom versions to spdx_tools expected format e.g. "SPDX-2.3" + spdx_version_str = f"SPDX-{spec_version}" + + try: + parser = JsonLikeDictParser() + document = parser.parse(aibom_data) + except SPDXParsingError as e: + logger.error("SPDX parsing failed for version %s: %s", spec_version, e) + return False + + validation_messages = validate_full_spdx_document(document, spdx_version_str) + if validation_messages: + for msg in validation_messages: + logger.error("SPDX validation error: %s", msg.validation_message) + return False + + logger.info("SPDX schema validation successful for version %s", spec_version) + return True + except Exception as e: + logger.error("Failed to validate SPDX schema for version %s: %s", spec_version, e) + return False def generate(self, model_id: str, output_file: Optional[str] = None, include_inference: bool = False, enable_summarization: bool = False, verbose: bool = False, diff --git a/tests/test_cli_controller.py b/tests/test_cli_controller.py index 19af382..3cdac1b 100644 --- a/tests/test_cli_controller.py +++ b/tests/test_cli_controller.py @@ -104,6 +104,51 @@ def _normalise_model_id(model_id): class CLIControllerTests(unittest.TestCase): + + def test_validate_spdx_schema_version_success(self): + controller = CLIController() + # A minimal valid SPDX 2.3 document + valid_spdx_data = { + "spdxVersion": "SPDX-2.3", + "dataLicense": "CC0-1.0", + "SPDXID": "SPDXRef-DOCUMENT", + "name": "Test-Document", + "documentNamespace": "http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301", + "creationInfo": { + "creators": ["Tool: owasp-aibom-generator"], + "created": "2023-11-20T14:30:00Z" + }, + "packages": [ + { + "name": "Test-Package", + "SPDXID": "SPDXRef-Package", + "downloadLocation": "NOASSERTION", + "licenseDeclared": "NOASSERTION" + } + ], + "relationships": [ + { + "spdxElementId": "SPDXRef-DOCUMENT", + "relatedSpdxElement": "SPDXRef-Package", + "relationshipType": "DESCRIBES" + } + ] + } + + result = controller._validate_spdx_schema_version(valid_spdx_data, "2.3") + self.assertTrue(result) + + def test_validate_spdx_schema_version_failure(self): + controller = CLIController() + # Invalid SPDX document (missing mandatory fields like creationInfo) + invalid_spdx_data = { + "spdxVersion": "SPDX-2.3", + "SPDXID": "SPDXRef-DOCUMENT", + } + + result = controller._validate_spdx_schema_version(invalid_spdx_data, "2.3") + self.assertFalse(result) + def _assert_cyclonedx_export(self, exported: dict, spec_version: str): self.assertEqual(exported["bomFormat"], "CycloneDX") self.assertEqual(exported["specVersion"], spec_version)