All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.metaeffekt.mirror.contents.advisory.CertSeiAdvisorEntry Maven / Gradle / Ivy

There is a newer version: 0.132.0
Show 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.advisory;

import com.metaeffekt.artifact.analysis.utils.StringUtils;
import com.metaeffekt.artifact.analysis.utils.TimeUtils;
import com.metaeffekt.mirror.contents.base.DescriptionParagraph;
import com.metaeffekt.mirror.contents.base.Reference;
import com.metaeffekt.mirror.contents.store.AdvisoryTypeStore;
import com.metaeffekt.mirror.contents.store.VulnerabilityTypeStore;
import org.apache.lucene.document.Document;
import org.json.JSONArray;
import org.json.JSONObject;
import org.metaeffekt.core.inventory.processor.model.AdvisoryMetaData;
import org.metaeffekt.core.inventory.processor.report.model.AdvisoryUtils;
import org.metaeffekt.core.security.cvss.CvssSource;
import org.metaeffekt.core.security.cvss.KnownCvssEntities;
import org.metaeffekt.core.security.cvss.v2.Cvss2;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

public class CertSeiAdvisorEntry extends AdvisoryEntry {

    private final static Logger LOG = LoggerFactory.getLogger(CertSeiAdvisorEntry.class);

    protected final static Set CONVERSION_KEYS_AMB = new HashSet(AdvisoryEntry.CONVERSION_KEYS_AMB) {{
    }};

    protected final static Set CONVERSION_KEYS_MAP = new HashSet(AdvisoryEntry.CONVERSION_KEYS_MAP) {{
    }};


    public CertSeiAdvisorEntry() {
        super(AdvisoryTypeStore.CERT_SEI);
    }

    public CertSeiAdvisorEntry(String id) {
        super(AdvisoryTypeStore.CERT_SEI, id);
    }

    @Override
    public String getUrl() {
        return "https://kb.cert.org/vuls/id/" + id.replace("VU#", "");
    }

    @Override
    public String getType() {
        return AdvisoryUtils.normalizeType("alert");
    }

    /* TYPE CONVERSION METHODS */

    @Override
    protected Set conversionKeysAmb() {
        return CONVERSION_KEYS_AMB;
    }

    @Override
    protected Set conversionKeysMap() {
        return CONVERSION_KEYS_MAP;
    }

    @Override
    public CertSeiAdvisorEntry constructDataClass() {
        return new CertSeiAdvisorEntry();
    }

    public static CertSeiAdvisorEntry fromAdvisoryMetaData(AdvisoryMetaData amd) {
        return AdvisoryEntry.fromAdvisoryMetaData(amd, CertSeiAdvisorEntry::new);
    }

    public static CertSeiAdvisorEntry fromInputMap(Map map) {
        return AdvisoryEntry.fromInputMap(map, CertSeiAdvisorEntry::new);
    }

    public static CertSeiAdvisorEntry fromJson(JSONObject json) {
        return AdvisoryEntry.fromJson(json, CertSeiAdvisorEntry::new);
    }

    public static CertSeiAdvisorEntry fromDocument(Document document) {
        return AdvisoryEntry.fromDocument(document, CertSeiAdvisorEntry::new);
    }

    @Override
    public void appendFromBaseModel(AdvisoryMetaData amd) {
        super.appendFromBaseModel(amd);
    }

    @Override
    public void appendToBaseModel(AdvisoryMetaData amd) {
        super.appendToBaseModel(amd);
    }

    @Override
    public void appendFromMap(Map map) {
        super.appendFromMap(map);
    }

    @Override
    public void appendToJson(JSONObject json) {
        super.appendToJson(json);
    }

    @Override
    public void appendFromDocument(Document document) {
        super.appendFromDocument(document);
    }

    @Override
    public void appendToDocument(Document document) {
        super.appendToDocument(document);
    }

    /* PARSING OF CERT-SEI ADVISORIES */

    /**
     * The first element of each array element is the official title, the others are alternative titles that get merged
     * into the official title.
     */
    private final static String[][] CERT_SEI_OVERVIEW_HEADERS_ALTERNATE_NAMES = new String[][]{
            {"Overview"},
            {"Description"},
            {"Impact"},
            {"Solution"},
            {"Acknowledgements"},
            {"Workarounds", "Workaround", "Possible Workarounds", "Apply a workaround"},
    };

    public static CertSeiAdvisorEntry fromDownloadJson(JSONObject json) {
        final CertSeiAdvisorEntry entry = new CertSeiAdvisorEntry();

        {
            entry.setId(firstNonEmpty(
                    json.optString("vuid", null),
                    json.optString("idnumber", null),
                    json.optString("id", null),
                    "unknown"
            ).toUpperCase(Locale.ENGLISH)
                    .replaceAll("[-–_ ]+", "-")
                    .replaceAll("\\D*(\\d+)\\D*", "VU#$1"));
        }

        {
            entry.setCreateDate(TimeUtils.tryParse(firstNonEmpty(
                    json.optString("datecreated", null),
                    json.optString("datefirstpublished", null),
                    json.optString("publicdate", null),
                    json.optString("dateupdated", null)
            )));
            entry.setUpdateDate(TimeUtils.tryParse(firstNonEmpty(
                    json.optString("dateupdated", null),
                    json.optString("datecreated", null),
                    json.optString("datefirstpublished", null),
                    json.optString("publicdate", null)
            )));
        }

        {
            final JSONArray referencesJson = json.optJSONArray("public");
            if (referencesJson != null) {
                final StringBuilder brokenUrlCollector = new StringBuilder();

                for (int i = 0; i < referencesJson.length(); i++) {
                    if (StringUtils.hasText(referencesJson.optString(i))) {
                        if (referencesJson.getString(i).length() == 1) {
                            brokenUrlCollector.append(referencesJson.getString(i));
                        } else {
                            entry.addReference(Reference.fromUrl(referencesJson.optString(i)));
                        }
                    }
                }

                if (brokenUrlCollector.length() > 0) {
                    entry.addReference(Reference.fromUrl(brokenUrlCollector.toString()));
                }
            }
        }

        {
            final JSONArray keywordsJson = json.optJSONArray("keywords");
            if (keywordsJson != null) {
                for (int i = 0; i < keywordsJson.length(); i++) {
                    entry.addKeyword(keywordsJson.optString(i));
                }
            }
        }

        {
            final Map overviewMap = new HashMap<>();

            if (json.optJSONObject("overview") != null) {
                LOG.info("overview is JSON on {}", entry.getId());
                json.optJSONObject("overview").toMap()
                        .forEach((key, value) -> overviewMap.put(key, String.valueOf(value)));
            } else {
                if (json.has("overview")) {
                    final String overviewString = String.valueOf(json.opt("overview"));

                    String currentTitle = "";
                    StringJoiner currentLines = new StringJoiner("\n");

                    final String[] lines = overviewString.split("\r?\n");

                    for (String line : lines) {
                        final String normalizedTitle = certSeiNormalizeTitleIfAvailable(line);

                        if (normalizedTitle != null) {
                            if (currentLines.length() > 0) {
                                overviewMap.put(currentTitle, trimNewlines(currentLines.toString()));
                                currentLines = new StringJoiner("\n");
                            }

                            currentTitle = normalizedTitle;
                        } else {
                            currentLines.add(line);
                        }
                    }

                    if (currentLines.length() > 0) {
                        overviewMap.put(currentTitle, trimNewlines(currentLines.toString()));
                    }
                }
            }

            final String cleanDescription = json.optString("clean_desc", null);
            final String impact = json.optString("impact", null);
            final String resolution = json.optString("resolution", null);
            final String workarounds = json.optString("workarounds", null);
            final String name = json.optString("name", null);

            entry.setSummary(firstNonEmpty(name, overviewMap.get("Overview"), cleanDescription));
            entry.addDescription(DescriptionParagraph.fromContent(longestNonEmpty(overviewMap.get("Description"), overviewMap.get(""), cleanDescription)));
            entry.setThreat(firstNonEmpty(overviewMap.get("Impact"), impact));
            entry.setRecommendations(firstNonEmpty(overviewMap.get("Solution"), resolution));
            entry.setWorkarounds(firstNonEmpty(overviewMap.get("Workarounds"), workarounds));

            entry.addAcknowledgements(Arrays.asList(json.optString("author", "").split("(,|\n\n)")));
            entry.addAcknowledgements(Arrays.asList(json.optString("thanks", "").split("(,|\n\n)")));
            entry.addAcknowledgements(Arrays.asList(overviewMap.getOrDefault("Acknowledgements", "").split("(,|\n\n)")));
        }

        {
            final JSONArray referencedCves = json.optJSONArray("cveids");
            if (referencedCves != null && !referencedCves.isEmpty()) {
                for (int i = 0; i < referencedCves.length(); i++) {
                    final String id = referencedCves.optString(i, null);
                    if (!StringUtils.hasText(id)) continue;

                    if (id.contains(" ")) {
                        entry.addReferencedVulnerabilities(VulnerabilityTypeStore.CVE, Arrays.asList(referencedCves.optString(i).split(" ?,? ")));
                    } else {
                        entry.addReferencedVulnerability(VulnerabilityTypeStore.CVE, referencedCves.optString(i));
                    }
                }
            }

            if (entry.getReferencedVulnerabilities(VulnerabilityTypeStore.CVE).isEmpty()) {
                extractReferenceIdsFromStringIfPresent(entry.getSummary(), entry, VulnerabilityTypeStore.CVE);
                extractReferenceIdsFromStringIfPresent(entry.getTextDescription(), entry, VulnerabilityTypeStore.CVE);
                extractReferenceIdsFromStringIfPresent(entry.getThreat(), entry, VulnerabilityTypeStore.CVE);
                extractReferenceIdsFromStringIfPresent(entry.getRecommendations(), entry, VulnerabilityTypeStore.CVE);
                extractReferenceIdsFromStringIfPresent(entry.getWorkarounds(), entry, VulnerabilityTypeStore.CVE);
            }

            if (entry.getReferencedVulnerabilities(VulnerabilityTypeStore.CVE).isEmpty()) {
                LOG.debug("No referenced CVEs found for [{}], which makes it impossible for this advisory to be found during our enrichment.", entry.getId());
            }
        }

        {
            final String cvss2Base = json.optString("cvss_basevector", null);
            final String cvss2Environmental = json.optString("cvss_environmentalvector", null);

            final StringJoiner cvss2Joiner = new StringJoiner("/");
            if (StringUtils.hasText(cvss2Base) && !cvss2Base.equals("N/A") && !cvss2Base.contains("--")) {
                cvss2Joiner.add(cvss2Base);
            }
            if (StringUtils.hasText(cvss2Environmental) && !cvss2Environmental.equals("N/A") && !cvss2Environmental.contains("--")) {
                cvss2Joiner.add(cvss2Environmental);
            }

            if (!cvss2Joiner.toString().trim().isEmpty()) {
                final Cvss2 vector = new Cvss2(cvss2Joiner.toString(), new CvssSource(KnownCvssEntities.CERT_SEI, Cvss2.class));
                if (vector.isAnyBaseDefined() || vector.isAnyEnvironmentalDefined() || vector.isAnyTemporalDefined()) {
                    entry.getCvssVectors().addCvssVector(vector);
                }
            }
        }

        return entry;
    }

    private static String certSeiNormalizeTitleIfAvailable(String title) {
        if (title.startsWith("### ") || title.startsWith("#### ")) {
            title = title
                    .replace("#### ", "")
                    .replace(" ####", "")
                    .replace("### ", "")
                    .replace(" ###", "")
                    .trim();

            for (String[] validTitles : CERT_SEI_OVERVIEW_HEADERS_ALTERNATE_NAMES) {
                for (String validTitle : validTitles) {
                    if (validTitle.equals(title)) {
                        return validTitles[0];
                    }
                }
            }
        }

        return null;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy