Skip to content

Commit e02cc4e

Browse files
committed
Allow multiple Identities in evidence (#382)
Signed-off-by: Alex Alzate <aalzate@sonatype.com>
1 parent 64bf5e1 commit e02cc4e

File tree

11 files changed

+255
-37
lines changed

11 files changed

+255
-37
lines changed

src/main/java/org/cyclonedx/generators/AbstractBomGenerator.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import org.cyclonedx.CycloneDxSchema;
66
import org.cyclonedx.Version;
77
import org.cyclonedx.model.Bom;
8+
import org.cyclonedx.util.serializer.EvidenceSerializer;
89
import org.cyclonedx.util.serializer.InputTypeSerializer;
910
import org.cyclonedx.util.serializer.LifecycleSerializer;
1011
import org.cyclonedx.util.serializer.MetadataSerializer;
@@ -48,5 +49,9 @@ protected void setupObjectMapper(boolean isXml) {
4849
SimpleModule outputTypeModule = new SimpleModule();
4950
outputTypeModule.addSerializer(new OutputTypeSerializer(isXml));
5051
mapper.registerModule(outputTypeModule);
52+
53+
SimpleModule evidenceModule = new SimpleModule();
54+
evidenceModule.addSerializer(new EvidenceSerializer(isXml, getSchemaVersion()));
55+
mapper.registerModule(evidenceModule);
5156
}
5257
}

src/main/java/org/cyclonedx/model/Bom.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -63,35 +63,35 @@ public class Bom extends ExtensibleElement {
6363
@JacksonXmlProperty(isAttribute = true)
6464
private String xmlns;
6565

66-
@VersionFilter(value = Version.VERSION_12)
66+
@VersionFilter(Version.VERSION_12)
6767
private Metadata metadata;
6868

6969
private List<Component> components;
7070

71-
@VersionFilter(value = Version.VERSION_12)
71+
@VersionFilter(Version.VERSION_12)
7272
private List<Service> services;
7373

74-
@VersionFilter(value = Version.VERSION_11)
74+
@VersionFilter(Version.VERSION_11)
7575
private DependencyList dependencies;
7676

77-
@VersionFilter(value = Version.VERSION_11)
77+
@VersionFilter(Version.VERSION_11)
7878
@JsonDeserialize(using = ExternalReferencesDeserializer.class)
7979
private List<ExternalReference> externalReferences;
8080

81-
@VersionFilter(value = Version.VERSION_13)
81+
@VersionFilter(Version.VERSION_13)
8282
private List<Composition> compositions;
8383

84-
@VersionFilter(value = Version.VERSION_15)
84+
@VersionFilter(Version.VERSION_15)
8585
private List<Formula> formulation;
8686

87-
@VersionFilter(value = Version.VERSION_14)
87+
@VersionFilter(Version.VERSION_14)
8888
@JsonDeserialize(using = VulnerabilityDeserializer.class)
8989
private List<Vulnerability> vulnerabilities;
9090

91-
@VersionFilter(value = Version.VERSION_15)
91+
@VersionFilter(Version.VERSION_15)
9292
private List<Annotation> annotations;
9393

94-
@VersionFilter(value = Version.VERSION_13)
94+
@VersionFilter(Version.VERSION_13)
9595
private List<Property> properties;
9696

9797
@JacksonXmlProperty(isAttribute = true)
@@ -107,7 +107,7 @@ public class Bom extends ExtensibleElement {
107107
private String bomFormat;
108108

109109
@JsonOnly
110-
@VersionFilter(value = Version.VERSION_14)
110+
@VersionFilter(Version.VERSION_14)
111111
private Signature signature;
112112

113113
public Metadata getMetadata() {

src/main/java/org/cyclonedx/model/Component.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ public enum Type {
100100
MACHINE_LEARNING_MODEL("machine-learning-model"),
101101
@JsonProperty("data")
102102
DATA("data"),
103-
@VersionFilter(value = Version.VERSION_16)
103+
@VersionFilter(Version.VERSION_16)
104104
@JsonProperty("cryptographic-asset")
105105
CRYPTOGRAPHIC_ASSET("cryptographic-asset");
106106

@@ -185,11 +185,11 @@ public String getScopeName() {
185185
@JsonProperty("data")
186186
private ComponentData data;
187187

188-
@VersionFilter(value = Version.VERSION_16)
188+
@VersionFilter(Version.VERSION_16)
189189
@JsonProperty("cryptoProperties")
190190
private CryptoProperties cryptoProperties;
191191

192-
@VersionFilter(value = Version.VERSION_16)
192+
@VersionFilter(Version.VERSION_16)
193193
@JsonProperty("provides")
194194
private List<String> provides;
195195

src/main/java/org/cyclonedx/model/Copyright.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@
1818
*/
1919
package org.cyclonedx.model;
2020

21+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
22+
import com.fasterxml.jackson.annotation.JsonInclude;
23+
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
24+
25+
@JsonIgnoreProperties(ignoreUnknown = true)
26+
@JsonInclude(JsonInclude.Include.NON_EMPTY)
27+
@JacksonXmlRootElement(localName = "copyright")
2128
public class Copyright {
2229

2330
private String text;

src/main/java/org/cyclonedx/model/Evidence.java

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.cyclonedx.model.component.evidence.Callstack;
3030
import org.cyclonedx.model.component.evidence.Identity;
3131
import org.cyclonedx.model.component.evidence.Occurrence;
32+
import org.cyclonedx.util.deserializer.IdentityDeserializer;
3233
import org.cyclonedx.util.deserializer.LicenseDeserializer;
3334

3435
import java.util.ArrayList;
@@ -45,8 +46,8 @@ public class Evidence
4546

4647
private List<Copyright> copyright;
4748

48-
@VersionFilter(Version.VERSION_15)
49-
private Identity identity;
49+
@VersionFilter(Version.VERSION_16)
50+
private List<Identity> identities;
5051

5152
@VersionFilter(Version.VERSION_15)
5253
private List<Occurrence> occurrences;
@@ -66,6 +67,8 @@ public void setLicenseChoice(LicenseChoice licenseChoice) {
6667
}
6768

6869
@JacksonXmlElementWrapper(useWrapping = false)
70+
@JacksonXmlProperty(localName = "copyright")
71+
@JsonProperty("copyright")
6972
public List<Copyright> getCopyright() {
7073
return copyright;
7174
}
@@ -81,14 +84,6 @@ public void addCopyright(Copyright copyright) {
8184
this.copyright.add(copyright);
8285
}
8386

84-
public Identity getIdentity() {
85-
return identity;
86-
}
87-
88-
public void setIdentity(final Identity identity) {
89-
this.identity = identity;
90-
}
91-
9287
@JsonProperty("occurrences")
9388
@JacksonXmlElementWrapper(localName = "occurrences")
9489
@JacksonXmlProperty(localName = "occurrence")
@@ -107,4 +102,16 @@ public Callstack getCallstack() {
107102
public void setCallstack(final Callstack callstack) {
108103
this.callstack = callstack;
109104
}
105+
106+
@JacksonXmlElementWrapper(useWrapping = false)
107+
@JacksonXmlProperty(localName = "identity")
108+
@JsonProperty("identity")
109+
@JsonDeserialize(using = IdentityDeserializer.class)
110+
public List<Identity> getIdentities() {
111+
return identities;
112+
}
113+
114+
public void setIdentities(final List<Identity> identities) {
115+
this.identities = identities;
116+
}
110117
}

src/main/java/org/cyclonedx/model/component/evidence/Identity.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,16 @@
1818
@JsonPropertyOrder({"field", "confidence", "concludedValue", "methods", "tools"})
1919
public class Identity extends ExtensibleElement
2020
{
21-
public Field field;
21+
private Field field;
2222

23-
public Double confidence;
23+
private Double confidence;
2424

2525
@VersionFilter(Version.VERSION_16)
26-
public String concludedValue;
26+
private String concludedValue;
2727

28-
public List<Method> methods;
28+
private List<Method> methods;
2929

30-
public List<BomReference> tools;
30+
private List<BomReference> tools;
3131

3232
public enum Field {
3333
@JsonProperty("group")

src/main/java/org/cyclonedx/model/component/evidence/Method.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,14 @@
44
import com.fasterxml.jackson.annotation.JsonInclude;
55
import com.fasterxml.jackson.annotation.JsonProperty;
66
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
7-
import org.cyclonedx.model.ExtensibleElement;
7+
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
88

99
@JsonIgnoreProperties(ignoreUnknown = true)
1010
@JsonInclude(JsonInclude.Include.NON_EMPTY)
1111
@JsonPropertyOrder({"technique", "confidence", "value"})
12+
@JacksonXmlRootElement(localName = "method")
1213
public class Method
13-
extends ExtensibleElement
1414
{
15-
1615
private Technique technique;
1716

1817
private Double confidence;

src/main/java/org/cyclonedx/model/component/evidence/Occurrence.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,16 @@ public class Occurrence extends ExtensibleElement
1818

1919
private String location;
2020

21-
@VersionFilter(value = Version.VERSION_16)
21+
@VersionFilter(Version.VERSION_16)
2222
private Integer line;
2323

24-
@VersionFilter(value = Version.VERSION_16)
24+
@VersionFilter(Version.VERSION_16)
2525
private Integer offset;
2626

27-
@VersionFilter(value = Version.VERSION_16)
27+
@VersionFilter(Version.VERSION_16)
2828
private Integer symbol;
2929

30-
@VersionFilter(value = Version.VERSION_16)
30+
@VersionFilter(Version.VERSION_16)
3131
private String additionalContext;
3232

3333
public String getBomRef() {
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
* This file is part of CycloneDX Core (Java).
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-License-Identifier: Apache-2.0
17+
* Copyright (c) OWASP Foundation. All Rights Reserved.
18+
*/
19+
package org.cyclonedx.util.deserializer;
20+
21+
import java.io.IOException;
22+
import java.util.ArrayList;
23+
import java.util.List;
24+
25+
import com.fasterxml.jackson.core.JsonParser;
26+
import com.fasterxml.jackson.databind.DeserializationContext;
27+
import com.fasterxml.jackson.databind.JsonDeserializer;
28+
import com.fasterxml.jackson.databind.JsonNode;
29+
import com.fasterxml.jackson.databind.ObjectMapper;
30+
import com.fasterxml.jackson.databind.node.ArrayNode;
31+
import org.cyclonedx.model.BomReference;
32+
import org.cyclonedx.model.component.evidence.Identity;
33+
import org.cyclonedx.model.component.evidence.Identity.Field;
34+
import org.cyclonedx.model.component.evidence.Method;
35+
36+
public class IdentityDeserializer
37+
extends JsonDeserializer<List<Identity>> {
38+
39+
40+
private final ObjectMapper mapper = new ObjectMapper();
41+
@Override
42+
public List<Identity> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
43+
JsonNode node = jsonParser.getCodec().readTree(jsonParser);
44+
45+
List<Identity> identities = new ArrayList<>();
46+
47+
if(node.has("identity")) {
48+
node = node.get("identity");
49+
}
50+
51+
if (node.isArray()) {
52+
// If the node is an array, deserialize each element individually
53+
for (JsonNode identityNode : node) {
54+
Identity singleIdentity = deserializeSingleIdentity(identityNode);
55+
identities.add(singleIdentity);
56+
}
57+
} else {
58+
// If the node is a single object, deserialize it as a single Identity
59+
identities.add(deserializeSingleIdentity(node));
60+
}
61+
return identities;
62+
}
63+
64+
private Identity deserializeSingleIdentity(JsonNode node) {
65+
Identity identity = new Identity();
66+
67+
if (node.has("field")) {
68+
Field field = mapper.convertValue(node.get("field"), Field.class);
69+
identity.setField(field);
70+
}
71+
72+
if (node.has("confidence")) {
73+
Double confidence = node.get("confidence").asDouble();
74+
identity.setConfidence(confidence);
75+
}
76+
77+
if (node.has("concludedValue")) {
78+
String concludedValue = node.get("concludedValue").asText();
79+
identity.setConcludedValue(concludedValue);
80+
}
81+
82+
if (node.has("methods")) {
83+
JsonNode methodsNode = node.get("methods");
84+
85+
if(methodsNode.has("method")) {
86+
methodsNode = methodsNode.get("method");
87+
}
88+
89+
ArrayNode nodes = (methodsNode.isArray() ? (ArrayNode) methodsNode : new ArrayNode(null).add(methodsNode));
90+
91+
List<Method> methods = new ArrayList<>();
92+
for (JsonNode resolvesNode : nodes) {
93+
Method method = mapper.convertValue(resolvesNode, Method.class);
94+
methods.add(method);
95+
}
96+
identity.setMethods(methods);
97+
}
98+
99+
if (node.has("tools")) {
100+
JsonNode toolsNode = node.get("tools");
101+
102+
if(toolsNode.has("tool")) {
103+
toolsNode = toolsNode.get("tool");
104+
}
105+
106+
ArrayNode nodes = (toolsNode.isArray() ? (ArrayNode) toolsNode : new ArrayNode(null).add(toolsNode));
107+
108+
List<BomReference> tools = new ArrayList<>();
109+
for (JsonNode resolvesNode : nodes) {
110+
BomReference tool = mapper.convertValue(resolvesNode, BomReference.class);
111+
tools.add(tool);
112+
}
113+
114+
identity.setTools(tools);
115+
}
116+
return identity;
117+
}
118+
}

0 commit comments

Comments
 (0)