com.metaeffekt.mirror.contents.vulnerability.Vulnerability 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.mirror.contents.vulnerability;
import com.metaeffekt.artifact.analysis.utils.CustomCollectors;
import com.metaeffekt.artifact.analysis.utils.FileUtils;
import com.metaeffekt.artifact.analysis.utils.StringUtils;
import com.metaeffekt.artifact.analysis.utils.TimeUtils;
import com.metaeffekt.artifact.analysis.vulnerability.enrichment.InventoryAttribute;
import com.metaeffekt.artifact.analysis.vulnerability.enrichment.keywords.KeywordSet;
import com.metaeffekt.artifact.analysis.vulnerability.enrichment.score.VulnerabilityPriorityCalculator;
import com.metaeffekt.artifact.analysis.vulnerability.enrichment.vulnerabilitystatus.VulnerabilityStatus;
import com.metaeffekt.artifact.analysis.vulnerability.enrichment.vulnerabilitystatus.VulnerabilityStatusConverter;
import com.metaeffekt.mirror.contents.advisory.AdvisoryEntry;
import com.metaeffekt.mirror.contents.base.CvssConditionAttributes;
import com.metaeffekt.mirror.contents.base.MatchableDetailsAmbDataClass;
import com.metaeffekt.mirror.contents.base.Reference;
import com.metaeffekt.mirror.contents.epss.EpssData;
import com.metaeffekt.mirror.contents.kev.KevData;
import com.metaeffekt.mirror.contents.store.AdvisoryTypeIdentifier;
import com.metaeffekt.mirror.contents.store.OtherTypeStore;
import com.metaeffekt.mirror.contents.store.VulnerabilityTypeIdentifier;
import com.metaeffekt.mirror.contents.store.VulnerabilityTypeStore;
import com.metaeffekt.mirror.query.VulnerabilityIndexQuery;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.TextField;
import org.json.JSONArray;
import org.json.JSONObject;
import org.metaeffekt.core.inventory.processor.model.Artifact;
import org.metaeffekt.core.inventory.processor.model.VulnerabilityMetaData;
import org.metaeffekt.core.inventory.processor.report.configuration.CentralSecurityPolicyConfiguration;
import org.metaeffekt.core.security.cvss.CvssSource;
import org.metaeffekt.core.security.cvss.CvssVector;
import org.metaeffekt.core.security.cvss.KnownCvssEntities;
import org.metaeffekt.core.security.cvss.processor.CvssSelectionResult;
import org.metaeffekt.core.security.cvss.processor.CvssSelectionResult.CvssScoreVersionSelectionPolicy;
import org.metaeffekt.core.security.cvss.processor.CvssSelector;
import org.metaeffekt.core.security.cvss.processor.CvssVectorSet;
import org.metaeffekt.core.security.cvss.v2.Cvss2;
import org.metaeffekt.core.security.cvss.v3.Cvss3P1;
import org.metaeffekt.core.security.cvss.v4P0.Cvss4P0;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yaml.snakeyaml.Yaml;
import us.springett.parsers.cpe.Cpe;
import us.springett.parsers.cpe.exceptions.CpeValidationException;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.*;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.stream.Collectors;
public class Vulnerability extends MatchableDetailsAmbDataClass {
private static final Logger LOG = LoggerFactory.getLogger(Vulnerability.class);
private final static Set CONVERSION_KEYS_AMB = new HashSet(MatchableDetailsAmbDataClass.CONVERSION_KEYS_AMB) {{
addAll(Arrays.asList(
VulnerabilityMetaData.Attribute.NAME.getKey(),
VulnerabilityMetaData.Attribute.SOURCE.getKey(),
VulnerabilityMetaData.Attribute.SOURCE_IMPLEMENTATION.getKey(),
InventoryAttribute.DESCRIPTION.getKey(),
VulnerabilityMetaData.Attribute.URL.getKey(),
InventoryAttribute.VULNERABILITY_UPDATED_DATE_TIMESTAMP.getKey(),
InventoryAttribute.VULNERABILITY_CREATED_DATE_TIMESTAMP.getKey(),
VulnerabilityMetaData.Attribute.REFERENCES.getKey(),
VulnerabilityMetaData.Attribute.WEAKNESS.getKey(),
InventoryAttribute.TAGS.getKey(),
InventoryAttribute.VULNERABILITY_STATUS.getKey()
));
}};
private final static Set CONVERSION_KEYS_MAP = new HashSet(MatchableDetailsAmbDataClass.CONVERSION_KEYS_MAP) {{
addAll(Arrays.asList(
"name", "description", "notes", "url", "cvssVectors", "createDate", "updateDate", "cwe",
"vulnerable_software", "references", "tags", "vulnerabilityStatus",
"cvss", "kevData", "epssData"
));
}};
/**
* Stores what provider this vulnerability originates from.
* Must be defined on all vulnerability instances.
*/
protected VulnerabilityTypeIdentifier> sourceIdentifier;
@Getter
@Setter
private String description;
@Getter
@Setter
private String notes;
@Setter
private String url;
@Getter
@Setter
protected Date createDate;
@Getter
@Setter
protected Date updateDate;
private final CvssVectorSet cvssVectors = new CvssVectorSet();
private CvssSelectionResult cvssSelectionResult;
@Getter
private final Set references = new LinkedHashSet<>();
@Getter
private final Set cwes = new LinkedHashSet<>();
@Getter
@Setter
private EpssData epssData;
@Getter
private final Set vulnerableSoftwareConfigurations = new HashSet<>();
@Getter
private final Set securityAdvisories = new LinkedHashSet<>();
private KevData kevData;
@Getter
private final Set tags = new LinkedHashSet<>();
@Getter
@Setter
private VulnerabilityStatus vulnerabilityStatus;
public final static Comparator UPDATE_CREATE_TIME_COMPARATOR = Comparator
.comparing(Vulnerability::getUpdateDate)
.thenComparing(Vulnerability::getCreateDate);
public Vulnerability() {
}
public Vulnerability(String id) {
super.setId(id);
}
public void setSourceIdentifier(VulnerabilityTypeIdentifier> source) {
if (source == null) {
throw new IllegalArgumentException("Advisory source must not be null");
}
if (LOG.isDebugEnabled() && source != this.sourceIdentifier) {
if (this.sourceIdentifier != null) {
LOG.warn("Explicitly assigned source differs from originally assigned [{}] --> [{}]",
this.sourceIdentifier.toExtendedString(), source.toExtendedString());
}
}
this.sourceIdentifier = source;
}
@Override
public VulnerabilityTypeIdentifier> getSourceIdentifier() {
return sourceIdentifier;
}
public String getUrl() {
if (url != null) {
return url;
} else {
if (id.startsWith("CVE-")) {
return "https://nvd.nist.gov/vuln/detail/" + id;
} else {
return null;
}
}
}
public boolean isCreatedAfter(Date date) {
return createDate != null && createDate.after(date);
}
public boolean isCreatedBefore(Date date) {
return createDate != null && createDate.before(date);
}
public boolean isUpdatedAfter(Date date) {
return updateDate != null && updateDate.after(date);
}
public boolean isUpdatedBefore(Date date) {
return updateDate != null && updateDate.before(date);
}
public boolean hasBeenUpdatedSince(long millis) {
return (updateDate != null && updateDate.getTime() > millis) || (createDate != null && createDate.getTime() > millis);
}
public void addReference(Reference reference) {
this.references.add(reference);
}
public void addReferences(Collection references) {
this.references.addAll(references);
}
public void addCwe(String cwe) {
if (cwe != null) {
this.cwes.add(cwe);
}
}
public void addCwe(String... cwe) {
addCwes(Arrays.asList(cwe));
}
private void addCwes(Collection cwes) {
cwes.forEach(this::addCwe);
}
public void addVulnerableSoftware(VulnerableSoftwareVersionRangeCpe vulnerableSoftware) {
final VulnerableSoftwareTreeNode node = new VulnerableSoftwareTreeNode("OR");
node.addNode(vulnerableSoftware);
this.vulnerableSoftwareConfigurations.add(node);
}
public void addVulnerableSoftwares(Collection vulnerableSoftware) {
vulnerableSoftware.forEach(this::addVulnerableSoftware);
}
public void addVulnerableSoftwareTreeNode(VulnerableSoftwareTreeNode vulnerableSoftware) {
this.vulnerableSoftwareConfigurations.add(vulnerableSoftware);
}
public void addVulnerableSoftwaresTreeNodes(Collection vulnerableSoftware) {
vulnerableSoftware.forEach(this::addVulnerableSoftwareTreeNode);
}
public boolean cpeFlatMatchesVulnerableSoftware(Cpe cpe) {
return vulnerableSoftwareConfigurations.stream()
.anyMatch(configuration -> configuration.isAffectedFlat(cpe));
}
public VulnerableSoftwareVersionRangeCpe getCpeFlatMatchedVulnerableSoftware(Cpe cpe) {
return vulnerableSoftwareConfigurations.stream()
.map(configuration -> configuration.getFlatAffectedNode(cpe))
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
}
public Optional optVulnerabilityStatus() {
return Optional.ofNullable(vulnerabilityStatus);
}
public VulnerabilityStatus getOrCreateNewVulnerabilityStatus() {
if (vulnerabilityStatus == null) {
vulnerabilityStatus = new VulnerabilityStatus();
}
return vulnerabilityStatus;
}
public void addTag(String tag) {
this.tags.add(tag);
}
public void addTags(Collection tags) {
this.tags.addAll(tags);
}
public boolean hasTag(String tag) {
return this.tags.contains(tag);
}
/* SECURITY ADVISORIES */
public Map, Set> deepCopyReferencedSecurityAdvisories() {
final Map, Set> copy = new HashMap<>();
synchronized (this.referencedSecurityAdvisories) {
for (Map.Entry, Set> referencedEntry : referencedSecurityAdvisories.entrySet()) {
copy.put(referencedEntry.getKey(), new HashSet<>(referencedEntry.getValue()));
}
}
return copy;
}
public void addSecurityAdvisory(AdvisoryEntry advisoryEntry) {
this.securityAdvisories.add(advisoryEntry);
this.addReferencedSecurityAdvisory(advisoryEntry);
}
public void removeSecurityAdvisory(AdvisoryEntry advisoryEntry) {
this.securityAdvisories.remove(advisoryEntry);
this.removeReferencedSecurityAdvisory(advisoryEntry);
}
public Set getRelatedAdvisors(AdvisoryTypeIdentifier> type) {
return getRelatedAdvisors(type, this.securityAdvisories);
}
public static Set getRelatedAdvisors(AdvisoryTypeIdentifier> type, Collection advisoryProviders) {
if (type == null) {
return Collections.emptySet();
}
return advisoryProviders.stream()
.filter(advisory -> advisory.getSourceIdentifier() == type)
.collect(Collectors.toSet());
}
public List getRelatedAdvisors(AdvisoryTypeIdentifier> type, Class typeClass) {
if (type == null || typeClass == null) {
return Collections.emptyList();
}
return getRelatedAdvisors(type).stream()
.filter(typeClass::isInstance)
.map(typeClass::cast)
.collect(Collectors.toList());
}
public Set> getRelatedAdvisorsTypes() {
return securityAdvisories.stream()
.map(AdvisoryEntry::getSourceIdentifier)
.collect(Collectors.toSet());
}
public void setKevData(KevData kevData) {
this.kevData = kevData;
}
public KevData getKevData() {
return kevData;
}
/* CVSS */
public CvssVectorSet getCvssVectors() {
return this.cvssVectors;
}
/**
* This method will only return a meaningful result if the instance has been created via the
* {@link com.metaeffekt.mirror.contents.base.VulnerabilityContextInventory} class or if the
* {@link #securityAdvisories} and {@link #affectedArtifacts} have manually been filled appropriately.
*
* Effective in this context means that the {@link CvssVector}s from the {@link #securityAdvisories} are
* added to the {@link #cvssVectors} if the {@link CvssVector#getApplicabilityCondition()} is met.
* To determine whether this is the case, several factors like MSRC information from the affected artifacts are
* taken into account.
*
* Consider using {@link #selectEffectiveCvssVectors(CvssSelector, CvssSelector, List)} to pre-calculate the effective
* {@link CvssVector}s if you expect to call this method multiple times to reduce the overhead of the calculation.
*
* @return a {@link CvssVectorSet} containing the effective {@link CvssVector}s for this vulnerability.
*/
public CvssVectorSet calculateEffectiveCvssVectors() {
return calculateEffectiveCvssVectors(this.cvssVectors);
}
public CvssVectorSet calculateEffectiveCvssVectors(CvssVectorSet cvssVectors) {
final CvssVectorSet effectiveCvssVectors = new CvssVectorSet();
// add all cvss vectors from the vulnerability itself
effectiveCvssVectors.addAllCvssVectors(cvssVectors);
// add all cvss vectors from the security advisories that are applicable
for (AdvisoryEntry advisoryEntry : this.securityAdvisories) {
for (CvssVector sourcedCvssVector : advisoryEntry.getCvssVectors().getCvssVectors()) {
if (isCvssVectorApplicable(sourcedCvssVector.getApplicabilityCondition())) {
effectiveCvssVectors.addCvssVector(sourcedCvssVector);
}
}
}
return effectiveCvssVectors;
}
protected boolean isCvssVectorApplicable(JSONObject applicabilityCondition) {
if (applicabilityCondition == null || applicabilityCondition.isEmpty()) {
return true;
}
final Object findMsProductIdsObj = applicabilityCondition.opt(CvssConditionAttributes.MATCHES_ON_MS_PRODUCT_ID);
if (findMsProductIdsObj instanceof JSONArray) {
final List findMsProductIds = ((JSONArray) findMsProductIdsObj).toList().stream().map(String::valueOf).collect(Collectors.toList());
boolean foundMatchingArtifact = false;
for (Artifact artifact : this.getAffectedArtifactsByDefaultKey()) {
final String msProductId = artifact.get(InventoryAttribute.MS_PRODUCT_ID);
if (msProductId == null) {
continue;
}
final List artifactMsProductIds = Arrays.asList(msProductId.split(", "));
if (artifactMsProductIds.stream().anyMatch(findMsProductIds::contains)) {
foundMatchingArtifact = true;
break;
}
}
if (!foundMatchingArtifact) {
return false;
}
}
return true;
}
public CvssSelectionResult selectEffectiveCvssVectors(CvssVectorSet cvssVectorSet, CvssSelector baseSelector, CvssSelector effectiveSelector, List versionSelectionPolicy) {
return new CvssSelectionResult(
cvssVectorSet,
baseSelector, effectiveSelector,
versionSelectionPolicy
);
}
public CvssSelectionResult selectEffectiveCvssVectors(CvssVectorSet cvssVectorSet, CentralSecurityPolicyConfiguration config) {
return selectEffectiveCvssVectors(
cvssVectorSet,
config.getInitialCvssSelector(), config.getContextCvssSelector(),
config.getCvssVersionSelectionPolicy()
);
}
public void selectEffectiveCvssVectors(CvssSelector baseSelector, CvssSelector effectiveSelector, List versionSelectionPolicy) {
this.cvssSelectionResult = selectEffectiveCvssVectors(
this.calculateEffectiveCvssVectors(),
baseSelector, effectiveSelector,
versionSelectionPolicy
);
}
public void selectEffectiveCvssVectors(CentralSecurityPolicyConfiguration config) {
selectEffectiveCvssVectors(
config.getInitialCvssSelector(), config.getContextCvssSelector(),
config.getCvssVersionSelectionPolicy()
);
}
public boolean isCvssSelectionResultAvailable() {
return this.cvssSelectionResult != null;
}
public CvssSelectionResult getCvssSelectionResult() {
if (!isCvssSelectionResultAvailable()) {
throw new IllegalStateException("No cvss selection result available. Please call selectEffectiveCvssVectors() first, or use the getCvssSelectionResult(CentralSecurityPolicyConfiguration) method to select the effective cvss vectors on the fly.");
}
return cvssSelectionResult;
}
public CvssSelectionResult getCvssSelectionResult(CentralSecurityPolicyConfiguration config) {
if (!isCvssSelectionResultAvailable()) {
if (config == null) {
throw new IllegalStateException("No cvss selection result available and passed CentralSecurityPolicyConfiguration is null, cannot select cvss vectors on the fly.");
}
selectEffectiveCvssVectors(config);
}
return cvssSelectionResult;
}
public void clearCvssSelectionResult() {
this.cvssSelectionResult = null;
}
public void mapCvssSelectionResult(Function mapper) {
if (this.cvssSelectionResult == null) {
throw new IllegalStateException("No cvss selection result available. Please call selectEffectiveCvssVectors() first, or use the getCvssSelectionResult(CentralSecurityPolicyConfiguration) method to select the effective cvss vectors on the fly.");
}
this.cvssSelectionResult = mapper.apply(this.cvssSelectionResult);
}
public List parseKeywords() {
return KeywordSet.fromVulnerability(this);
}
public VulnerabilityPriorityCalculator.PriorityScoreResult calculatePriorityScore(CentralSecurityPolicyConfiguration config) {
return new VulnerabilityPriorityCalculator().contribute(this).calculatePriorityScore(config);
}
/* CLEANING DATA */
public void clearNonTransferableStatusDetails() {
this.setAdditionalAttribute(VulnerabilityMetaData.Attribute.STATUS, null);
this.setAdditionalAttribute(VulnerabilityMetaData.Attribute.RATIONALE, null);
this.setAdditionalAttribute(VulnerabilityMetaData.Attribute.RISK, null);
this.setAdditionalAttribute(InventoryAttribute.STATUS_ACCEPTED, null);
this.setAdditionalAttribute(InventoryAttribute.STATUS_REPORTED, null);
this.setAdditionalAttribute(InventoryAttribute.STATUS_HISTORY, null);
this.setAdditionalAttribute(InventoryAttribute.STATUS_TITLE, null);
this.setAdditionalAttribute(InventoryAttribute.REVIEWED_ADVISORIES, null);
}
/* TYPE CONVERSION METHODS */
@Override
public VulnerabilityMetaData constructBaseModel() {
return new VulnerabilityMetaData();
}
@Override
public Vulnerability constructDataClass() {
return new Vulnerability();
}
@Override
protected Set conversionKeysAmb() {
return CONVERSION_KEYS_AMB;
}
@Override
protected Set conversionKeysMap() {
return CONVERSION_KEYS_MAP;
}
public static Vulnerability fromVulnerabilityMetaData(VulnerabilityMetaData vmd) {
if (vmd == null) {
return null;
}
return new Vulnerability()
.performAction(v -> v.appendFromBaseModel(vmd));
}
public static Vulnerability fromInputMap(Map map) {
if (map == null) {
return null;
}
return new Vulnerability()
.performAction(v -> v.appendFromMap(map));
}
public static Vulnerability fromJson(JSONObject json) {
if (json == null) {
return null;
}
return fromInputMap(json.toMap());
}
public static Vulnerability fromDocument(Document document) {
if (document == null) {
return null;
}
return new Vulnerability()
.performAction(v -> v.appendFromDocument(document));
}
@Override
public void appendFromBaseModel(VulnerabilityMetaData vmd) {
super.appendFromBaseModel(vmd);
this.setId(vmd.get(VulnerabilityMetaData.Attribute.NAME));
final String vulnerabilitySource = vmd.get(VulnerabilityMetaData.Attribute.SOURCE);
final String vulnerabilitySourceImplementation = vmd.get(VulnerabilityMetaData.Attribute.SOURCE_IMPLEMENTATION);
if (StringUtils.hasText(vulnerabilitySource) || StringUtils.hasText(vulnerabilitySourceImplementation)) {
this.setSourceIdentifier(VulnerabilityTypeStore.get().fromNameAndImplementation(vulnerabilitySource, vulnerabilitySourceImplementation));
} else {
VulnerabilityTypeStore.get().inferSourceIdentifierFromIdIfAbsent(this);
}
this.setDescription(vmd.get(InventoryAttribute.DESCRIPTION.getKey()));
this.setUrl(vmd.get(VulnerabilityMetaData.Attribute.URL));
CvssSource.fromMultipleColumnHeaderStrings(vmd.getAttributes()).forEach((header, source) -> {
if (StringUtils.hasText(vmd.get(header))) {
this.cvssVectors.addCvssVector(source, vmd.get(header));
}
});
if (StringUtils.hasText(vmd.get(InventoryAttribute.VULNERABILITY_UPDATED_DATE_TIMESTAMP.getKey()))) {
this.setUpdateDate(new Date(Long.parseLong(vmd.get(InventoryAttribute.VULNERABILITY_UPDATED_DATE_TIMESTAMP.getKey()))));
}
if (StringUtils.hasText(vmd.get(InventoryAttribute.VULNERABILITY_CREATED_DATE_TIMESTAMP.getKey()))) {
this.setCreateDate(new Date(Long.parseLong(vmd.get(InventoryAttribute.VULNERABILITY_CREATED_DATE_TIMESTAMP.getKey()))));
}
if (StringUtils.hasText(vmd.get(VulnerabilityMetaData.Attribute.REFERENCES))) {
final String referencesString = vmd.get(VulnerabilityMetaData.Attribute.REFERENCES);
if (referencesString.startsWith("[")) {
final List references = Reference.fromJsonArray(new JSONArray(referencesString));
this.addReferences(references);
} else {
for (String ref : referencesString.split(", ?")) {
final Matcher matcher = Reference.REFERENCE_STRING_PATTERN.matcher(ref);
if (matcher.matches()) {
this.addReference(Reference.fromTitleAndUrl(matcher.group(1) + "(" + matcher.group(3) + ")", matcher.group(2)));
} else {
this.addReference(Reference.fromUrl(ref));
}
}
}
}
if (vmd.get(InventoryAttribute.KEV_DATA) != null) {
this.setKevData(KevData.fromJson(new JSONObject(vmd.get(InventoryAttribute.KEV_DATA))));
}
if (StringUtils.hasText(vmd.get(VulnerabilityMetaData.Attribute.WEAKNESS))) {
this.addCwes(Arrays.asList(vmd.get(VulnerabilityMetaData.Attribute.WEAKNESS).split(", ?")));
}
if (vmd.get(InventoryAttribute.EPSS_DATA) != null) {
this.setEpssData(EpssData.fromJson(new JSONObject(vmd.get(InventoryAttribute.EPSS_DATA))));
}
final String tagsString = vmd.get(InventoryAttribute.TAGS.getKey());
if (StringUtils.hasText(tagsString)) {
this.addTags(Arrays.asList(tagsString.split(", ")));
}
if (StringUtils.hasText(vmd.get(InventoryAttribute.VULNERABILITY_STATUS))) {
this.setVulnerabilityStatus(VulnerabilityStatusConverter.fromJson(new JSONObject(vmd.get(InventoryAttribute.VULNERABILITY_STATUS))));
}
}
@Override
public void appendToBaseModel(VulnerabilityMetaData vmd) {
super.appendToBaseModel(vmd);
if (this.url != null) {
vmd.set(VulnerabilityMetaData.Attribute.URL, this.url);
} else if (StringUtils.hasText(this.id) && this.id.startsWith("CVE-")) {
vmd.set(VulnerabilityMetaData.Attribute.URL, "https://nvd.nist.gov/vuln/detail/" + this.id);
} else {
vmd.set(VulnerabilityMetaData.Attribute.URL, null);
}
if (!this.cwes.isEmpty()) {
vmd.set(VulnerabilityMetaData.Attribute.WEAKNESS, String.join(", ", this.cwes));
} else {
vmd.set(VulnerabilityMetaData.Attribute.WEAKNESS, null);
}
if (this.epssData != null) {
vmd.set(InventoryAttribute.EPSS_DATA, this.epssData.toJson().toString());
} else {
vmd.set(InventoryAttribute.EPSS_DATA, null);
}
for (CvssVector entry : this.cvssVectors.getCvssVectors()) {
if (entry.getCvssSource() == null) {
LOG.warn("Using NVD-CNA-NVD for cvss vector [{}] for vulnerability [{}] as it has no source.", entry, this.id);
vmd.set(new CvssSource(KnownCvssEntities.NVD, CvssSource.CvssIssuingEntityRole.CNA, KnownCvssEntities.NVD, entry.getClass()).toColumnHeaderString(), entry.toString());
} else {
vmd.set(entry.getCvssSource().toColumnHeaderString(), entry.toString());
}
}
vmd.set(VulnerabilityMetaData.Attribute.SCORE_CONTEXT_OVERALL, null);
vmd.set(VulnerabilityMetaData.Attribute.SCORE_INITIAL_OVERALL, null);
vmd.set(VulnerabilityMetaData.Attribute.SCORE_INITIAL_OVERALL_SEVERITY, null);
vmd.set(VulnerabilityMetaData.Attribute.SCORE_CONTEXT_OVERALL_SEVERITY, null);
vmd.set(VulnerabilityMetaData.Attribute.SCORE_BASE, null);
vmd.set(VulnerabilityMetaData.Attribute.SCORE_EXPLOITABILITY, null);
vmd.set(VulnerabilityMetaData.Attribute.SCORE_IMPACT, null);
if (this.isCvssSelectionResultAvailable()) {
final CvssVector selectedContextCvss = this.cvssSelectionResult.getSelectedContextCvss();
final CvssVector selectedInitialCvss = this.cvssSelectionResult.getSelectedInitialCvss();
final CvssVector contextOrInitial = this.cvssSelectionResult.getSelectedContextIfAvailableOtherwiseInitial();
if (selectedInitialCvss != null) {
vmd.set(VulnerabilityMetaData.Attribute.SCORE_INITIAL_OVERALL, String.valueOf(selectedInitialCvss.getBakedScores().getOverallScore()));
vmd.set(VulnerabilityMetaData.Attribute.SCORE_INITIAL_SELECTION, String.valueOf(selectedInitialCvss.toJson()));
}
if (selectedContextCvss != null) {
vmd.set(VulnerabilityMetaData.Attribute.SCORE_CONTEXT_OVERALL, String.valueOf(selectedContextCvss.getBakedScores().getOverallScore()));
vmd.set(VulnerabilityMetaData.Attribute.SCORE_CONTEXT_SELECTION, String.valueOf(selectedContextCvss.toJson()));
}
if (contextOrInitial != null) {
if (!Double.isNaN(contextOrInitial.getBakedScores().getBaseScore())) {
vmd.set(VulnerabilityMetaData.Attribute.SCORE_BASE, String.valueOf(contextOrInitial.getBakedScores().getBaseScore()));
}
if (!Double.isNaN(contextOrInitial.getBakedScores().getExploitabilityScore())) {
vmd.set(VulnerabilityMetaData.Attribute.SCORE_EXPLOITABILITY, String.valueOf(contextOrInitial.getBakedScores().getExploitabilityScore()));
}
if (!Double.isNaN(contextOrInitial.getBakedScores().getImpactScore())) {
vmd.set(VulnerabilityMetaData.Attribute.SCORE_IMPACT, String.valueOf(contextOrInitial.getBakedScores().getImpactScore()));
}
}
}
if (this.kevData != null) {
vmd.set(InventoryAttribute.KEV_DATA, kevData.toJson().toString());
} else {
vmd.set(InventoryAttribute.KEV_DATA, null);
}
if (this.createDate != null) {
vmd.set(InventoryAttribute.VULNERABILITY_CREATED_DATE_TIMESTAMP.getKey(), Long.toString(this.createDate.getTime()));
vmd.set(InventoryAttribute.VULNERABILITY_CREATED_DATE_FORMATTED.getKey(), TimeUtils.formatNormalizedDate(this.createDate));
} else {
vmd.set(InventoryAttribute.VULNERABILITY_CREATED_DATE_TIMESTAMP.getKey(), null);
}
if (this.updateDate != null) {
vmd.set(InventoryAttribute.VULNERABILITY_UPDATED_DATE_TIMESTAMP.getKey(), Long.toString(this.updateDate.getTime()));
vmd.set(InventoryAttribute.VULNERABILITY_UPDATED_DATE_FORMATTED.getKey(), TimeUtils.formatNormalizedDate(this.updateDate));
} else {
vmd.set(InventoryAttribute.VULNERABILITY_UPDATED_DATE_TIMESTAMP.getKey(), null);
}
if (!this.references.isEmpty()) {
final List mergedReferences = Reference.mergeReferences(this.references, Reference.fromJsonArray((vmd.get(VulnerabilityMetaData.Attribute.REFERENCES))));
vmd.set(VulnerabilityMetaData.Attribute.REFERENCES, mergedReferences.stream().map(Reference::toJson).collect(CustomCollectors.toJsonArray()).toString());
} else {
vmd.set(VulnerabilityMetaData.Attribute.REFERENCES, null);
}
if (this.description != null) {
vmd.set(InventoryAttribute.DESCRIPTION.getKey(), this.description);
} else {
vmd.set(InventoryAttribute.DESCRIPTION.getKey(), null);
}
if (!this.tags.isEmpty()) {
vmd.set(InventoryAttribute.TAGS.getKey(), String.join(", ", this.tags));
} else {
vmd.set(InventoryAttribute.TAGS.getKey(), null);
}
if (this.vulnerabilityStatus != null) {
vmd.set(InventoryAttribute.VULNERABILITY_STATUS, this.vulnerabilityStatus.toJson().toString());
} else {
vmd.set(InventoryAttribute.VULNERABILITY_STATUS, null);
}
}
@Override
public void appendFromDataClass(Vulnerability dataClass) {
super.appendFromDataClass(dataClass);
if (StringUtils.hasText(dataClass.getDescription())) {
this.setDescription(dataClass.getDescription());
}
if (StringUtils.hasText(dataClass.getNotes())) {
this.setNotes(dataClass.getNotes());
}
if (StringUtils.hasText(dataClass.getUrl())) {
this.setUrl(dataClass.getUrl());
}
if (dataClass.getCreateDate() != null) {
this.setCreateDate(dataClass.getCreateDate());
}
if (dataClass.getUpdateDate() != null) {
this.setUpdateDate(dataClass.getUpdateDate());
}
this.cvssVectors.addAllCvssVectors(dataClass.getCvssVectors());
this.addReferences(dataClass.getReferences());
this.addCwes(dataClass.getCwes());
this.setKevData(dataClass.getKevData());
this.setEpssData(dataClass.getEpssData());
dataClass.getSecurityAdvisories().forEach(this::addSecurityAdvisory);
if (dataClass.getVulnerabilityStatus() != null) {
this.setVulnerabilityStatus(dataClass.getVulnerabilityStatus());
}
this.addTags(dataClass.getTags());
}
@Override
public void appendFromMap(Map input) {
super.appendFromMap(input);
this.setId(getStringOrNullFromMap(input, "name"));
final String source = (String) input.getOrDefault("source", null);
final String sourceImplementation = (String) input.getOrDefault("sourceImplementation", null);
if (source != null || sourceImplementation != null) {
this.setSourceIdentifier(VulnerabilityTypeStore.get().fromNameAndImplementation(source, sourceImplementation));
}
this.setDescription(getStringOrNullFromMap(input, "description"));
this.setNotes(getStringOrNullFromMap(input, "notes"));
this.setUrl(getStringOrNullFromMap(input, "url"));
if (input.get("cvss") != null) {
final JSONArray cvssVectorsJson;
if (input.get("cvss") instanceof String) {
final String cvssVectors = (String) input.get("cvss");
cvssVectorsJson = new JSONArray(cvssVectors);
} else if (input.get("cvss") instanceof JSONArray) {
cvssVectorsJson = (JSONArray) input.get("cvss");
} else if (input.get("cvss") instanceof Collection) {
cvssVectorsJson = new JSONArray((Collection) input.get("cvss"));
} else {
throw new RuntimeException("Unable to parse cvss vectors from input map: " + input.get("cvss"));
}
this.cvssVectors.addAllCvssVectors(CvssVectorSet.fromJson(cvssVectorsJson));
}
this.setCreateDate(ObjectUtils.firstNonNull(getDateOrNullFromMap(input, "createDate"), getDateOrNullFromMap(input, "publishedDate")));
this.setUpdateDate(ObjectUtils.firstNonNull(getDateOrNullFromMap(input, "updateDate"), getDateOrNullFromMap(input, "lastModifiedDate")));
for (String cwe : ((Collection) input.getOrDefault("cwe", Collections.EMPTY_SET))) {
this.addCwe(cwe);
}
if (input.get("vulnerable_software") != null) {
try {
JSONArray array = new JSONArray(((ArrayList