Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,20 @@
import java.util.Objects;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import org.cyclonedx.model.Component;
import org.cyclonedx.model.Service;

@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class ToolInformation
{
@JacksonXmlElementWrapper(localName = "components")
@JacksonXmlProperty(localName = "component")
private List<Component> components;

@JacksonXmlElementWrapper(localName = "services")
@JacksonXmlProperty(localName = "service")
private List<Service> services;

public List<Component> getComponents() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,18 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import org.cyclonedx.model.*;
import org.cyclonedx.Version;
import org.cyclonedx.model.OrganizationalContact;
import org.cyclonedx.model.OrganizationalEntity;
import org.cyclonedx.model.Property;
import org.cyclonedx.model.Tool;
import org.cyclonedx.model.VersionFilter;
import org.cyclonedx.model.metadata.ToolInformation;
import org.cyclonedx.util.deserializer.VulnerabilityDeserializer;
import org.cyclonedx.util.serializer.CustomDateSerializer;

/**
Expand Down Expand Up @@ -61,6 +69,7 @@
"properties"
})
@JsonInclude(JsonInclude.Include.NON_EMPTY)
@JsonDeserialize(using = VulnerabilityDeserializer.class)
public class Vulnerability
{
public Vulnerability() {}
Expand Down Expand Up @@ -99,7 +108,11 @@ public Vulnerability() {}
private Credits credits;
@JacksonXmlElementWrapper(localName = "tools")
@JacksonXmlProperty(localName = "tool")
@Deprecated
private List<Tool> tools;
@JacksonXmlProperty(localName = "tools")
@VersionFilter(org.cyclonedx.Version.VERSION_15)
private ToolInformation toolInformation;
private Analysis analysis;
private List<Affect> affects;
private List<Property> properties;
Expand Down Expand Up @@ -254,6 +267,17 @@ public void setTools(final List<Tool> tools) {
this.tools = tools;
}

@JacksonXmlProperty(localName = "tools")
@JsonProperty("tools")
@VersionFilter(org.cyclonedx.Version.VERSION_15)
public ToolInformation getToolChoice() {
return toolInformation;
}

public void setToolChoice(final ToolInformation toolInformation) {
this.toolInformation = toolInformation;
}

public Analysis getAnalysis() {
return analysis;
}
Expand Down
38 changes: 38 additions & 0 deletions src/main/java/org/cyclonedx/util/TimestampUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* 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;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

@SuppressWarnings("unused")
public final class TimestampUtils {
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");

private TimestampUtils() {}

public static Date parseTimestamp(String text) {
try {
return DATE_FORMAT.parse(text);
} catch (ParseException | NullPointerException e) {
return null;
}
}
}
63 changes: 63 additions & 0 deletions src/main/java/org/cyclonedx/util/ToolsJsonParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* 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;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import org.cyclonedx.model.Tool;
import org.cyclonedx.model.metadata.ToolInformation;
import org.cyclonedx.util.deserializer.ToolInformationDeserializer;
import org.cyclonedx.util.deserializer.ToolsDeserializer;

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

public class ToolsJsonParser {
private final ToolInformationDeserializer toolInformationDeserializer = new ToolInformationDeserializer();
private final ToolsDeserializer toolsDeserializer = new ToolsDeserializer();

private ToolInformation toolInformation;
private List<Tool> tools;

public ToolsJsonParser(JsonNode node, JsonParser parser, DeserializationContext context) throws IOException {
parse(node, parser, context);
}

private void parse(JsonNode node, JsonParser parser, DeserializationContext context) throws IOException {
if (node.has("tools")) {
JsonNode toolsNode = node.get("tools");
JsonParser toolsParser = toolsNode.traverse(parser.getCodec());
toolsParser.nextToken();
if (toolsNode.has("components") || toolsNode.has("services")) {
toolInformation = toolInformationDeserializer.deserialize(toolsParser, context);
} else {
tools = toolsDeserializer.deserialize(toolsParser, context);
}
}
}

public List<Tool> getTools() {
return tools;
}

public ToolInformation getToolInformation() {
return toolInformation;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* 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 com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.core.type.TypeReference;
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 org.cyclonedx.model.vulnerability.Vulnerability;

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

public class AffectDeserializer
extends JsonDeserializer<Vulnerability.Affect>
{
private final ObjectMapper mapper = new ObjectMapper();

@Override
public Vulnerability.Affect deserialize(JsonParser parser, DeserializationContext context) throws IOException {
ObjectCodec codec = parser.getCodec();
JsonNode node = codec.readTree(parser);

Vulnerability.Affect affect = new Vulnerability.Affect();

JsonNode refNode = node.get("ref");
if (refNode != null) {
affect.setRef(refNode.asText());
}

JsonNode versionsNode = node.get("versions");
if (versionsNode != null) {
if (versionsNode.isArray()) {
List<Vulnerability.Version> versions = mapper.convertValue(node.get("versions"), new TypeReference<List<Vulnerability.Version>>() {});
affect.setVersions(versions);
} else if (versionsNode.has("version")) {
JsonNode versionNode = versionsNode.get("version");
if (versionNode.isArray()) {
List<Vulnerability.Version> versions = mapper.convertValue(versionNode, new TypeReference<List<Vulnerability.Version>>() {});
affect.setVersions(versions);
} else {
affect.setVersions(Collections.singletonList(
mapper.convertValue(versionNode, Vulnerability.Version.class)
));
}
}
}

return affect;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* 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 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.vulnerability.Vulnerability;

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

public class AffectsDeserializer
extends JsonDeserializer<List<Vulnerability.Affect>>
{
private final AffectDeserializer affectDeserializer = new AffectDeserializer();
private final ObjectMapper objectMapper = new ObjectMapper();

@Override
public List<Vulnerability.Affect> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
JsonNode node = p.getCodec().readTree(p);
return parseAffects(node.has("target") ? node.get("target") : node, p, ctxt);
}

private List<Vulnerability.Affect> parseAffects(JsonNode node, JsonParser p, DeserializationContext ctxt) throws IOException {
List<Vulnerability.Affect> affects = new ArrayList<>();
ArrayNode nodes = DeserializerUtils.getArrayNode(node, objectMapper);

for (JsonNode affectNode : nodes) {
affects.add(parseAffect(affectNode, p, ctxt));
}

return affects;
}

private Vulnerability.Affect parseAffect(JsonNode node, JsonParser p, DeserializationContext ctxt) throws IOException {
JsonParser affectParser = node.traverse(p.getCodec());
affectParser.nextToken();
return affectDeserializer.deserialize(affectParser, ctxt);
}
}
Loading