From c5305b048165155dbe8370e0177b927ca0a36d73 Mon Sep 17 00:00:00 2001 From: David Meibusch Date: Tue, 23 Jan 2024 15:58:14 +1000 Subject: [PATCH 1/2] Adds failing test for metadata with pedigree (XML) Signed-off-by: David Meibusch --- .../org/cyclonedx/parsers/XmlParserTest.java | 14 +++++++- .../resources/bom-1.2-metadata-pedigree.xml | 35 +++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 src/test/resources/bom-1.2-metadata-pedigree.xml diff --git a/src/test/java/org/cyclonedx/parsers/XmlParserTest.java b/src/test/java/org/cyclonedx/parsers/XmlParserTest.java index a7568e86b..a067c1e06 100644 --- a/src/test/java/org/cyclonedx/parsers/XmlParserTest.java +++ b/src/test/java/org/cyclonedx/parsers/XmlParserTest.java @@ -18,6 +18,7 @@ */ package org.cyclonedx.parsers; +import org.apache.commons.io.FileUtils; import org.cyclonedx.CycloneDxSchema; import org.cyclonedx.CycloneDxSchema.Version; import org.cyclonedx.model.Bom; @@ -125,6 +126,17 @@ private void testPedigreeFromExample(final Pedigree pedigree) { assertEquals(2, pedigree.getPatches().get(1).getResolves().size()); } + @Test + public void testValid12BomWithMetadataPedigree() throws Exception { + final File file = new File(Objects.requireNonNull(this.getClass().getResource("/bom-1.2-metadata-pedigree.xml")).getFile()); + final byte[] bytes = FileUtils.readFileToByteArray(file); + final XmlParser parser = new XmlParser(); + final boolean valid = parser.isValid(bytes, CycloneDxSchema.Version.VERSION_12); + assertTrue(valid); + + parser.parse(bytes); + } + @Test public void testParsedObjects10Bom() throws Exception { final Bom bom = getXmlBom("bom-1.0.xml"); @@ -348,7 +360,7 @@ public void testIssue336Regression() throws Exception { assertEquals("foo", bom.getMetadata().getComponent().getProperties().get(0).getName()); assertEquals("bar", bom.getMetadata().getComponent().getProperties().get(0).getValue()); } - + @Test public void testIssue338RegressionWithSingleTool() throws Exception { final Bom bom = getXmlBom("regression/issue338-single-tool.xml"); diff --git a/src/test/resources/bom-1.2-metadata-pedigree.xml b/src/test/resources/bom-1.2-metadata-pedigree.xml new file mode 100644 index 000000000..5822dcfb3 --- /dev/null +++ b/src/test/resources/bom-1.2-metadata-pedigree.xml @@ -0,0 +1,35 @@ + + + + + com.acme + sample-library + 1.0.0 + + + + org.example + sample-library-ancestor + 1.0.0 + + + + + org.example + sample-library-descendant + 1.0.1 + + + + + org.example + sample-library-variant + 1.0.2 + + + + + + From 8e13d7fee1148067f7b7aab8550462d26564825f Mon Sep 17 00:00:00 2001 From: David Meibusch Date: Thu, 25 Jan 2024 15:20:20 +1000 Subject: [PATCH 2/2] Fix for cyclonedx-core-java/issues/363 Explicitly handle the parsing of components when parsing from the token buffer. This happens in `MetadataDeserializer` when the token stream is materialized to a JsonNode and then "re-parsed" (hence no longer an XML-based stream) Signed-off-by: David Meibusch --- .../ComponentWrapperDeserializer.java | 28 ++++++++++++++++--- .../deserializer/MetadataDeserializer.java | 21 ++++++++------ .../org/cyclonedx/parsers/XmlParserTest.java | 20 +++++++------ .../resources/bom-1.2-metadata-pedigree.xml | 12 ++++---- 4 files changed, 54 insertions(+), 27 deletions(-) diff --git a/src/main/java/org/cyclonedx/util/deserializer/ComponentWrapperDeserializer.java b/src/main/java/org/cyclonedx/util/deserializer/ComponentWrapperDeserializer.java index ecf6b5910..39b757a19 100644 --- a/src/main/java/org/cyclonedx/util/deserializer/ComponentWrapperDeserializer.java +++ b/src/main/java/org/cyclonedx/util/deserializer/ComponentWrapperDeserializer.java @@ -20,10 +20,15 @@ import java.io.IOException; import java.util.Arrays; +import java.util.Collections; +import java.util.List; import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.dataformat.xml.deser.FromXmlParser; import org.cyclonedx.model.Ancestors; import org.cyclonedx.model.Component; @@ -72,10 +77,25 @@ public ComponentWrapper deserialize( return null; } - Component[] components = parser.readValueAs(Component[].class); - - wrapper.setComponents(Arrays.asList(components)); - + List components = Collections.emptyList(); + JsonToken currentToken = parser.currentToken(); + if (currentToken == JsonToken.START_ARRAY) { + components = Arrays.asList(parser.readValueAs(Component[].class)); + } else if (currentToken == JsonToken.START_OBJECT) { + // This is possible for XML input when tree has been read, then parsed with token buffer parser + ObjectNode node = parser.readValueAs(ObjectNode.class); + if (node.has("component")) { + JsonNode component = node.get("component"); + JsonParser componentsParser = component.traverse(parser.getCodec()); + if (component.isArray()) { + components = Arrays.asList(componentsParser.readValueAs(Component[].class)); + } else { + components = Collections.singletonList(componentsParser.readValueAs(Component.class)); + } + } + } + wrapper.setComponents(components); return wrapper; + } } diff --git a/src/main/java/org/cyclonedx/util/deserializer/MetadataDeserializer.java b/src/main/java/org/cyclonedx/util/deserializer/MetadataDeserializer.java index 0314ac251..8f702ac7f 100644 --- a/src/main/java/org/cyclonedx/util/deserializer/MetadataDeserializer.java +++ b/src/main/java/org/cyclonedx/util/deserializer/MetadataDeserializer.java @@ -3,10 +3,10 @@ import java.io.IOException; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.List; import java.util.ArrayList; import java.util.Collections; +import java.util.Date; +import java.util.List; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.type.TypeReference; @@ -28,8 +28,6 @@ public class MetadataDeserializer extends JsonDeserializer { - private final ObjectMapper mapper = new ObjectMapper(); - private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX"); private final LifecycleDeserializer lifecycleDeserializer = new LifecycleDeserializer(); @@ -42,6 +40,13 @@ public Metadata deserialize(JsonParser jsonParser, DeserializationContext ctxt) Metadata metadata = new Metadata(); + ObjectMapper mapper; + if (jsonParser.getCodec() instanceof ObjectMapper) { + mapper = (ObjectMapper) jsonParser.getCodec(); + } else { + mapper = new ObjectMapper(); + } + // Parsing other fields in the Metadata object if (node.has("authors")) { JsonNode authorsNode = node.get("authors"); @@ -122,10 +127,10 @@ else if (toolsNode.has("tool")) { else { ToolInformation toolInformation = new ToolInformation(); if (toolsNode.has("components")) { - parseComponents(toolsNode.get("components"), toolInformation); + parseComponents(toolsNode.get("components"), toolInformation, mapper); } if (toolsNode.has("services")) { - parseServices(toolsNode.get("services"), toolInformation); + parseServices(toolsNode.get("services"), toolInformation, mapper); } metadata.setToolChoice(toolInformation); } @@ -134,7 +139,7 @@ else if (toolsNode.has("tool")) { return metadata; } - private void parseComponents(JsonNode componentsNode, ToolInformation toolInformation) { + private void parseComponents(JsonNode componentsNode, ToolInformation toolInformation, ObjectMapper mapper) { if (componentsNode != null) { if (componentsNode.isArray()) { List components = mapper.convertValue(componentsNode, new TypeReference>() {}); @@ -146,7 +151,7 @@ private void parseComponents(JsonNode componentsNode, ToolInformation toolInform } } - private void parseServices(JsonNode servicesNode, ToolInformation toolInformation) { + private void parseServices(JsonNode servicesNode, ToolInformation toolInformation, ObjectMapper mapper) { if (servicesNode != null) { if (servicesNode.isArray()) { List services = mapper.convertValue(servicesNode, new TypeReference>() {}); diff --git a/src/test/java/org/cyclonedx/parsers/XmlParserTest.java b/src/test/java/org/cyclonedx/parsers/XmlParserTest.java index a067c1e06..9f9854843 100644 --- a/src/test/java/org/cyclonedx/parsers/XmlParserTest.java +++ b/src/test/java/org/cyclonedx/parsers/XmlParserTest.java @@ -18,7 +18,10 @@ */ package org.cyclonedx.parsers; -import org.apache.commons.io.FileUtils; +import java.io.File; +import java.util.List; +import java.util.Objects; + import org.cyclonedx.CycloneDxSchema; import org.cyclonedx.CycloneDxSchema.Version; import org.cyclonedx.model.Bom; @@ -26,15 +29,11 @@ import org.cyclonedx.model.Dependency; import org.cyclonedx.model.ExternalReference; import org.cyclonedx.model.Pedigree; -import org.junit.jupiter.api.Test; -import java.io.File; -import java.util.List; -import java.util.Objects; - import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Test; public class XmlParserTest extends AbstractParserTest @@ -129,12 +128,15 @@ private void testPedigreeFromExample(final Pedigree pedigree) { @Test public void testValid12BomWithMetadataPedigree() throws Exception { final File file = new File(Objects.requireNonNull(this.getClass().getResource("/bom-1.2-metadata-pedigree.xml")).getFile()); - final byte[] bytes = FileUtils.readFileToByteArray(file); final XmlParser parser = new XmlParser(); - final boolean valid = parser.isValid(bytes, CycloneDxSchema.Version.VERSION_12); + final boolean valid = parser.isValid(file, CycloneDxSchema.Version.VERSION_12); assertTrue(valid); - parser.parse(bytes); + final Bom bom = parser.parse(file); + Pedigree pedigree = bom.getMetadata().getComponent().getPedigree(); + assertEquals(2, pedigree.getAncestors().getComponents().size()); + assertEquals(1, pedigree.getDescendants().getComponents().size()); + assertEquals(0, pedigree.getVariants().getComponents().size()); } @Test diff --git a/src/test/resources/bom-1.2-metadata-pedigree.xml b/src/test/resources/bom-1.2-metadata-pedigree.xml index 5822dcfb3..e4776a2dd 100644 --- a/src/test/resources/bom-1.2-metadata-pedigree.xml +++ b/src/test/resources/bom-1.2-metadata-pedigree.xml @@ -11,7 +11,12 @@ org.example - sample-library-ancestor + sample-library-ancestor-1 + 1.0.0 + + + org.example + sample-library-ancestor-2 1.0.0 @@ -23,11 +28,6 @@ - - org.example - sample-library-variant - 1.0.2 -