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

com.metaeffekt.mirror.contents.advisory.CertEuAdvisorEntry 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.advisory;

import com.metaeffekt.artifact.analysis.utils.StringUtils;
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.JSONObject;
import org.metaeffekt.core.inventory.processor.model.AdvisoryMetaData;
import org.metaeffekt.core.inventory.processor.report.model.AdvisoryUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class CertEuAdvisorEntry extends AdvisoryEntry {

    private final static Logger LOG = LoggerFactory.getLogger(CertEuAdvisorEntry.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 CertEuAdvisorEntry() {
        super(AdvisoryTypeStore.CERT_EU);
    }

    public CertEuAdvisorEntry(String id) {
        super(AdvisoryTypeStore.CERT_EU, id);
    }

    @Override
    public String getUrl() {
        return "https://cert.europa.eu/publications/security-advisories/" + id.replace("CERT-EU-", "");
    }

    @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 CertEuAdvisorEntry constructDataClass() {
        return new CertEuAdvisorEntry();
    }

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

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

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

    public static CertEuAdvisorEntry fromDocument(Document document) {
        return AdvisoryEntry.fromDocument(document, CertEuAdvisorEntry::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-EU ADVISORIES */

    /*
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "CERT-EU Advisories Schema",
  "type": "array",
  "items": {
    "type": "object",
    "properties": {
      "file_item": {
        "type": "object",
        "properties": {
          "filepath": {
            "type": "string"
          },
          "filename": {
            "type": "string"
          }
        }
      },
      "title": {
        "type": "string"
      },
      "serial_number": {
        "type": "string"
      },
      "publish_date": {
        "type": "string"
      },
      "description": {
        "type": "string"
      },
      "url_title": {
        "type": "string"
      },
      "content_markdown": {
        "type": [
          "null",
          "string"
        ]
      },
      "content_html": {
        "type": [
          "null",
          "string"
        ]
      },
      "licence": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string"
          },
          "link": {
            "type": "string"
          },
          "restrictions": {
            "type": "string"
          },
          "author": {
            "type": "string"
          }
        }
      }
    },
    "required": [
      "file_item",
      "title",
      "serial_number",
      "publish_date",
      "description",
      "url_title",
      "content_markdown",
      "content_html",
      "licence"
    ],
    "title": "Combined Schema"
  }
}
    */
    public static CertEuAdvisorEntry fromDownloadJson(JSONObject json) {
        final CertEuAdvisorEntry certEuAdvisorEntry = new CertEuAdvisorEntry();

        certEuAdvisorEntry.setId("CERT-EU-" + json.getString("serial_number"));
        certEuAdvisorEntry.setSummary(json.optString("title", null));

        if (json.has("description") && json.get("description") != JSONObject.NULL) {
            certEuAdvisorEntry.addDescription(DescriptionParagraph.fromTitleAndContent("description", json.getString("description")));
            extractReferenceIdsFromStringIfPresent(json.getString("description"), certEuAdvisorEntry, VulnerabilityTypeStore.CVE);
        }
        if (json.has("content_markdown") && json.get("content_markdown") != JSONObject.NULL) {
            extractMarkdownChaptersContent(json.getString("content_markdown"), certEuAdvisorEntry);
            extractReferenceIdsFromStringIfPresent(json.getString("content_markdown"), certEuAdvisorEntry, VulnerabilityTypeStore.CVE);
        }
        if (json.has("content_html") && json.get("content_html") != JSONObject.NULL) {
            certEuAdvisorEntry.addDescription(DescriptionParagraph.fromTitleAndContent("content_html", json.getString("content_html")));
            extractReferenceIdsFromStringIfPresent(json.getString("content_html"), certEuAdvisorEntry, VulnerabilityTypeStore.CVE);
        }

        try {
            // 20-06-2017 13:40:00
            final long createDate = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss").parse(json.getString("publish_date")).getTime();
            certEuAdvisorEntry.setCreateDate(createDate);
            certEuAdvisorEntry.setUpdateDate(createDate);
        } catch (ParseException e) {
            LOG.error("Unable to parse date from JSON: {}", json.getString("publish_date"));
        }

        return certEuAdvisorEntry;
    }

    private final static Map> CHAPTER_TITLE_NORMALIZATION = new HashMap<>();

    static {
        CHAPTER_TITLE_NORMALIZATION.put("Affected Products", new HashSet<>(Arrays.asList("products affected", "affected products", "**[update]** affected products", "products affected by sunburst", "products affected by supernova", "products and versions affected", "versions affected", "vulnerable systems")));
        CHAPTER_TITLE_NORMALIZATION.put("Unaffected Products", new HashSet<>(Arrays.asList("unaffected products", "non affected versions")));
        CHAPTER_TITLE_NORMALIZATION.put("Affected/Fixed Versions", new HashSet<>(Arrays.asList("affected products and fixed versions", "affected products and fixed release", "affected products and fixed releases")));
        CHAPTER_TITLE_NORMALIZATION.put("Recommendations", new HashSet<>(Arrays.asList("recommendations and workarounds", "recommendations and mitigations", "recommendation", "**[update]** recommendations", "recommandations")));
        CHAPTER_TITLE_NORMALIZATION.put("Workarounds", new HashSet<>(Arrays.asList("workaround:", "workaround", "workarounds", "mitigations", "remediation", "mitigation and workarounds", "workaround and mitigation")));
        CHAPTER_TITLE_NORMALIZATION.put("Exploitation", new HashSet<>(Arrays.asList("exploitation", "exploits")));
        CHAPTER_TITLE_NORMALIZATION.put("Technical Details", new HashSet<>(Arrays.asList("technical details", "technical details", "technical detail", "details")));
    }

    private static void extractMarkdownChaptersContent(String markdownContent, CertEuAdvisorEntry certEuAdvisorEntry) {
        // find the line which equals to "# References" and iterate over the following lines until the end or another header is found
        // each line is a reference. The reference is either a URL or a URL in brackets.
        final String[] lines = markdownContent.split("\n");
        final Map chapterSections = new LinkedHashMap<>();
        String currentChapterTitle = null;
        boolean lastWasTitle = false;

        for (String line : lines) {
            if (line.startsWith("# ")) {
                currentChapterTitle = line.substring(2).trim();
                for (Map.Entry> entry : CHAPTER_TITLE_NORMALIZATION.entrySet()) {
                    if (entry.getValue().contains(currentChapterTitle.toLowerCase())) {
                        currentChapterTitle = entry.getKey();
                        break;
                    }
                }

                lastWasTitle = true;
                continue;
            }
            if (lastWasTitle) {
                lastWasTitle = false;
                if (StringUtils.isEmpty(line)) {
                    continue;
                }
            }
            if (currentChapterTitle == null) {
                continue;
            }

            if (currentChapterTitle.equals("References")) {
                if (line.startsWith("[")) {
                    final String trimmedLine = line.trim();

                    // extract the reference
                    if (trimmedLine.contains(" --- ")) {
                        // Twitter --- 
                        // ArsTechnica --- [http://arstechnica.com/security/2016/11/firefox-0day-used-against-tor-users-almost-identical-to-one-fbi-used-in-2013 /](http://arstechnica.com/security/2016/11/firefox-0day-used-against-tor-users-almost-identical-to-one-fbi-used-in-2013/)
                        final String[] parts = trimmedLine.split(" --- ", 2);
                        final String urlTitle = parts[0].trim();
                        final String referenceUrl;
                        if (parts[1].startsWith("<") && parts[1].endsWith(">")) {
                            referenceUrl = parts[1].substring(1, parts[1].length() - 1);
                        } else {
                            final Pattern urlExtractionPattern = Pattern.compile("\\[([^]]+)]\\(([^)]+)\\)");
                            final Matcher matcher = urlExtractionPattern.matcher(parts[1]);
                            if (matcher.find()) {
                                referenceUrl = matcher.group(2);
                            } else {
                                referenceUrl = parts[1];
                            }
                        }
                        certEuAdvisorEntry.addReference(Reference.fromTitleAndUrl(urlTitle, referenceUrl));
                    } else {
                        // https://www.sonicwall.com/support/knowledge-base/urgent-security-notice-sonicwall-gms-analytics-impacted-by-suite-of-vulnerabilities/230710150218060/
                        // [1] 
                        // [2]  (not available anymore)
                        // [2] 0
                        final String[] parts = trimmedLine.split(" ", 2);
                        if (parts.length == 1) {
                            continue;
                        }

                        if (parts[1].startsWith("<")) {
                            final String[] urlParts = parts[1].split(">", 2);
                            final String referenceUrl = urlParts[0].substring(1);
                            final Reference reference = Reference.fromUrl(referenceUrl);
                            if (urlParts.length > 1 && !urlParts[1].trim().isEmpty()) {
                                reference.addTag(urlParts[1].trim());
                            }
                            certEuAdvisorEntry.addReference(reference);
                        } else {
                            certEuAdvisorEntry.addReference(Reference.fromUrl(parts[1]));
                        }
                    }
                }
            } else {
                chapterSections
                        .computeIfAbsent(currentChapterTitle, k -> new StringJoiner("\n"))
                        .add(line);
            }
        }

        final DescriptionParagraph descriptionParagraph = certEuAdvisorEntry.getDescription("description");
        final boolean hasDescriptionChapter = descriptionParagraph != null;

        for (Map.Entry chapter : chapterSections.entrySet()) {
            final String title = chapter.getKey();
            final String content = chapter.getValue().toString();

            if (hasDescriptionChapter && "Summary".equals(chapter.getKey())) {
                certEuAdvisorEntry.getDescription().remove(descriptionParagraph);
                certEuAdvisorEntry.addDescription(DescriptionParagraph.fromTitleAndContent("description", content));

            } else if ("recommendations".equalsIgnoreCase(title)) {
                certEuAdvisorEntry.setRecommendations(content);

            } else if ("workarounds".equalsIgnoreCase(title)) {
                certEuAdvisorEntry.setWorkarounds(content);

            } else if ("exploitation".equalsIgnoreCase(title)) {
                certEuAdvisorEntry.setThreat(content);

            } else {
                certEuAdvisorEntry.addDescription(DescriptionParagraph.fromTitleAndContent(chapter.getKey(), content));
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy