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