com.metaeffekt.artifact.analysis.vulnerability.enrichment.vulnerabilitystatus.VulnerabilityStatusReviewedEntry 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.artifact.analysis.vulnerability.enrichment.vulnerabilitystatus;
import com.metaeffekt.artifact.analysis.utils.CustomCollectors;
import com.metaeffekt.artifact.analysis.utils.StringUtils;
import com.metaeffekt.artifact.analysis.vulnerability.enrichment.InventoryAttribute;
import com.metaeffekt.mirror.contents.store.AdvisoryTypeIdentifier;
import com.metaeffekt.mirror.contents.store.AdvisoryTypeStore;
import com.metaeffekt.mirror.contents.store.ContentIdentifierStore;
import com.metaeffekt.mirror.contents.vulnerability.Vulnerability;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.ObjectUtils;
import org.json.JSONArray;
import org.json.JSONObject;
import org.metaeffekt.core.inventory.processor.model.VulnerabilityMetaData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.NonNull;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Getter
public class VulnerabilityStatusReviewedEntry implements Cloneable {
private static final Logger LOG = LoggerFactory.getLogger(VulnerabilityStatusReviewedEntry.class);
private final String id;
@Setter
private String comment;
private final AdvisoryTypeIdentifier> advisor;
public VulnerabilityStatusReviewedEntry(String id, String comment, AdvisoryTypeIdentifier> type) {
this.id = id;
this.comment = comment;
if (type == null) {
final Optional>> response = AdvisoryTypeStore.get().fromEntryIdentifier(id);
if (!response.isPresent()) {
LOG.warn("VulnerabilityStatusReviewedEntry: Unable to parse advisor from id [{}]", id);
this.advisor = null;
} else {
this.advisor = response.get().getIdentifier();
}
} else {
this.advisor = type;
}
if (id == null) {
LOG.warn("VulnerabilityStatusReviewedEntry: Id is null (comment: {}; type: {})", comment, type);
}
}
public VulnerabilityStatusReviewedEntry(String id, String comment) {
this(id, comment, null);
}
public VulnerabilityStatusReviewedEntry(String id) {
this(id, null, null);
}
@Override
public String toString() {
return id + (comment != null && !comment.isEmpty() ? " (" + comment + ")" : "");
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
VulnerabilityStatusReviewedEntry that = (VulnerabilityStatusReviewedEntry) o;
return Objects.equals(id, that.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public VulnerabilityStatusReviewedEntry clone() {
return new VulnerabilityStatusReviewedEntry(id, comment, advisor);
}
/**
* Parses a list of entries that are either a Map or a String.
* [{
* "advisor": "MSRC", // or 'cert' as legacy key
* "comment": "This MSRC alert has been reviewed.",
* "id": "MSRC-CVE-2021-44228"
* },
* "MSRC-CVE-2021-44228 (comment)",
* "MSRC-CVE-2021-44228"]
*
* @param entries The entries to parse.
* @return The parsed entries.
*/
public static List fromMultipleFormattedStringOrMapEntries(List> entries) {
try {
final List parsed = new ArrayList<>();
for (final Object entry : entries) {
if (entry instanceof String) {
parsed.add(fromFormattedString(entry.toString()));
} else if (entry instanceof Map) {
parsed.add(fromMap((Map) entry));
}
}
return parsed;
} catch (Exception e) {
throw new RuntimeException("Unable to parse Vulnerability Status Reviewed Advisory Entries from List of Objects: " + entries, e);
}
}
public static VulnerabilityStatusReviewedEntry fromFormattedString(String entry) {
final String id;
final String comment;
final Pattern valueWithOptionalParenthesisPattern = Pattern.compile("^([^(]+)(?: \\(([^)]+)\\))?$");
final Matcher matcher = valueWithOptionalParenthesisPattern.matcher(entry);
if (matcher.matches()) {
id = matcher.group(1);
comment = matcher.groupCount() == 2 ? matcher.group(2) : null;
} else {
id = null;
comment = null;
}
return new VulnerabilityStatusReviewedEntry(id, comment);
}
public JSONObject toJson() {
JSONObject json = new JSONObject();
json.put("id", this.id);
if (advisor != null) {
json.put("advisor", advisor.getName());
json.put("advisor-implementation", advisor.getImplementation());
}
if (comment != null) {
json.put("comment", comment);
}
return json;
}
public static VulnerabilityStatusReviewedEntry fromMap(Map entry) {
final String id = entry.containsKey("id") ? String.valueOf(entry.get("id")) : null;
final String comment = entry.containsKey("comment") ? String.valueOf(entry.get("comment")) : null;
final Object advisor = ObjectUtils.firstNonNull(
entry.get("advisor"),
entry.get("cert")
);
final Object advisorImplementation = entry.get("advisor-implementation");
final AdvisoryTypeIdentifier> advisory;
if (advisor == null || StringUtils.isEmpty(String.valueOf(advisor))) {
final Optional>> response = AdvisoryTypeStore.get().fromEntryIdentifier(id);
if (!response.isPresent()) {
LOG.warn("VulnerabilityStatusReviewedEntry: Unable to parse advisor from id [{}]", id);
advisory = null;
} else {
advisory = response.get().getIdentifier();
}
} else {
advisory = AdvisoryTypeStore.get().fromNameAndImplementation(String.valueOf(advisor), advisorImplementation == null ? null : String.valueOf(advisorImplementation));
}
return new VulnerabilityStatusReviewedEntry(id, comment, advisory);
}
public static List fromJsonArray(JSONArray jsonArray) {
List entries = new ArrayList<>();
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject jsonObject = jsonArray.getJSONObject(i);
entries.add(fromMap(jsonObject.toMap()));
}
return entries;
}
public static List fromVulnerabilityMetaData(VulnerabilityMetaData vmd) {
if (vmd.has(InventoryAttribute.REVIEWED_ADVISORIES.getKey())) {
return fromJsonArray(new JSONArray(vmd.get(InventoryAttribute.REVIEWED_ADVISORIES.getKey())));
} else {
return new ArrayList<>();
}
}
public static List fromVulnerability(Vulnerability vulnerability) {
if (vulnerability.getAdditionalAttributes().containsKey(InventoryAttribute.REVIEWED_ADVISORIES.getKey())) {
return fromJsonArray(new JSONArray(vulnerability.getAdditionalAttribute(InventoryAttribute.REVIEWED_ADVISORIES.getKey())));
} else {
return new ArrayList<>();
}
}
public static JSONArray toJsonArray(List entries) {
return entries.stream()
.map(VulnerabilityStatusReviewedEntry::toJson)
.collect(CustomCollectors.toJsonArray());
}
public static JSONArray toJsonArray(VulnerabilityStatusReviewedEntry... entries) {
return toJsonArray(Arrays.asList(entries));
}
public static void appendToVulnerabilityMetaData(VulnerabilityMetaData vmd, List entries) {
final List parsed = fromVulnerabilityMetaData(vmd);
parsed.addAll(entries);
vmd.set(InventoryAttribute.REVIEWED_ADVISORIES.getKey(), toJsonArray(parsed).toString());
}
public static void appendToVulnerability(Vulnerability vulnerability, List entries) {
final List parsed = fromVulnerability(vulnerability);
parsed.addAll(entries);
vulnerability.setAdditionalAttribute(InventoryAttribute.REVIEWED_ADVISORIES, toJsonArray(parsed).toString());
}
public static boolean isReviewedEntry(@NonNull String id, List advisories) {
return advisories.stream().anyMatch(a -> id.equals(a.getId()));
}
public static VulnerabilityStatusReviewedEntry findReviewedEntry(@NonNull String id, List advisories) {
return advisories.stream().filter(a -> id.equals(a.getId())).findFirst().orElse(null);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy