From 2082a52bf981fe3dd88418195ce75c22cf1bd7ae Mon Sep 17 00:00:00 2001 From: Alex Alzate Date: Mon, 9 Dec 2024 17:29:24 -0500 Subject: [PATCH 1/2] Fix Issue 562 --- .../ToolInformationDeserializer.java | 32 +++++++++--- .../util/serializer/MetadataSerializer.java | 12 ++--- .../org/cyclonedx/BomJsonGeneratorTest.java | 15 ++++++ .../org/cyclonedx/BomXmlGeneratorTest.java | 51 ++++++++++++++++++- .../org/cyclonedx/parsers/JsonParserTest.java | 7 +++ .../org/cyclonedx/parsers/XmlParserTest.java | 7 +++ src/test/resources/regression/issue562.json | 25 +++++++++ src/test/resources/regression/issue562.xml | 26 ++++++++++ 8 files changed, 160 insertions(+), 15 deletions(-) create mode 100644 src/test/resources/regression/issue562.json create mode 100644 src/test/resources/regression/issue562.xml diff --git a/src/main/java/org/cyclonedx/util/deserializer/ToolInformationDeserializer.java b/src/main/java/org/cyclonedx/util/deserializer/ToolInformationDeserializer.java index 001967945..8f7c642ed 100644 --- a/src/main/java/org/cyclonedx/util/deserializer/ToolInformationDeserializer.java +++ b/src/main/java/org/cyclonedx/util/deserializer/ToolInformationDeserializer.java @@ -45,7 +45,7 @@ public ToolInformation deserialize(JsonParser jsonParser, DeserializationContext return parseToolInformation(node); } - private ToolInformation parseToolInformation(JsonNode toolsNode) throws IOException { + private ToolInformation parseToolInformation(JsonNode toolsNode) { ToolInformation toolInformation = new ToolInformation(); if (toolsNode.has("components")) { parseComponents(toolsNode.get("components"), toolInformation); @@ -58,24 +58,42 @@ private ToolInformation parseToolInformation(JsonNode toolsNode) throws IOExcept private void parseComponents(JsonNode componentsNode, ToolInformation toolInformation) { if (componentsNode != null) { + // Case JSON input where "components" is an array if (componentsNode.isArray()) { List components = mapper.convertValue(componentsNode, new TypeReference>() {}); toolInformation.setComponents(components); - } else if (componentsNode.isObject()) { - Component component = mapper.convertValue(componentsNode, Component.class); - toolInformation.setComponents(Collections.singletonList(component)); + } + // Case XML-like input where "components" contains "component" + else if (componentsNode.isObject() && componentsNode.has("component")) { + JsonNode componentNode = componentsNode.get("component"); + if (componentNode.isArray()) { + List components = mapper.convertValue(componentNode, new TypeReference>() {}); + toolInformation.setComponents(components); + } else if (componentNode.isObject()) { + Component component = mapper.convertValue(componentNode, Component.class); + toolInformation.setComponents(Collections.singletonList(component)); + } } } } private void parseServices(JsonNode servicesNode, ToolInformation toolInformation) { if (servicesNode != null) { + // Case JSON input where "services" is an array if (servicesNode.isArray()) { List services = mapper.convertValue(servicesNode, new TypeReference>() {}); toolInformation.setServices(services); - } else if (servicesNode.isObject()) { - Service service = mapper.convertValue(servicesNode, Service.class); - toolInformation.setServices(Collections.singletonList(service)); + } + // Case XML-like input where "services" contains "component" + else if (servicesNode.isObject() && servicesNode.has("service")) { + JsonNode serviceNode = servicesNode.get("service"); + if (serviceNode.isArray()) { + List services = mapper.convertValue(servicesNode, new TypeReference>() {}); + toolInformation.setServices(services); + } else if (serviceNode.isObject()) { + Service service = mapper.convertValue(servicesNode, Service.class); + toolInformation.setServices(Collections.singletonList(service)); + } } } } diff --git a/src/main/java/org/cyclonedx/util/serializer/MetadataSerializer.java b/src/main/java/org/cyclonedx/util/serializer/MetadataSerializer.java index 5b55c226d..f90c22379 100644 --- a/src/main/java/org/cyclonedx/util/serializer/MetadataSerializer.java +++ b/src/main/java/org/cyclonedx/util/serializer/MetadataSerializer.java @@ -11,6 +11,7 @@ import org.cyclonedx.Version; import org.cyclonedx.model.Metadata; import org.cyclonedx.model.Property; +import org.cyclonedx.model.component.evidence.Occurrence; import org.cyclonedx.model.metadata.ToolInformation; import static org.cyclonedx.util.serializer.SerializerUtils.shouldSerializeField; @@ -164,15 +165,14 @@ private void writeArrayFieldJSON(JsonGenerator jsonGenerator, String fieldNa } private void writeArrayFieldXML(List items, ToXmlGenerator xmlGenerator, String fieldName) throws IOException { - if (items != null) { + if (CollectionUtils.isNotEmpty(items)) { xmlGenerator.writeFieldName(fieldName + "s"); - xmlGenerator.writeStartArray(); + xmlGenerator.writeStartObject(); for (T item : items) { - xmlGenerator.writeStartObject(); - xmlGenerator.writeObjectField(fieldName, item); - xmlGenerator.writeEndObject(); + xmlGenerator.writeFieldName(fieldName); + xmlGenerator.writeObject(item); } - xmlGenerator.writeEndArray(); + xmlGenerator.writeEndObject(); } } diff --git a/src/test/java/org/cyclonedx/BomJsonGeneratorTest.java b/src/test/java/org/cyclonedx/BomJsonGeneratorTest.java index 09aea41ee..bfca5c8fe 100644 --- a/src/test/java/org/cyclonedx/BomJsonGeneratorTest.java +++ b/src/test/java/org/cyclonedx/BomJsonGeneratorTest.java @@ -545,6 +545,21 @@ public void testIssue408Regression_xmlToJson_externalReferenceBom() throws Excep assertTrue(parser.isValid(loadedFile, version)); } + @Test + public void testIssue562() throws Exception { + Version version = Version.VERSION_15; + Bom bom = createCommonJsonBom("/regression/issue562.json"); + + + BomJsonGenerator generator = BomGeneratorFactory.createJson(version, bom); + File loadedFile = writeToFile(generator.toJsonString()); + + JsonParser parser = new JsonParser(); + assertTrue(parser.isValid(loadedFile, version)); + } + + + private void assertExternalReferenceInfo(Bom bom) { assertEquals(3, bom.getExternalReferences().size()); assertEquals(3, bom.getComponents().get(0).getExternalReferences().size()); diff --git a/src/test/java/org/cyclonedx/BomXmlGeneratorTest.java b/src/test/java/org/cyclonedx/BomXmlGeneratorTest.java index 6f6c54b5a..df7c79e7c 100644 --- a/src/test/java/org/cyclonedx/BomXmlGeneratorTest.java +++ b/src/test/java/org/cyclonedx/BomXmlGeneratorTest.java @@ -32,8 +32,10 @@ import org.cyclonedx.model.License; import org.cyclonedx.model.LicenseChoice; import org.cyclonedx.model.Metadata; +import org.cyclonedx.model.OrganizationalContact; import org.cyclonedx.model.Service; import org.cyclonedx.model.license.Expression; +import org.cyclonedx.model.metadata.ToolInformation; import org.cyclonedx.parsers.JsonParser; import org.cyclonedx.parsers.XmlParser; import org.junit.jupiter.api.AfterEach; @@ -50,7 +52,9 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; +import java.util.LinkedList; import java.util.List; +import java.util.UUID; import java.util.stream.Stream; import java.util.Objects; @@ -672,11 +676,54 @@ public void testXxeProtection() { @Test public void testIssue408Regression_extensibleTypes() throws Exception { + Bom bom = new Bom(); + bom.setSerialNumber("urn:uuid:" + UUID.randomUUID()); + + Metadata meta = new Metadata(); + + // ToolInformation test + Component tool1 = new Component(); + tool1.setType(Component.Type.APPLICATION); + tool1.setName("TOOL 1"); + tool1.setVersion("v1"); + + Component tool2 = new Component(); + tool2.setType(Component.Type.APPLICATION); + tool2.setName("TOOL 2"); + tool2.setVersion("v2"); + + ToolInformation tools = new ToolInformation(); + List components = new LinkedList<>(); + components.add(tool1); + components.add(tool2); + tools.setComponents(components); + meta.setToolChoice(tools); + + // Author test + OrganizationalContact auth1 = new OrganizationalContact(); + auth1.setName("Author 1"); + meta.addAuthor(auth1); + + OrganizationalContact auth2 = new OrganizationalContact(); + auth2.setName("Author 2"); + meta.addAuthor(auth2); + + bom.setMetadata(meta); + + BomXmlGenerator generator = BomGeneratorFactory.createXml(Version.VERSION_16, bom); + File loadedFile = writeToFile(generator.toXmlString()); + + XmlParser parser = new XmlParser(); + assertTrue(parser.isValid(loadedFile, Version.VERSION_16)); + } + + @Test + public void testIssue562() throws Exception { Version version = Version.VERSION_15; - Bom bom = createCommonBomXml("/regression/issue408-extensible-type.xml"); - addExtensibleTypes(bom); + Bom bom = createCommonBomXml("/regression/issue562.xml"); BomXmlGenerator generator = BomGeneratorFactory.createXml(version, bom); + File loadedFile = writeToFile(generator.toXmlString()); XmlParser parser = new XmlParser(); diff --git a/src/test/java/org/cyclonedx/parsers/JsonParserTest.java b/src/test/java/org/cyclonedx/parsers/JsonParserTest.java index 94edd03a6..0e4113b7c 100644 --- a/src/test/java/org/cyclonedx/parsers/JsonParserTest.java +++ b/src/test/java/org/cyclonedx/parsers/JsonParserTest.java @@ -568,4 +568,11 @@ public void schema16_cbom() throws Exception { .containsAll(Arrays.asList(CryptoFunction.KEYGEN, CryptoFunction.ENCRYPT, CryptoFunction.DECRYPT, CryptoFunction.TAG))); } + + @Test + public void testIssue562Regression() throws Exception { + final Bom bom = getJsonBom("regression/issue562.json"); + assertEquals(2, bom.getMetadata().getToolChoice().getComponents().size()); + assertEquals(2, bom.getMetadata().getAuthors().size()); + } } diff --git a/src/test/java/org/cyclonedx/parsers/XmlParserTest.java b/src/test/java/org/cyclonedx/parsers/XmlParserTest.java index c2aadeadd..a29d52822 100644 --- a/src/test/java/org/cyclonedx/parsers/XmlParserTest.java +++ b/src/test/java/org/cyclonedx/parsers/XmlParserTest.java @@ -713,4 +713,11 @@ public void schema16_cbom() throws Exception { .containsAll(Arrays.asList(CryptoFunction.KEYGEN, CryptoFunction.ENCRYPT, CryptoFunction.DECRYPT, CryptoFunction.TAG))); } + + @Test + public void testIssue562Regression() throws Exception { + final Bom bom = getXmlBom("regression/issue562.xml"); + assertEquals(2, bom.getMetadata().getToolChoice().getComponents().size()); + assertEquals(2, bom.getMetadata().getAuthors().size()); + } } diff --git a/src/test/resources/regression/issue562.json b/src/test/resources/regression/issue562.json new file mode 100644 index 000000000..c2222e9c2 --- /dev/null +++ b/src/test/resources/regression/issue562.json @@ -0,0 +1,25 @@ +{ + "bomFormat":"CycloneDX", + "specVersion":"1.6", + "serialNumber":"urn:uuid:0c81ff2e-d64e-4897-bfa4-2f0f7d8ab767", + "version" : 1, + "metadata" : { + "timestamp":"2024-12-09T21:56:45Z", + "tools" : { + "components" : [ { + "type":"application", + "name":"TOOL 1", + "version":"v1" + }, { + "type":"application", + "name":"TOOL 2", + "version":"v2" + } ] + }, + "authors" : [ { + "name":"Author 1" + }, { + "name":"Author 2" + } ] + } +} \ No newline at end of file diff --git a/src/test/resources/regression/issue562.xml b/src/test/resources/regression/issue562.xml new file mode 100644 index 000000000..dc37b7f13 --- /dev/null +++ b/src/test/resources/regression/issue562.xml @@ -0,0 +1,26 @@ + + + + 2024-12-09T21:56:45Z + + + + TOOL 1 + v1 + + + TOOL 2 + v2 + + + + + + Author 1 + + + Author 2 + + + + From f4cc7f46a45e9ade72a5c3ff40b8b555140ee908 Mon Sep 17 00:00:00 2001 From: Alex Alzate Date: Mon, 9 Dec 2024 19:01:55 -0500 Subject: [PATCH 2/2] Fix Issue 492 --- .../org/cyclonedx/BomJsonGeneratorTest.java | 13 ++++++++++++- .../org/cyclonedx/BomXmlGeneratorTest.java | 13 +++++++++++++ .../org/cyclonedx/parsers/JsonParserTest.java | 6 ++++++ .../org/cyclonedx/parsers/XmlParserTest.java | 6 ++++++ src/test/resources/regression/issue492.json | 17 +++++++++++++++++ src/test/resources/regression/issue492.xml | 18 ++++++++++++++++++ 6 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 src/test/resources/regression/issue492.json create mode 100644 src/test/resources/regression/issue492.xml diff --git a/src/test/java/org/cyclonedx/BomJsonGeneratorTest.java b/src/test/java/org/cyclonedx/BomJsonGeneratorTest.java index bfca5c8fe..35357f095 100644 --- a/src/test/java/org/cyclonedx/BomJsonGeneratorTest.java +++ b/src/test/java/org/cyclonedx/BomJsonGeneratorTest.java @@ -547,7 +547,7 @@ public void testIssue408Regression_xmlToJson_externalReferenceBom() throws Excep @Test public void testIssue562() throws Exception { - Version version = Version.VERSION_15; + Version version = Version.VERSION_16; Bom bom = createCommonJsonBom("/regression/issue562.json"); @@ -558,7 +558,18 @@ public void testIssue562() throws Exception { assertTrue(parser.isValid(loadedFile, version)); } + @Test + public void testIssue492() throws Exception { + Version version = Version.VERSION_14; + Bom bom = createCommonJsonBom("/regression/issue492.json"); + + BomJsonGenerator generator = BomGeneratorFactory.createJson(version, bom); + File loadedFile = writeToFile(generator.toJsonString()); + + JsonParser parser = new JsonParser(); + assertTrue(parser.isValid(loadedFile, version)); + } private void assertExternalReferenceInfo(Bom bom) { assertEquals(3, bom.getExternalReferences().size()); diff --git a/src/test/java/org/cyclonedx/BomXmlGeneratorTest.java b/src/test/java/org/cyclonedx/BomXmlGeneratorTest.java index df7c79e7c..dd1ecacbf 100644 --- a/src/test/java/org/cyclonedx/BomXmlGeneratorTest.java +++ b/src/test/java/org/cyclonedx/BomXmlGeneratorTest.java @@ -730,6 +730,19 @@ public void testIssue562() throws Exception { assertTrue(parser.isValid(loadedFile, version)); } + @Test + public void testIssue492() throws Exception { + Version version = Version.VERSION_15; + Bom bom = createCommonBomXml("/regression/issue492.xml"); + + BomXmlGenerator generator = BomGeneratorFactory.createXml(version, bom); + + File loadedFile = writeToFile(generator.toXmlString()); + + XmlParser parser = new XmlParser(); + assertTrue(parser.isValid(loadedFile, version)); + } + private void addExtensibleTypes(Bom bom) { ExtensibleType t1 = new ExtensibleType("abc", "test", "test"); ExtensibleType t2 = new ExtensibleType("abc", "test", "test1"); diff --git a/src/test/java/org/cyclonedx/parsers/JsonParserTest.java b/src/test/java/org/cyclonedx/parsers/JsonParserTest.java index 0e4113b7c..1cdccb4db 100644 --- a/src/test/java/org/cyclonedx/parsers/JsonParserTest.java +++ b/src/test/java/org/cyclonedx/parsers/JsonParserTest.java @@ -575,4 +575,10 @@ public void testIssue562Regression() throws Exception { assertEquals(2, bom.getMetadata().getToolChoice().getComponents().size()); assertEquals(2, bom.getMetadata().getAuthors().size()); } + + @Test + public void testIssue492Regression() throws Exception { + final Bom bom = getJsonBom("regression/issue492.json"); + assertEquals(2, bom.getMetadata().getTools().size()); + } } diff --git a/src/test/java/org/cyclonedx/parsers/XmlParserTest.java b/src/test/java/org/cyclonedx/parsers/XmlParserTest.java index a29d52822..997813e59 100644 --- a/src/test/java/org/cyclonedx/parsers/XmlParserTest.java +++ b/src/test/java/org/cyclonedx/parsers/XmlParserTest.java @@ -720,4 +720,10 @@ public void testIssue562Regression() throws Exception { assertEquals(2, bom.getMetadata().getToolChoice().getComponents().size()); assertEquals(2, bom.getMetadata().getAuthors().size()); } + + @Test + public void testIssue492Regression() throws Exception { + final Bom bom = getXmlBom("regression/issue492.xml"); + assertEquals(2, bom.getMetadata().getTools().size()); + } } diff --git a/src/test/resources/regression/issue492.json b/src/test/resources/regression/issue492.json new file mode 100644 index 000000000..a88564fb1 --- /dev/null +++ b/src/test/resources/regression/issue492.json @@ -0,0 +1,17 @@ +{ + "bomFormat":"CycloneDX", + "specVersion":"1.4", + "serialNumber":"urn:uuid:0c81ff2e-d64e-4897-bfa4-2f0f7d8ab767", + "version" : 1, + "metadata" : { + "timestamp":"2024-12-09T21:56:45Z", + "tools": [ + { + "name": "tool-a" + }, + { + "name": "tool-b" + } + ] + } +} \ No newline at end of file diff --git a/src/test/resources/regression/issue492.xml b/src/test/resources/regression/issue492.xml new file mode 100644 index 000000000..c562d1f78 --- /dev/null +++ b/src/test/resources/regression/issue492.xml @@ -0,0 +1,18 @@ + + + + 2024-12-09T21:56:45Z + + + corp A + tool A + 1-A + + + corp B + tool B + 1-B + + + +