From 1d495b9334470410348fe9d74ea52e2403a77c0d Mon Sep 17 00:00:00 2001 From: Mac Date: Fri, 6 Feb 2026 03:31:08 +0530 Subject: [PATCH] Replace deprecated documentDescribes with DESCRIBES relationships Fixes #1958 The SPDX 2.3 schema marks documentDescribes as deprecated and recommends using DESCRIBES relationships instead. This change: - Removes documentDescribes field from SPDX output - Adds DESCRIBES relationships to the relationships array - Maintains backward compatibility for reading old SPDX documents - Updates test expectations and test data files The implementation follows the SPDX spec recommendation to use SPDXRef-DOCUMENT DESCRIBES relationships instead of the deprecated documentDescribes field. Signed-off-by: Gyan Ranjan Panda --- scanpipe/pipes/spdx.py | 26 ++++++++++++++++--- .../data/asgiref/asgiref-3.3.0.spdx.json | 8 +++--- .../tests/data/spdx/dependencies.spdx.json | 8 +++--- scanpipe/tests/pipes/test_spdx.py | 17 +++++++++++- 4 files changed, 48 insertions(+), 11 deletions(-) diff --git a/scanpipe/pipes/spdx.py b/scanpipe/pipes/spdx.py index 5bb3e6bb16..a11c20987a 100644 --- a/scanpipe/pipes/spdx.py +++ b/scanpipe/pipes/spdx.py @@ -607,7 +607,6 @@ def as_dict(self): "SPDXID": self.spdx_id, "name": self.safe_document_name(self.name), "documentNamespace": self.namespace, - "documentDescribes": self.describes, "creationInfo": self.creation_info.as_dict(), "packages": [package.as_dict(self.version) for package in self.packages], } @@ -620,9 +619,19 @@ def as_dict(self): license_info.as_dict() for license_info in self.extracted_licenses ] - if self.relationships: + relationships_list = list(self.relationships) if self.relationships else [] + if self.describes: + for described_id in self.describes: + describes_relationship = Relationship( + spdx_id=self.spdx_id, + related_spdx_id=described_id, + relationship="DESCRIBES", + ) + relationships_list.append(describes_relationship) + + if relationships_list: data["relationships"] = [ - relationship.as_dict() for relationship in self.relationships + relationship.as_dict() for relationship in relationships_list ] if self.comment: @@ -636,13 +645,22 @@ def as_json(self, indent=2): @classmethod def from_data(cls, data): + describes = data.get("documentDescribes") + if not describes: + describes = [] + document_id = data.get("SPDXID", "SPDXRef-DOCUMENT") + for rel_data in data.get("relationships", []): + if (rel_data.get("spdxElementId") == document_id and + rel_data.get("relationshipType") == "DESCRIBES"): + describes.append(rel_data.get("relatedSpdxElement")) + return cls( spdx_id=data.get("SPDXID"), version=data.get("spdxVersion", "").split("SPDX-")[-1], data_license=data.get("dataLicense"), name=data.get("name"), namespace=data.get("documentNamespace"), - describes=data.get("documentDescribes"), + describes=describes, creation_info=CreationInfo.from_data(data.get("creationInfo", {})), packages=[ Package.from_data(package_data) diff --git a/scanpipe/tests/data/asgiref/asgiref-3.3.0.spdx.json b/scanpipe/tests/data/asgiref/asgiref-3.3.0.spdx.json index 565e2f4506..28ee50be87 100644 --- a/scanpipe/tests/data/asgiref/asgiref-3.3.0.spdx.json +++ b/scanpipe/tests/data/asgiref/asgiref-3.3.0.spdx.json @@ -4,9 +4,6 @@ "SPDXID": "SPDXRef-DOCUMENT-92fe63d9-1d53-4b63-b19a-85022fb7a3f3", "name": "scancodeio_asgiref", "documentNamespace": "https://scancode.io/spdxdocs/92fe63d9-1d53-4b63-b19a-85022fb7a3f3", - "documentDescribes": [ - "SPDXRef-scancodeio-project-92fe63d9-1d53-4b63-b19a-85022fb7a3f3" - ], "creationInfo": { "created": "2000-01-01T01:02:03Z", "creators": [ @@ -131,6 +128,11 @@ ], "files": [], "relationships": [ + { + "spdxElementId": "SPDXRef-DOCUMENT-92fe63d9-1d53-4b63-b19a-85022fb7a3f3", + "relatedSpdxElement": "SPDXRef-scancodeio-project-92fe63d9-1d53-4b63-b19a-85022fb7a3f3", + "relationshipType": "DESCRIBES" + }, { "spdxElementId": "SPDXRef-scancodeio-project-92fe63d9-1d53-4b63-b19a-85022fb7a3f3", "relatedSpdxElement": "SPDXRef-scancodeio-discoveredpackage-543a3583-3a13-4b5d-a039-c6bc4072de35", diff --git a/scanpipe/tests/data/spdx/dependencies.spdx.json b/scanpipe/tests/data/spdx/dependencies.spdx.json index 7b83ccefa8..2c5141e25b 100644 --- a/scanpipe/tests/data/spdx/dependencies.spdx.json +++ b/scanpipe/tests/data/spdx/dependencies.spdx.json @@ -4,9 +4,6 @@ "SPDXID": "SPDXRef-DOCUMENT-b74fe5df-e965-415e-ba65-f38421a0695d", "name": "scancodeio_analysis", "documentNamespace": "https://scancode.io/spdxdocs/b74fe5df-e965-415e-ba65-f38421a0695d", - "documentDescribes": [ - "SPDXRef-scancodeio-project-b74fe5df-e965-415e-ba65-f38421a0695d" - ], "creationInfo": { "created": "2000-01-01T01:02:03Z", "creators": [ @@ -99,6 +96,11 @@ } ], "relationships": [ + { + "spdxElementId": "SPDXRef-DOCUMENT-b74fe5df-e965-415e-ba65-f38421a0695d", + "relatedSpdxElement": "SPDXRef-scancodeio-project-b74fe5df-e965-415e-ba65-f38421a0695d", + "relationshipType": "DESCRIBES" + }, { "spdxElementId": "SPDXRef-scancodeio-project-b74fe5df-e965-415e-ba65-f38421a0695d", "relatedSpdxElement": "SPDXRef-scancodeio-discoveredpackage-a83a60de-81bc-4bf4-b48c-dc78e0e658a9", diff --git a/scanpipe/tests/pipes/test_spdx.py b/scanpipe/tests/pipes/test_spdx.py index befa12913a..96f7f668af 100644 --- a/scanpipe/tests/pipes/test_spdx.py +++ b/scanpipe/tests/pipes/test_spdx.py @@ -196,7 +196,6 @@ def setUp(self): "SPDXID": "SPDXRef-DOCUMENT", "name": "document_name", "documentNamespace": "https://[CreatorWebsite]/[DocumentName]-[UUID]", - "documentDescribes": ["SPDXRef-project"], "creationInfo": { "created": "2022-09-21T13:50:20Z", "creators": [ @@ -272,6 +271,11 @@ def setUp(self): } ], "relationships": [ + { + "spdxElementId": "SPDXRef-DOCUMENT", + "relatedSpdxElement": "SPDXRef-project", + "relationshipType": "DESCRIBES", + }, { "spdxElementId": "SPDXRef-package1", "relatedSpdxElement": "SPDXRef-file1", @@ -389,6 +393,17 @@ def test_spdx_document_from_data(self): assert spdx.Document.from_data({}) document = spdx.Document.from_data(self.document_spdx_data) assert self.document_spdx_data == document.as_dict() + + # Test backward compatibility with deprecated documentDescribes field + legacy_data = dict(self.document_spdx_data) + legacy_data["documentDescribes"] = ["SPDXRef-project"] + # Remove DESCRIBES relationship to test fallback + legacy_data["relationships"] = [ + rel for rel in legacy_data["relationships"] + if rel.get("relationshipType") != "DESCRIBES" + ] + document_from_legacy = spdx.Document.from_data(legacy_data) + assert ["SPDXRef-project"] == document_from_legacy.describes def test_spdx_document_as_json(self): document = spdx.Document(**self.document_data)