com.metaeffekt.artifact.enrichment.configurations.VadDetailLevelMatcher Maven / Gradle / Ivy
/*
* 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.artifact.enrichment.configurations;
import com.metaeffekt.artifact.analysis.vulnerability.CommonEnumerationUtil;
import com.metaeffekt.artifact.analysis.vulnerability.enrichment.vulnerabilitystatus.VulnerabilityStatus;
import com.metaeffekt.artifact.analysis.vulnerability.enrichment.vulnerabilitystatus.VulnerabilityStatusHistoryEntry;
import com.metaeffekt.mirror.contents.vulnerability.Vulnerability;
import org.metaeffekt.core.inventory.processor.configuration.ProcessConfiguration;
import org.metaeffekt.core.inventory.processor.configuration.ProcessMisconfiguration;
import org.metaeffekt.core.inventory.processor.model.Artifact;
import org.metaeffekt.core.inventory.processor.model.VulnerabilityMetaData;
import us.springett.parsers.cpe.Cpe;
import java.util.*;
public class VadDetailLevelMatcher extends ProcessConfiguration {
private final static Set VALID_STATUS_NAMES = new HashSet() {{
add(VulnerabilityMetaData.STATUS_VALUE_APPLICABLE);
add(VulnerabilityMetaData.STATUS_VALUE_NOTAPPLICABLE);
add(VulnerabilityMetaData.STATUS_VALUE_INSIGNIFICANT);
add(VulnerabilityMetaData.STATUS_VALUE_IN_REVIEW);
add(VulnerabilityMetaData.STATUS_VALUE_VOID);
}};
private String status;
private String allCpe;
private String anyCpe;
private String vulnerabilityName;
private List cachedAllCpes;
private List cachedAnyCpes;
public String[] getStatus() {
return status == null ? null : status.split(", ");
}
public List getAllCpe() {
if (cachedAllCpes == null) {
cachedAllCpes = CommonEnumerationUtil.parseCpes(allCpe);
}
return cachedAllCpes;
}
public List getAnyCpe() {
if (cachedAnyCpes == null) {
cachedAnyCpes = CommonEnumerationUtil.parseCpes(anyCpe);
}
return cachedAnyCpes;
}
public String[] getVulnerabilityName() {
return vulnerabilityName == null ? null : vulnerabilityName.split(", ");
}
public VadDetailLevelMatcher setStatus(String status) {
this.status = status;
return this;
}
public VadDetailLevelMatcher setAllCpe(String allCpe) {
this.allCpe = allCpe;
cachedAllCpes = null;
return this;
}
public VadDetailLevelMatcher setAnyCpe(String anyCpe) {
this.anyCpe = anyCpe;
cachedAnyCpes = null;
return this;
}
public VadDetailLevelMatcher setVulnerabilityName(String vulnerabilityName) {
this.vulnerabilityName = vulnerabilityName;
return this;
}
public VadDetailLevelMatcher setVulnerabilityName(String[] vulnerabilityName) {
this.vulnerabilityName = vulnerabilityName == null ? null : String.join(", ", vulnerabilityName);
return this;
}
public boolean matches(Vulnerability vulnerability, VulnerabilityStatus status, Set artifacts) {
final VulnerabilityStatusHistoryEntry latestActiveEntry = status.getLatestActiveStatusHistoryEntry();
if (latestActiveEntry != null) {
if (!isStatusApplicable(latestActiveEntry.getStatus())) {
return false;
}
}
final List allCpe = getAllCpe();
if (allCpe != null && !allCpe.isEmpty()) {
for (Cpe cpe : allCpe) {
boolean cpeMatched = false;
for (Artifact artifact : artifacts) {
final List effectiveArtifactCpes = CommonEnumerationUtil.parseEffectiveCpe(artifact);
for (Cpe effectiveArtifactCpe : effectiveArtifactCpes) {
if (CommonEnumerationUtil.compareCpeUsingWildcardsOneWay(cpe, effectiveArtifactCpe)) {
cpeMatched = true;
break;
}
}
if (cpeMatched) break;
}
if (!cpeMatched) {
return false;
}
}
}
final List anyCpe = getAnyCpe();
if (anyCpe != null && !anyCpe.isEmpty()) {
boolean cpeMatched = false;
for (Artifact artifact : artifacts) {
final List effectiveArtifactCpes = CommonEnumerationUtil.parseEffectiveCpe(artifact);
for (Cpe effectiveArtifactCpe : effectiveArtifactCpes) {
for (Cpe cpe : anyCpe) {
if (CommonEnumerationUtil.compareCpeUsingWildcardsOneWay(cpe, effectiveArtifactCpe)) {
cpeMatched = true;
break;
}
}
if (cpeMatched) break;
}
if (cpeMatched) break;
}
if (!cpeMatched) {
return false;
}
}
final String[] vulnerabilityName = getVulnerabilityName();
if (vulnerabilityName != null && vulnerabilityName.length > 0) {
boolean vulnerabilityNameMatched = false;
for (String name : vulnerabilityName) {
if (vulnerability.getId().contains(name)) {
vulnerabilityNameMatched = true;
break;
}
}
if (!vulnerabilityNameMatched) {
return false;
}
}
return true;
}
public boolean isStatusApplicable(String checkStatus) {
final String[] statusNames = this.getStatus();
if (statusNames == null) return true;
for (String statusName : statusNames) {
if (checkStatus.equals(statusName.trim())) {
return true;
}
}
return false;
}
@Override
public LinkedHashMap getProperties() {
final LinkedHashMap properties = new LinkedHashMap<>();
properties.put("status", status);
properties.put("allCpe", allCpe);
properties.put("anyCpe", anyCpe);
properties.put("vulnerabilityName", vulnerabilityName);
return properties;
}
@Override
public void setProperties(LinkedHashMap properties) {
super.loadStringProperty(properties, "status", this::setStatus);
super.loadStringProperty(properties, "allCpe", this::setAllCpe);
super.loadStringProperty(properties, "anyCpe", this::setAnyCpe);
super.loadStringProperty(properties, "vulnerabilityName", this::setVulnerabilityName);
}
@Override
protected void collectMisconfigurations(List misconfigurations) {
if (status != null) {
final String[] statusNames = this.getStatus();
for (String statusName : statusNames) {
if (!VALID_STATUS_NAMES.contains(statusName.trim())) {
misconfigurations.add(new ProcessMisconfiguration("status", "Invalid status name: [" + statusName + "]. Valid names are: " + VALID_STATUS_NAMES));
}
}
}
}
/**
* matcher: status = "in review"; anyCpe = "cpe:/a:linux:kernel"
. See method in comment block above for more details.
*
* @param configString The configuration string to parse
* @return The parsed matcher
* @see VadDetailLevelConfiguration#fromConfigurationString(String)
*/
public static VadDetailLevelMatcher fromConfigurationString(String configString) {
if (!configString.startsWith("matcher:")) {
throw new IllegalArgumentException("Invalid configuration string, expected 'matcher:' but got '" + configString + "'");
}
final String trimmedConfigString = configString.substring("matcher:".length()).trim();
final Map properties = VadDetailLevelConfiguration.extractPropertiesFromConfigLine(trimmedConfigString);
final VadDetailLevelMatcher matcher = new VadDetailLevelMatcher();
if (properties.containsKey("status")) {
matcher.setStatus(properties.get("status"));
}
if (properties.containsKey("allCpe")) {
matcher.setAllCpe(properties.get("allCpe"));
}
if (properties.containsKey("anyCpe")) {
matcher.setAnyCpe(properties.get("anyCpe"));
}
if (properties.containsKey("vulnerabilityName")) {
matcher.setVulnerabilityName(properties.get("vulnerabilityName"));
}
return matcher;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy