com.metaeffekt.mirror.contents.vulnerability.VulnerableSoftwareTreeNode Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2021-2024 the original author or authors.
*
* 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.
*/
package com.metaeffekt.mirror.contents.vulnerability;
import com.metaeffekt.artifact.analysis.version.curation.VersionContext;
import com.metaeffekt.artifact.analysis.vulnerability.CommonEnumerationUtil;
import org.apache.commons.lang3.ObjectUtils;
import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import us.springett.parsers.cpe.Cpe;
import us.springett.parsers.cpe.exceptions.CpeValidationException;
import java.util.*;
public class VulnerableSoftwareTreeNode {
private static final Logger LOG = LoggerFactory.getLogger(Vulnerability.class);
private final List childNodes = new ArrayList<>();
private final List nodes = new ArrayList<>();
private final String operator;
public VulnerableSoftwareTreeNode(String operator) {
this.operator = operator;
}
public void addChildNode(VulnerableSoftwareTreeNode node) {
this.childNodes.add(node);
}
public void addNode(VulnerableSoftwareVersionRangeCpe software) {
this.nodes.add(software);
}
public List getNodes() {
return nodes;
}
public List getChildNodes() {
return childNodes;
}
public String getOperator() {
return operator;
}
public boolean isAffected(Set cpes, Cpe specificCheckCpe) {
// TODO: implement
throw new RuntimeException("Not implemented");
}
/**
* Checks whether any of the child nodes in the tree affects the CPE passed as a parameter.
* This ignores the operator hierarchy by effectively flattening the tree into a list.
* This is useful in a first assessment step that ignores the hierarchy to collect as many vulnerabilities as
* possible in order to minimise false negatives caused by missing CPE.
*
* @param cpe The CPE to check the nodes for.
* @return Whether any of the child nodes contained the CPE as affected product.
*/
public boolean isAffectedFlat(Cpe cpe) {
final VulnerableSoftwareVersionRangeCpe flatAffectedNode = getFlatAffectedNode(cpe);
return flatAffectedNode != null;
}
public VulnerableSoftwareVersionRangeCpe getFlatAffectedNode(Cpe cpe) {
switch (operator) {
case "OR":
return isNodesFlatAffected(cpe);
case "AND":
if (!childNodes.isEmpty()) {
return isChildNodesFlatAffected(cpe);
} else {
// in rare cases, the NVD seems to violate its own schema and uses AND without child nodes.
// in this case, use the nodes as child nodes.
return isNodesFlatAffected(cpe);
}
default:
throw new RuntimeException("Unknown operator in vulnerable software tree node: " + operator);
}
}
private VulnerableSoftwareVersionRangeCpe isChildNodesFlatAffected(Cpe cpe) {
for (VulnerableSoftwareTreeNode childNode : childNodes) {
final VulnerableSoftwareVersionRangeCpe flatAffectedNode = childNode.getFlatAffectedNode(cpe);
if (flatAffectedNode != null) {
return flatAffectedNode;
}
}
return null;
}
private VulnerableSoftwareVersionRangeCpe isNodesFlatAffected(Cpe cpe) {
for (VulnerableSoftwareVersionRangeCpe node : nodes) {
if (node.isVulnerable() && node.matches(cpe)) {
return node;
}
}
return null;
}
public List getAllCpes() {
final List cpes = new ArrayList<>(nodes);
for (VulnerableSoftwareTreeNode childNode : childNodes) {
cpes.addAll(childNode.getAllCpes());
}
return cpes;
}
public static List getAllCpes(Collection configurations) {
final List cpes = new ArrayList<>();
for (VulnerableSoftwareTreeNode configuration : configurations) {
cpes.addAll(configuration.getAllCpes());
}
return cpes;
}
public JSONObject toJson() {
final JSONObject json = new JSONObject();
final JSONArray cpeMatch = new JSONArray();
for (VulnerableSoftwareVersionRangeCpe vulnerableSoftware : nodes) {
final JSONObject vulnerableSoftwareJson = vulnerableSoftware.toJson();
cpeMatch.put(vulnerableSoftwareJson);
}
final JSONArray children = new JSONArray();
for (VulnerableSoftwareTreeNode child : childNodes) {
children.put(child.toJson());
}
json.put("cpe_match", cpeMatch);
json.put("children", children);
json.put("operator", operator);
return json;
}
public static VulnerableSoftwareTreeNode fromJson(JSONObject json) throws CpeValidationException {
final JSONArray cpeMatch = ObjectUtils.firstNonNull(json.optJSONArray("cpeMatch"), json.optJSONArray("cpe_match"));
final JSONArray children = json.optJSONArray("children");
final String operator = json.getString("operator");
final VulnerableSoftwareTreeNode root = new VulnerableSoftwareTreeNode(operator);
if (cpeMatch != null) {
for (int i = 0; i < cpeMatch.length(); i++) {
final JSONObject cpeMatchJson = cpeMatch.getJSONObject(i);
final boolean vulnerable = cpeMatchJson.optBoolean("vulnerable", true);
final String versionEndExcluding = cpeMatchJson.optString("versionEndExcluding", null);
final String versionEndIncluding = cpeMatchJson.optString("versionEndIncluding", null);
final String versionStartExcluding = cpeMatchJson.optString("versionStartExcluding", null);
final String versionStartIncluding = cpeMatchJson.optString("versionStartIncluding", null);
final Cpe cpe = CommonEnumerationUtil.parseCpe(ObjectUtils.firstNonNull(cpeMatchJson.optString("criteria", null), cpeMatchJson.optString("cpe23Uri", null))).get();
try {
final VulnerableSoftwareVersionRangeCpe node = new VulnerableSoftwareVersionRangeCpe(cpe,
versionStartExcluding, versionStartIncluding, versionEndIncluding, versionEndExcluding,
VersionContext.fromCpe(cpe),
vulnerable
);
root.nodes.add(node);
} catch (Exception e) {
LOG.error("Failed to parse CPE URI on vulnerable software [{}]", cpe, e);
}
}
}
if (children != null) {
for (int i = 0; i < children.length(); i++) {
final VulnerableSoftwareTreeNode child = VulnerableSoftwareTreeNode.fromJson(children.getJSONObject(i));
root.childNodes.add(child);
}
}
return root;
}
public static List fromJson(JSONArray json) throws CpeValidationException {
final List nodes = new ArrayList<>();
for (int i = 0; i < json.length(); i++) {
final VulnerableSoftwareTreeNode node = VulnerableSoftwareTreeNode.fromJson(json.getJSONObject(i));
nodes.add(node);
}
return nodes;
}
@Override
public String toString() {
final StringJoiner sb = new StringJoiner(" " + operator + " ");
if (!childNodes.isEmpty()) {
for (VulnerableSoftwareTreeNode node : childNodes) {
if (node.getNodes().size() == 1) {
sb.add(node.toString());
} else {
sb.add("{" + node + "}");
}
}
} else if (!nodes.isEmpty()) {
for (VulnerableSoftwareVersionRangeCpe node : nodes) {
sb.add(node.toString());
}
}
return sb.toString();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy