Skip to content

Commit

Permalink
Allow multiple Identities in evidence (#382)
Browse files Browse the repository at this point in the history
Signed-off-by: Alex Alzate <aalzate@sonatype.com>
  • Loading branch information
mr-zepol committed Apr 7, 2024
1 parent 64bf5e1 commit e02cc4e
Show file tree
Hide file tree
Showing 11 changed files with 255 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.cyclonedx.CycloneDxSchema;
import org.cyclonedx.Version;
import org.cyclonedx.model.Bom;
import org.cyclonedx.util.serializer.EvidenceSerializer;
import org.cyclonedx.util.serializer.InputTypeSerializer;
import org.cyclonedx.util.serializer.LifecycleSerializer;
import org.cyclonedx.util.serializer.MetadataSerializer;
Expand Down Expand Up @@ -48,5 +49,9 @@ protected void setupObjectMapper(boolean isXml) {
SimpleModule outputTypeModule = new SimpleModule();
outputTypeModule.addSerializer(new OutputTypeSerializer(isXml));
mapper.registerModule(outputTypeModule);

SimpleModule evidenceModule = new SimpleModule();
evidenceModule.addSerializer(new EvidenceSerializer(isXml, getSchemaVersion()));
mapper.registerModule(evidenceModule);
}
}
20 changes: 10 additions & 10 deletions src/main/java/org/cyclonedx/model/Bom.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,35 +63,35 @@ public class Bom extends ExtensibleElement {
@JacksonXmlProperty(isAttribute = true)
private String xmlns;

@VersionFilter(value = Version.VERSION_12)
@VersionFilter(Version.VERSION_12)
private Metadata metadata;

private List<Component> components;

@VersionFilter(value = Version.VERSION_12)
@VersionFilter(Version.VERSION_12)
private List<Service> services;

@VersionFilter(value = Version.VERSION_11)
@VersionFilter(Version.VERSION_11)
private DependencyList dependencies;

@VersionFilter(value = Version.VERSION_11)
@VersionFilter(Version.VERSION_11)
@JsonDeserialize(using = ExternalReferencesDeserializer.class)
private List<ExternalReference> externalReferences;

@VersionFilter(value = Version.VERSION_13)
@VersionFilter(Version.VERSION_13)
private List<Composition> compositions;

@VersionFilter(value = Version.VERSION_15)
@VersionFilter(Version.VERSION_15)
private List<Formula> formulation;

@VersionFilter(value = Version.VERSION_14)
@VersionFilter(Version.VERSION_14)
@JsonDeserialize(using = VulnerabilityDeserializer.class)
private List<Vulnerability> vulnerabilities;

@VersionFilter(value = Version.VERSION_15)
@VersionFilter(Version.VERSION_15)
private List<Annotation> annotations;

@VersionFilter(value = Version.VERSION_13)
@VersionFilter(Version.VERSION_13)
private List<Property> properties;

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

@JsonOnly
@VersionFilter(value = Version.VERSION_14)
@VersionFilter(Version.VERSION_14)
private Signature signature;

public Metadata getMetadata() {
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/org/cyclonedx/model/Component.java
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ public enum Type {
MACHINE_LEARNING_MODEL("machine-learning-model"),
@JsonProperty("data")
DATA("data"),
@VersionFilter(value = Version.VERSION_16)
@VersionFilter(Version.VERSION_16)
@JsonProperty("cryptographic-asset")
CRYPTOGRAPHIC_ASSET("cryptographic-asset");

Expand Down Expand Up @@ -185,11 +185,11 @@ public String getScopeName() {
@JsonProperty("data")
private ComponentData data;

@VersionFilter(value = Version.VERSION_16)
@VersionFilter(Version.VERSION_16)
@JsonProperty("cryptoProperties")
private CryptoProperties cryptoProperties;

@VersionFilter(value = Version.VERSION_16)
@VersionFilter(Version.VERSION_16)
@JsonProperty("provides")
private List<String> provides;

Expand Down
7 changes: 7 additions & 0 deletions src/main/java/org/cyclonedx/model/Copyright.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@
*/
package org.cyclonedx.model;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;

@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_EMPTY)
@JacksonXmlRootElement(localName = "copyright")
public class Copyright {

private String text;
Expand Down
27 changes: 17 additions & 10 deletions src/main/java/org/cyclonedx/model/Evidence.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.cyclonedx.model.component.evidence.Callstack;
import org.cyclonedx.model.component.evidence.Identity;
import org.cyclonedx.model.component.evidence.Occurrence;
import org.cyclonedx.util.deserializer.IdentityDeserializer;
import org.cyclonedx.util.deserializer.LicenseDeserializer;

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

private List<Copyright> copyright;

@VersionFilter(Version.VERSION_15)
private Identity identity;
@VersionFilter(Version.VERSION_16)
private List<Identity> identities;

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

@JacksonXmlElementWrapper(useWrapping = false)
@JacksonXmlProperty(localName = "copyright")
@JsonProperty("copyright")
public List<Copyright> getCopyright() {
return copyright;
}
Expand All @@ -81,14 +84,6 @@ public void addCopyright(Copyright copyright) {
this.copyright.add(copyright);
}

public Identity getIdentity() {
return identity;
}

public void setIdentity(final Identity identity) {
this.identity = identity;
}

@JsonProperty("occurrences")
@JacksonXmlElementWrapper(localName = "occurrences")
@JacksonXmlProperty(localName = "occurrence")
Expand All @@ -107,4 +102,16 @@ public Callstack getCallstack() {
public void setCallstack(final Callstack callstack) {
this.callstack = callstack;
}

@JacksonXmlElementWrapper(useWrapping = false)
@JacksonXmlProperty(localName = "identity")
@JsonProperty("identity")
@JsonDeserialize(using = IdentityDeserializer.class)
public List<Identity> getIdentities() {
return identities;
}

public void setIdentities(final List<Identity> identities) {
this.identities = identities;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@
@JsonPropertyOrder({"field", "confidence", "concludedValue", "methods", "tools"})
public class Identity extends ExtensibleElement
{
public Field field;
private Field field;

public Double confidence;
private Double confidence;

@VersionFilter(Version.VERSION_16)
public String concludedValue;
private String concludedValue;

public List<Method> methods;
private List<Method> methods;

public List<BomReference> tools;
private List<BomReference> tools;

public enum Field {
@JsonProperty("group")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import org.cyclonedx.model.ExtensibleElement;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;

@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_EMPTY)
@JsonPropertyOrder({"technique", "confidence", "value"})
@JacksonXmlRootElement(localName = "method")
public class Method
extends ExtensibleElement
{

private Technique technique;

private Double confidence;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@ public class Occurrence extends ExtensibleElement

private String location;

@VersionFilter(value = Version.VERSION_16)
@VersionFilter(Version.VERSION_16)
private Integer line;

@VersionFilter(value = Version.VERSION_16)
@VersionFilter(Version.VERSION_16)
private Integer offset;

@VersionFilter(value = Version.VERSION_16)
@VersionFilter(Version.VERSION_16)
private Integer symbol;

@VersionFilter(value = Version.VERSION_16)
@VersionFilter(Version.VERSION_16)
private String additionalContext;

public String getBomRef() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* This file is part of CycloneDX Core (Java).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
* Copyright (c) OWASP Foundation. All Rights Reserved.
*/
package org.cyclonedx.util.deserializer;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import org.cyclonedx.model.BomReference;
import org.cyclonedx.model.component.evidence.Identity;
import org.cyclonedx.model.component.evidence.Identity.Field;
import org.cyclonedx.model.component.evidence.Method;

public class IdentityDeserializer
extends JsonDeserializer<List<Identity>> {


private final ObjectMapper mapper = new ObjectMapper();
@Override
public List<Identity> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
JsonNode node = jsonParser.getCodec().readTree(jsonParser);

List<Identity> identities = new ArrayList<>();

if(node.has("identity")) {
node = node.get("identity");
}

if (node.isArray()) {
// If the node is an array, deserialize each element individually
for (JsonNode identityNode : node) {
Identity singleIdentity = deserializeSingleIdentity(identityNode);
identities.add(singleIdentity);
}
} else {
// If the node is a single object, deserialize it as a single Identity
identities.add(deserializeSingleIdentity(node));
}
return identities;
}

private Identity deserializeSingleIdentity(JsonNode node) {
Identity identity = new Identity();

if (node.has("field")) {
Field field = mapper.convertValue(node.get("field"), Field.class);
identity.setField(field);
}

if (node.has("confidence")) {
Double confidence = node.get("confidence").asDouble();
identity.setConfidence(confidence);
}

if (node.has("concludedValue")) {
String concludedValue = node.get("concludedValue").asText();
identity.setConcludedValue(concludedValue);
}

if (node.has("methods")) {
JsonNode methodsNode = node.get("methods");

if(methodsNode.has("method")) {
methodsNode = methodsNode.get("method");
}

ArrayNode nodes = (methodsNode.isArray() ? (ArrayNode) methodsNode : new ArrayNode(null).add(methodsNode));

List<Method> methods = new ArrayList<>();
for (JsonNode resolvesNode : nodes) {
Method method = mapper.convertValue(resolvesNode, Method.class);
methods.add(method);
}
identity.setMethods(methods);
}

if (node.has("tools")) {
JsonNode toolsNode = node.get("tools");

if(toolsNode.has("tool")) {
toolsNode = toolsNode.get("tool");
}

ArrayNode nodes = (toolsNode.isArray() ? (ArrayNode) toolsNode : new ArrayNode(null).add(toolsNode));

List<BomReference> tools = new ArrayList<>();
for (JsonNode resolvesNode : nodes) {
BomReference tool = mapper.convertValue(resolvesNode, BomReference.class);
tools.add(tool);
}

identity.setTools(tools);
}
return identity;
}
}
Loading

0 comments on commit e02cc4e

Please sign in to comment.