-
Notifications
You must be signed in to change notification settings - Fork 67
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- added cycloneDX core library - using cycloneDX core for vulnerability parsing
- Loading branch information
1 parent
c984c56
commit a8a83bc
Showing
15 changed files
with
315 additions
and
242 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
185 changes: 78 additions & 107 deletions
185
...c/main/java/com.mercedesbenz.sechub.xraywrapper/report/CycloneDXVulnerabilityBuilder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,145 +1,116 @@ | ||
package com.mercedesbenz.sechub.xraywrapper.report; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
import org.cyclonedx.model.vulnerability.Vulnerability; | ||
import org.cyclonedx.model.vulnerability.Vulnerability.Source; | ||
|
||
import com.fasterxml.jackson.databind.JsonNode; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import com.fasterxml.jackson.databind.node.ArrayNode; | ||
import com.fasterxml.jackson.databind.node.ObjectNode; | ||
|
||
/** | ||
* builds a cycloneDX vulnerability according to CycloneDX standard 1.4 @see <a | ||
* href="https://cyclonedx.org/docs/1.4/json/"/a> examples: <a | ||
* href="https://github.com/CycloneDX/bom-examples/blob/7d529848e2f8bd65d03aec9eab16f139fd445ff4/VEX/Use-Cases/Case-10/vex.json#L48"/a> | ||
* <a | ||
* href="https://github.com/CycloneDX/bom-examples/blob/7d529848e2f8bd65d03aec9eab16f139fd445ff4/VEX/CISA-Use-Cases/Case-7/vex.json#L29"/a> | ||
* <a | ||
* href="https://github.com/CycloneDX/bom-examples/blob/7d529848e2f8bd65d03aec9eab16f139fd445ff4/VEX/Use-Cases/Case-5/vex.json#L35"/a> | ||
*/ | ||
|
||
public class CycloneDXVulnerabilityBuilder { | ||
|
||
private String id; | ||
|
||
private ObjectNode vulnerability; | ||
|
||
private ObjectMapper objectMapper; | ||
|
||
public CycloneDXVulnerabilityBuilder(String id) { | ||
this.id = id; | ||
this.objectMapper = new ObjectMapper(); | ||
this.vulnerability = objectMapper.createObjectNode(); | ||
} | ||
|
||
public String getId() { | ||
return id; | ||
} | ||
Vulnerability vulnerability; | ||
|
||
public void setId(String id) { | ||
this.id = id; | ||
this.vulnerability.put("id", id); | ||
public CycloneDXVulnerabilityBuilder() { | ||
this.vulnerability = new Vulnerability(); | ||
this.vulnerability.setId("default"); | ||
} | ||
|
||
public ObjectNode getVulnerability() { | ||
public Vulnerability getVulnerability() { | ||
return vulnerability; | ||
} | ||
|
||
// adds the bom_ref variable to the CycloneDx vulnerability | ||
public void addBom_ref(String bomref) { | ||
this.vulnerability.put("bom-ref", bomref); | ||
} | ||
|
||
public void addCWE(ArrayNode cwe) { | ||
ArrayNode arrayNode = objectMapper.createArrayNode(); | ||
for (JsonNode node : cwe) { | ||
public void addCWE(ArrayNode cweArray) { | ||
List<Integer> cwes = new ArrayList<>(); | ||
for (JsonNode node : cweArray) { | ||
String s = node.asText(); | ||
if (s.contains("noinfo")) | ||
continue; | ||
try { | ||
s = s.split("-")[1]; | ||
arrayNode.add(Integer.parseInt(s)); | ||
cwes.add(Integer.parseInt(s)); | ||
} catch (NumberFormatException e) { | ||
break; | ||
} | ||
} | ||
this.vulnerability.set("cwes", arrayNode); | ||
this.vulnerability.setCwes(cwes); | ||
} | ||
|
||
public void addSource(String url_string, String name_string) { | ||
ObjectNode source = objectMapper.createObjectNode(); | ||
source.put("url", url_string); | ||
source.put("name", name_string); | ||
this.vulnerability.set("source", source); | ||
public void addSource(String sourceUrl, String sourceName) { | ||
Source source = new Source(); | ||
source.setName(sourceName); | ||
source.setUrl(sourceUrl); | ||
this.vulnerability.setSource(source); | ||
} | ||
|
||
public void addRating(Float score, String severity, String method, String vector, String source_name) { | ||
ArrayNode arrayRating = objectMapper.createArrayNode(); | ||
ObjectNode nestedNode = objectMapper.createObjectNode(); | ||
ObjectNode source = objectMapper.createObjectNode(); | ||
public void addRating(Double score, String severity, String method, String vector, String sourceName) { | ||
Vulnerability.Rating rating = new Vulnerability.Rating(); | ||
|
||
String url = ""; | ||
if (!this.getId().contains("XRAY")) { | ||
url = "https://nvd.nist.gov/vuln/detail/" + this.getId(); | ||
String sourceUrl = ""; | ||
String id = this.vulnerability.getId(); | ||
if (!id.contains("XRAY")) { | ||
sourceUrl = "https://nvd.nist.gov/vuln/detail/" + id; | ||
} | ||
source.put("name", source_name); | ||
source.put("url", url); | ||
nestedNode.set("source", source); | ||
nestedNode.put("score", score); | ||
nestedNode.put("severity", severity); | ||
nestedNode.put("method", method); | ||
nestedNode.put("vector", vector); | ||
|
||
arrayRating.add(nestedNode); | ||
this.vulnerability.set("ratings", arrayRating); | ||
} | ||
|
||
public void addDescription(String description) { | ||
this.vulnerability.put("description", description); | ||
} | ||
Source source = new Source(); | ||
source.setName(sourceName); | ||
source.setUrl(sourceUrl); | ||
rating.setSource(source); | ||
|
||
rating.setScore(score); | ||
|
||
rating.setSeverity(Vulnerability.Rating.Severity.fromString(severity)); | ||
|
||
rating.setMethod(Vulnerability.Rating.Method.fromString(method)); | ||
|
||
rating.setVector(vector); | ||
|
||
this.vulnerability.addRating(rating); | ||
|
||
public void addAnalysis(JsonNode analysis) { | ||
this.vulnerability.set("analysis", analysis); | ||
} | ||
|
||
public void addAffects(String ref, ArrayNode vulnerableVersions, ArrayNode fixedVersions, String purls) { | ||
ObjectNode component = objectMapper.createObjectNode(); | ||
component.put("ref", ref); | ||
ArrayNode versions = objectMapper.createArrayNode(); | ||
ArrayNode affects = objectMapper.createArrayNode(); | ||
|
||
// todo: not correct format ( correct: vers:semver/<1.5.0|>=7.0.) but valid | ||
if (vulnerableVersions != null) | ||
putVersions(vulnerableVersions, versions, "affected", purls); | ||
if (fixedVersions != null) | ||
putVersions(fixedVersions, versions, "unaffected", purls); | ||
|
||
component.set("versions", versions); | ||
affects.add(component); | ||
this.vulnerability.set("affects", affects); | ||
public void addAffects(String ref, ArrayNode vulnerableVersions, ArrayNode fixedVersions) { | ||
Vulnerability.Affect affect = new Vulnerability.Affect(); | ||
List<Vulnerability.Affect> affects = new ArrayList<>(); | ||
List<Vulnerability.Version> versions = new ArrayList<>(); | ||
affect.setRef(ref); | ||
String pkg = ref.split(":")[0]; | ||
transformVersions(vulnerableVersions, "affected", pkg, versions); | ||
transformVersions(fixedVersions, "unaffected", pkg, versions); | ||
affect.setVersions(versions); | ||
affects.add(affect); | ||
this.vulnerability.setAffects(affects); | ||
|
||
} | ||
|
||
private void putVersions(ArrayNode arrayNode, ArrayNode versions, String status, String purls) { | ||
boolean isRange = false; | ||
StringBuilder cycloneVersion = new StringBuilder(purls); | ||
for (JsonNode node : arrayNode) { | ||
// only ≤ and < are used in Xray report | ||
String s = node.asText(); | ||
// versions are a range | ||
if (s.contains("<") | s.contains("≤") | s.contains("All Versions") | s.contains(">") | s.contains("≥")) { | ||
isRange = true; | ||
s = s.replace(" ", ""); | ||
cycloneVersion.append("@").append(s); | ||
} else { | ||
ObjectNode cycloneArrayNode = objectMapper.createObjectNode(); | ||
cycloneArrayNode.put("version", s); | ||
cycloneArrayNode.put("status", status); | ||
versions.add(cycloneArrayNode); | ||
private void transformVersions(ArrayNode securityVersions, String status, String pkg, List<Vulnerability.Version> versions) { | ||
// "All Versions", "< 1.19.10", "1.20.0-0 ≤ Version < 1.20.5", "1.2.34" | ||
// cycloneDX using Version Range Spec (vers) vers:npm/1.2.3|>=2.0.0|<5.0.0 | ||
pkg = "vers:" + pkg + "/"; | ||
if (securityVersions != null) { | ||
for (JsonNode entry : securityVersions) { | ||
String versionString = entry.asText(); | ||
Vulnerability.Version version = new Vulnerability.Version(); | ||
if (versionString.equals("All Versions")) { | ||
version.setRange(pkg + ">=0.0.0"); | ||
|
||
} else if (versionString.contains("Version")) { | ||
versionString = versionString.replace(" ", ""); | ||
String[] split = versionString.split("Version"); | ||
version.setRange(pkg + split[0] + "|" + split[1]); | ||
|
||
} else if (versionString.contains("<") | versionString.contains("≤")) { | ||
versionString = versionString.replace(" ", ""); | ||
version.setRange(pkg + versionString); | ||
|
||
} else { | ||
version.setVersion(versionString); | ||
} | ||
version.setStatus(Vulnerability.Version.Status.fromString(status)); | ||
versions.add(version); | ||
} | ||
} | ||
// not single vulnerable or fixed versions but a range | ||
if (isRange) { | ||
ObjectNode cycloneArrayNode = objectMapper.createObjectNode(); | ||
cycloneArrayNode.put("range", cycloneVersion.toString()); | ||
cycloneArrayNode.put("status", status); | ||
versions.add(cycloneArrayNode); | ||
} | ||
} | ||
} |
Oops, something went wrong.