Skip to content

Commit c7c3aa9

Browse files
authored
Merge pull request #568 from CycloneDX/issue_562
2 parents f8cac88 + f4cc7f4 commit c7c3aa9

File tree

10 files changed

+231
-15
lines changed

10 files changed

+231
-15
lines changed

src/main/java/org/cyclonedx/util/deserializer/ToolInformationDeserializer.java

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public ToolInformation deserialize(JsonParser jsonParser, DeserializationContext
4545
return parseToolInformation(node);
4646
}
4747

48-
private ToolInformation parseToolInformation(JsonNode toolsNode) throws IOException {
48+
private ToolInformation parseToolInformation(JsonNode toolsNode) {
4949
ToolInformation toolInformation = new ToolInformation();
5050
if (toolsNode.has("components")) {
5151
parseComponents(toolsNode.get("components"), toolInformation);
@@ -58,24 +58,42 @@ private ToolInformation parseToolInformation(JsonNode toolsNode) throws IOExcept
5858

5959
private void parseComponents(JsonNode componentsNode, ToolInformation toolInformation) {
6060
if (componentsNode != null) {
61+
// Case JSON input where "components" is an array
6162
if (componentsNode.isArray()) {
6263
List<Component> components = mapper.convertValue(componentsNode, new TypeReference<List<Component>>() {});
6364
toolInformation.setComponents(components);
64-
} else if (componentsNode.isObject()) {
65-
Component component = mapper.convertValue(componentsNode, Component.class);
66-
toolInformation.setComponents(Collections.singletonList(component));
65+
}
66+
// Case XML-like input where "components" contains "component"
67+
else if (componentsNode.isObject() && componentsNode.has("component")) {
68+
JsonNode componentNode = componentsNode.get("component");
69+
if (componentNode.isArray()) {
70+
List<Component> components = mapper.convertValue(componentNode, new TypeReference<List<Component>>() {});
71+
toolInformation.setComponents(components);
72+
} else if (componentNode.isObject()) {
73+
Component component = mapper.convertValue(componentNode, Component.class);
74+
toolInformation.setComponents(Collections.singletonList(component));
75+
}
6776
}
6877
}
6978
}
7079

7180
private void parseServices(JsonNode servicesNode, ToolInformation toolInformation) {
7281
if (servicesNode != null) {
82+
// Case JSON input where "services" is an array
7383
if (servicesNode.isArray()) {
7484
List<Service> services = mapper.convertValue(servicesNode, new TypeReference<List<Service>>() {});
7585
toolInformation.setServices(services);
76-
} else if (servicesNode.isObject()) {
77-
Service service = mapper.convertValue(servicesNode, Service.class);
78-
toolInformation.setServices(Collections.singletonList(service));
86+
}
87+
// Case XML-like input where "services" contains "component"
88+
else if (servicesNode.isObject() && servicesNode.has("service")) {
89+
JsonNode serviceNode = servicesNode.get("service");
90+
if (serviceNode.isArray()) {
91+
List<Service> services = mapper.convertValue(servicesNode, new TypeReference<List<Service>>() {});
92+
toolInformation.setServices(services);
93+
} else if (serviceNode.isObject()) {
94+
Service service = mapper.convertValue(servicesNode, Service.class);
95+
toolInformation.setServices(Collections.singletonList(service));
96+
}
7997
}
8098
}
8199
}

src/main/java/org/cyclonedx/util/serializer/MetadataSerializer.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.cyclonedx.Version;
1212
import org.cyclonedx.model.Metadata;
1313
import org.cyclonedx.model.Property;
14+
import org.cyclonedx.model.component.evidence.Occurrence;
1415
import org.cyclonedx.model.metadata.ToolInformation;
1516

1617
import static org.cyclonedx.util.serializer.SerializerUtils.shouldSerializeField;
@@ -164,15 +165,14 @@ private <T> void writeArrayFieldJSON(JsonGenerator jsonGenerator, String fieldNa
164165
}
165166

166167
private <T> void writeArrayFieldXML(List<T> items, ToXmlGenerator xmlGenerator, String fieldName) throws IOException {
167-
if (items != null) {
168+
if (CollectionUtils.isNotEmpty(items)) {
168169
xmlGenerator.writeFieldName(fieldName + "s");
169-
xmlGenerator.writeStartArray();
170+
xmlGenerator.writeStartObject();
170171
for (T item : items) {
171-
xmlGenerator.writeStartObject();
172-
xmlGenerator.writeObjectField(fieldName, item);
173-
xmlGenerator.writeEndObject();
172+
xmlGenerator.writeFieldName(fieldName);
173+
xmlGenerator.writeObject(item);
174174
}
175-
xmlGenerator.writeEndArray();
175+
xmlGenerator.writeEndObject();
176176
}
177177
}
178178

src/test/java/org/cyclonedx/BomJsonGeneratorTest.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,32 @@ public void testIssue408Regression_xmlToJson_externalReferenceBom() throws Excep
545545
assertTrue(parser.isValid(loadedFile, version));
546546
}
547547

548+
@Test
549+
public void testIssue562() throws Exception {
550+
Version version = Version.VERSION_16;
551+
Bom bom = createCommonJsonBom("/regression/issue562.json");
552+
553+
554+
BomJsonGenerator generator = BomGeneratorFactory.createJson(version, bom);
555+
File loadedFile = writeToFile(generator.toJsonString());
556+
557+
JsonParser parser = new JsonParser();
558+
assertTrue(parser.isValid(loadedFile, version));
559+
}
560+
561+
@Test
562+
public void testIssue492() throws Exception {
563+
Version version = Version.VERSION_14;
564+
Bom bom = createCommonJsonBom("/regression/issue492.json");
565+
566+
567+
BomJsonGenerator generator = BomGeneratorFactory.createJson(version, bom);
568+
File loadedFile = writeToFile(generator.toJsonString());
569+
570+
JsonParser parser = new JsonParser();
571+
assertTrue(parser.isValid(loadedFile, version));
572+
}
573+
548574
private void assertExternalReferenceInfo(Bom bom) {
549575
assertEquals(3, bom.getExternalReferences().size());
550576
assertEquals(3, bom.getComponents().get(0).getExternalReferences().size());

src/test/java/org/cyclonedx/BomXmlGeneratorTest.java

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@
3232
import org.cyclonedx.model.License;
3333
import org.cyclonedx.model.LicenseChoice;
3434
import org.cyclonedx.model.Metadata;
35+
import org.cyclonedx.model.OrganizationalContact;
3536
import org.cyclonedx.model.Service;
3637
import org.cyclonedx.model.license.Expression;
38+
import org.cyclonedx.model.metadata.ToolInformation;
3739
import org.cyclonedx.parsers.JsonParser;
3840
import org.cyclonedx.parsers.XmlParser;
3941
import org.junit.jupiter.api.AfterEach;
@@ -50,7 +52,9 @@
5052
import java.nio.file.Path;
5153
import java.util.ArrayList;
5254
import java.util.Arrays;
55+
import java.util.LinkedList;
5356
import java.util.List;
57+
import java.util.UUID;
5458
import java.util.stream.Stream;
5559
import java.util.Objects;
5660

@@ -672,11 +676,67 @@ public void testXxeProtection() {
672676

673677
@Test
674678
public void testIssue408Regression_extensibleTypes() throws Exception {
679+
Bom bom = new Bom();
680+
bom.setSerialNumber("urn:uuid:" + UUID.randomUUID());
681+
682+
Metadata meta = new Metadata();
683+
684+
// ToolInformation test
685+
Component tool1 = new Component();
686+
tool1.setType(Component.Type.APPLICATION);
687+
tool1.setName("TOOL 1");
688+
tool1.setVersion("v1");
689+
690+
Component tool2 = new Component();
691+
tool2.setType(Component.Type.APPLICATION);
692+
tool2.setName("TOOL 2");
693+
tool2.setVersion("v2");
694+
695+
ToolInformation tools = new ToolInformation();
696+
List<Component> components = new LinkedList<>();
697+
components.add(tool1);
698+
components.add(tool2);
699+
tools.setComponents(components);
700+
meta.setToolChoice(tools);
701+
702+
// Author test
703+
OrganizationalContact auth1 = new OrganizationalContact();
704+
auth1.setName("Author 1");
705+
meta.addAuthor(auth1);
706+
707+
OrganizationalContact auth2 = new OrganizationalContact();
708+
auth2.setName("Author 2");
709+
meta.addAuthor(auth2);
710+
711+
bom.setMetadata(meta);
712+
713+
BomXmlGenerator generator = BomGeneratorFactory.createXml(Version.VERSION_16, bom);
714+
File loadedFile = writeToFile(generator.toXmlString());
715+
716+
XmlParser parser = new XmlParser();
717+
assertTrue(parser.isValid(loadedFile, Version.VERSION_16));
718+
}
719+
720+
@Test
721+
public void testIssue562() throws Exception {
675722
Version version = Version.VERSION_15;
676-
Bom bom = createCommonBomXml("/regression/issue408-extensible-type.xml");
677-
addExtensibleTypes(bom);
723+
Bom bom = createCommonBomXml("/regression/issue562.xml");
678724

679725
BomXmlGenerator generator = BomGeneratorFactory.createXml(version, bom);
726+
727+
File loadedFile = writeToFile(generator.toXmlString());
728+
729+
XmlParser parser = new XmlParser();
730+
assertTrue(parser.isValid(loadedFile, version));
731+
}
732+
733+
@Test
734+
public void testIssue492() throws Exception {
735+
Version version = Version.VERSION_15;
736+
Bom bom = createCommonBomXml("/regression/issue492.xml");
737+
738+
BomXmlGenerator generator = BomGeneratorFactory.createXml(version, bom);
739+
680740
File loadedFile = writeToFile(generator.toXmlString());
681741

682742
XmlParser parser = new XmlParser();

src/test/java/org/cyclonedx/parsers/JsonParserTest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,4 +568,17 @@ public void schema16_cbom() throws Exception {
568568
.containsAll(Arrays.asList(CryptoFunction.KEYGEN, CryptoFunction.ENCRYPT, CryptoFunction.DECRYPT,
569569
CryptoFunction.TAG)));
570570
}
571+
572+
@Test
573+
public void testIssue562Regression() throws Exception {
574+
final Bom bom = getJsonBom("regression/issue562.json");
575+
assertEquals(2, bom.getMetadata().getToolChoice().getComponents().size());
576+
assertEquals(2, bom.getMetadata().getAuthors().size());
577+
}
578+
579+
@Test
580+
public void testIssue492Regression() throws Exception {
581+
final Bom bom = getJsonBom("regression/issue492.json");
582+
assertEquals(2, bom.getMetadata().getTools().size());
583+
}
571584
}

src/test/java/org/cyclonedx/parsers/XmlParserTest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,4 +713,17 @@ public void schema16_cbom() throws Exception {
713713
.containsAll(Arrays.asList(CryptoFunction.KEYGEN, CryptoFunction.ENCRYPT, CryptoFunction.DECRYPT,
714714
CryptoFunction.TAG)));
715715
}
716+
717+
@Test
718+
public void testIssue562Regression() throws Exception {
719+
final Bom bom = getXmlBom("regression/issue562.xml");
720+
assertEquals(2, bom.getMetadata().getToolChoice().getComponents().size());
721+
assertEquals(2, bom.getMetadata().getAuthors().size());
722+
}
723+
724+
@Test
725+
public void testIssue492Regression() throws Exception {
726+
final Bom bom = getXmlBom("regression/issue492.xml");
727+
assertEquals(2, bom.getMetadata().getTools().size());
728+
}
716729
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"bomFormat":"CycloneDX",
3+
"specVersion":"1.4",
4+
"serialNumber":"urn:uuid:0c81ff2e-d64e-4897-bfa4-2f0f7d8ab767",
5+
"version" : 1,
6+
"metadata" : {
7+
"timestamp":"2024-12-09T21:56:45Z",
8+
"tools": [
9+
{
10+
"name": "tool-a"
11+
},
12+
{
13+
"name": "tool-b"
14+
}
15+
]
16+
}
17+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2+
<bom xmlns="http://cyclonedx.org/schema/bom/1.4" serialNumber="urn:uuid:0c81ff2e-d64e-4897-bfa4-2f0f7d8ab767" version="1">
3+
<metadata>
4+
<timestamp>2024-12-09T21:56:45Z</timestamp>
5+
<tools>
6+
<tool>
7+
<vendor>corp A</vendor>
8+
<name>tool A</name>
9+
<version>1-A</version>
10+
</tool>
11+
<tool>
12+
<vendor>corp B</vendor>
13+
<name>tool B</name>
14+
<version>1-B</version>
15+
</tool>
16+
</tools>
17+
</metadata>
18+
</bom>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"bomFormat":"CycloneDX",
3+
"specVersion":"1.6",
4+
"serialNumber":"urn:uuid:0c81ff2e-d64e-4897-bfa4-2f0f7d8ab767",
5+
"version" : 1,
6+
"metadata" : {
7+
"timestamp":"2024-12-09T21:56:45Z",
8+
"tools" : {
9+
"components" : [ {
10+
"type":"application",
11+
"name":"TOOL 1",
12+
"version":"v1"
13+
}, {
14+
"type":"application",
15+
"name":"TOOL 2",
16+
"version":"v2"
17+
} ]
18+
},
19+
"authors" : [ {
20+
"name":"Author 1"
21+
}, {
22+
"name":"Author 2"
23+
} ]
24+
}
25+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2+
<bom xmlns="http://cyclonedx.org/schema/bom/1.6" serialNumber="urn:uuid:0c81ff2e-d64e-4897-bfa4-2f0f7d8ab767" version="1">
3+
<metadata>
4+
<timestamp>2024-12-09T21:56:45Z</timestamp>
5+
<tools>
6+
<components>
7+
<component type="application">
8+
<name>TOOL 1</name>
9+
<version>v1</version>
10+
</component>
11+
<component type="application">
12+
<name>TOOL 2</name>
13+
<version>v2</version>
14+
</component>
15+
</components>
16+
</tools>
17+
<authors>
18+
<author>
19+
<name>Author 1</name>
20+
</author>
21+
<author>
22+
<name>Author 2</name>
23+
</author>
24+
</authors>
25+
</metadata>
26+
</bom>

0 commit comments

Comments
 (0)