From 707c53c7abdb2a10009d53a0903bf9b2ce051654 Mon Sep 17 00:00:00 2001 From: Tommy Quak Date: Wed, 11 Nov 2026 11:45:06 +0800 Subject: [PATCH] Check the document SPDX ID in Verify The Jackson store does not keep the document's SPDXID when it reads a JSON, YAML or XML file and always creates the document as SPDXRef-DOCUMENT, so the model's verify never sees the original value and a document with any other ID was reported as valid. The spec requires the document SPDXID to be SPDXRef-DOCUMENT [1]. Read the SPDXID from the file in Verify and report it with the same message the tag-value parser already uses. Part of Issue 316. [1] https://spdx.github.io/spdx-spec/v2.3/document-creation-information/#63-spdx-identifier-field Signed-off-by: Tommy Quak --- src/main/java/org/spdx/tools/Verify.java | 39 +++++++++++++++++++ src/test/java/org/spdx/tools/VerifyTest.java | 15 ++++++- .../SPDXJSONExample-wrongdocid.spdx.json | 17 ++++++++ .../SPDXXMLExample-wrongdocid.spdx.xml | 18 +++++++++ .../SPDXYAMLExample-wrongdocid.spdx.yaml | 15 +++++++ 5 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 testResources/SPDXJSONExample-wrongdocid.spdx.json create mode 100644 testResources/SPDXXMLExample-wrongdocid.spdx.xml create mode 100644 testResources/SPDXYAMLExample-wrongdocid.spdx.yaml diff --git a/src/main/java/org/spdx/tools/Verify.java b/src/main/java/org/spdx/tools/Verify.java index 1736ceb..57de7e6 100644 --- a/src/main/java/org/spdx/tools/Verify.java +++ b/src/main/java/org/spdx/tools/Verify.java @@ -32,6 +32,7 @@ import org.spdx.core.CoreModelObject; import org.spdx.core.InvalidSPDXAnalysisException; +import org.spdx.library.model.v2.SpdxConstantsCompatV2; import org.spdx.library.model.v2.Version; import org.spdx.storage.ISerializableModelStore; import org.spdx.tagvaluestore.TagValueStore; @@ -40,6 +41,8 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLMapper; import com.networknt.schema.JsonSchema; import com.networknt.schema.JsonSchemaFactory; @@ -67,6 +70,8 @@ public class Verify { public static final String JSON_SCHEMA_RESOURCE_V3 = "resources/spdx-schema-v3.0.1.json"; static final ObjectMapper JSON_MAPPER = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT); + static final ObjectMapper YAML_MAPPER = new YAMLMapper(); + static final ObjectMapper XML_MAPPER = new XmlMapper(); /** * Main entry point for the Verify tool @@ -185,6 +190,9 @@ public static List verify(String filePath, SerFileType fileType) throws // add in any parser warnings retval.addAll(((TagValueStore)store).getWarnings()); } + if (SerFileType.JSON.equals(fileType) || SerFileType.YAML.equals(fileType) || SerFileType.XML.equals(fileType)) { + retval.addAll(verifyDocumentId(file, fileType)); + } if (SerFileType.JSON.equals(fileType) || SerFileType.JSONLD.equals(fileType)) { try { String jsonSchemaResource; @@ -237,6 +245,37 @@ public static List verify(String filePath, SerFileType fileType) throws return retval; } + /** + * Verify the document SPDX ID in a JSON, YAML or XML file + *

+ * The Jackson store always stores the document as SPDXRef-DOCUMENT, so the + * ID in the file is checked here rather than by the model's verify + * @param file SPDX file + * @param fileType JSON, YAML or XML + * @return A list of verification errors - empty if the document ID is valid + */ + private static List verifyDocumentId(File file, SerFileType fileType) { + ObjectMapper mapper; + if (SerFileType.YAML.equals(fileType)) { + mapper = YAML_MAPPER; + } else if (SerFileType.XML.equals(fileType)) { + mapper = XML_MAPPER; + } else { + mapper = JSON_MAPPER; + } + List retval = new ArrayList<>(); + try (InputStream is = new FileInputStream(file)) { + JsonNode documentId = mapper.readTree(is).get(SpdxConstantsCompatV2.SPDX_IDENTIFIER); + if (documentId != null && !SpdxConstantsCompatV2.SPDX_DOCUMENT_ID.equals(documentId.asText())) { + retval.add("SPDX Document " + documentId.asText() + " is invalid. Document IDs must be " + + SpdxConstantsCompatV2.SPDX_DOCUMENT_ID); + } + } catch (IOException e) { + retval.add("Unable to verify the document SPDX ID due to I/O Error"); + } + return retval; + } + /** * Verify a tag/value file * @param filePath File path to the SPDX Tag Value file to be verified diff --git a/src/test/java/org/spdx/tools/VerifyTest.java b/src/test/java/org/spdx/tools/VerifyTest.java index f2d03fe..ee188f6 100644 --- a/src/test/java/org/spdx/tools/VerifyTest.java +++ b/src/test/java/org/spdx/tools/VerifyTest.java @@ -36,7 +36,11 @@ public class VerifyTest extends TestCase { static final String TEST_WARNING_FILE_PATH = TEST_DIR + File.separator + "SPDXTagExample-v2.2-warning.spdx"; static final String BAD_JSON_FILE_PATH = TEST_DIR + File.separator + "BadJSON.spdx.json"; static final String DOUBLE_JSON_LD_FILE_PATH = TEST_DIR + File.separator + "double.jsonld"; - + static final String JSON_WRONG_DOC_ID_FILE_PATH = TEST_DIR + File.separator + "SPDXJSONExample-wrongdocid.spdx.json"; + static final String YAML_WRONG_DOC_ID_FILE_PATH = TEST_DIR + File.separator + "SPDXYAMLExample-wrongdocid.spdx.yaml"; + static final String XML_WRONG_DOC_ID_FILE_PATH = TEST_DIR + File.separator + "SPDXXMLExample-wrongdocid.spdx.xml"; + static final String WRONG_DOC_ID_MESSAGE = "SPDX Document SPDXRef-NOTDOC is invalid. Document IDs must be SPDXRef-DOCUMENT"; + protected void setUp() throws Exception { super.setUp(); ModelRegistry.getModelRegistry().registerModel(new SpdxModelInfoV3_0()); @@ -106,4 +110,13 @@ public void testVerifyDouble() throws SpdxVerificationException { List result = Verify.verify(DOUBLE_JSON_LD_FILE_PATH, SerFileType.JSONLD); assertEquals(0, result.size()); } + + public void testVerifyWrongDocumentId() throws SpdxVerificationException { + List result = Verify.verify(JSON_WRONG_DOC_ID_FILE_PATH, SerFileType.JSON); + assertTrue(result.contains(WRONG_DOC_ID_MESSAGE)); + result = Verify.verify(YAML_WRONG_DOC_ID_FILE_PATH, SerFileType.YAML); + assertTrue(result.contains(WRONG_DOC_ID_MESSAGE)); + result = Verify.verify(XML_WRONG_DOC_ID_FILE_PATH, SerFileType.XML); + assertTrue(result.contains(WRONG_DOC_ID_MESSAGE)); + } } diff --git a/testResources/SPDXJSONExample-wrongdocid.spdx.json b/testResources/SPDXJSONExample-wrongdocid.spdx.json new file mode 100644 index 0000000..42bbb20 --- /dev/null +++ b/testResources/SPDXJSONExample-wrongdocid.spdx.json @@ -0,0 +1,17 @@ +{ + "SPDXID" : "SPDXRef-NOTDOC", + "spdxVersion" : "SPDX-2.3", + "creationInfo" : { + "created" : "2026-11-10T00:00:00Z", + "creators" : [ "Tool: test" ] + }, + "name" : "wrong-document-id", + "dataLicense" : "CC0-1.0", + "documentNamespace" : "https://example.com/wrong-document-id", + "packages" : [ { + "SPDXID" : "SPDXRef-Package", + "name" : "package", + "downloadLocation" : "NOASSERTION", + "filesAnalyzed" : false + } ] +} diff --git a/testResources/SPDXXMLExample-wrongdocid.spdx.xml b/testResources/SPDXXMLExample-wrongdocid.spdx.xml new file mode 100644 index 0000000..47a957c --- /dev/null +++ b/testResources/SPDXXMLExample-wrongdocid.spdx.xml @@ -0,0 +1,18 @@ + + + SPDXRef-NOTDOC + SPDX-2.3 + + 2026-11-10T00:00:00Z + Tool: test + + wrong-document-id + CC0-1.0 + https://example.com/wrong-document-id + + SPDXRef-Package + package + NOASSERTION + false + + diff --git a/testResources/SPDXYAMLExample-wrongdocid.spdx.yaml b/testResources/SPDXYAMLExample-wrongdocid.spdx.yaml new file mode 100644 index 0000000..c713d89 --- /dev/null +++ b/testResources/SPDXYAMLExample-wrongdocid.spdx.yaml @@ -0,0 +1,15 @@ +--- +SPDXID: "SPDXRef-NOTDOC" +spdxVersion: "SPDX-2.3" +creationInfo: + created: "2026-11-10T00:00:00Z" + creators: + - "Tool: test" +name: "wrong-document-id" +dataLicense: "CC0-1.0" +documentNamespace: "https://example.com/wrong-document-id" +packages: +- SPDXID: "SPDXRef-Package" + name: "package" + downloadLocation: "NOASSERTION" + filesAnalyzed: false