From 868188ade603fb3b253d6c1f0ad75ead5a544f99 Mon Sep 17 00:00:00 2001 From: Weston Steimel Date: Wed, 11 Mar 2026 12:33:40 +0000 Subject: [PATCH] feat(enisa): support gathering ENISA EUVD aliases Signed-off-by: Weston Steimel --- .../identifiers/aliases.py | 5 ++ .../identifiers/providers/__init__.py | 4 ++ .../identifiers/providers/enisa.py | 56 +++++++++++++++++++ 3 files changed, 65 insertions(+) create mode 100644 src/anchore_security_cli/identifiers/providers/enisa.py diff --git a/src/anchore_security_cli/identifiers/aliases.py b/src/anchore_security_cli/identifiers/aliases.py index 51a7c5b..5ba0ed0 100644 --- a/src/anchore_security_cli/identifiers/aliases.py +++ b/src/anchore_security_cli/identifiers/aliases.py @@ -62,6 +62,7 @@ def parse_identifier_from_url(url: str) -> str | None: class Aliases: cve: list[str] = field(default_factory=list) gcve: list[str] = field(default_factory=list) + enisa: list[str] = field(default_factory=list) cnvd: list[str] = field(default_factory=list) github: list[str] = field(default_factory=list) chainguard: list[str] = field(default_factory=list) @@ -114,6 +115,7 @@ def normalize(cls, alias: str) -> str: def from_list(cls, aliases: list[str], provider: str | None = None): # noqa: C901, PLR0912, PLR0915 cve = set() gcve = set() + enisa = set() cnvd = set() github = set() chainguard = set() @@ -165,6 +167,8 @@ def from_list(cls, aliases: list[str], provider: str | None = None): # noqa: C9 cve_id = gcve_to_cve(a) if cve_id: cve.add(cve_id) + elif a.startswith("EUVD-"): + enisa.add(a) elif a.startswith("CNVD-"): cnvd.add(a) elif a.startswith("GHSA-"): @@ -238,6 +242,7 @@ def from_list(cls, aliases: list[str], provider: str | None = None): # noqa: C9 return Aliases( cve=list(cve), gcve=list(gcve), + enisa=list(enisa), cnvd=list(cnvd), github=list(github), chainguard=list(chainguard), diff --git a/src/anchore_security_cli/identifiers/providers/__init__.py b/src/anchore_security_cli/identifiers/providers/__init__.py index 66d0192..e81153b 100644 --- a/src/anchore_security_cli/identifiers/providers/__init__.py +++ b/src/anchore_security_cli/identifiers/providers/__init__.py @@ -11,6 +11,7 @@ from anchore_security_cli.identifiers.providers.cve5 import CVE5 from anchore_security_cli.identifiers.providers.debian import Debian from anchore_security_cli.identifiers.providers.echo import Echo +from anchore_security_cli.identifiers.providers.enisa import ENISA from anchore_security_cli.identifiers.providers.gcve import GCVE from anchore_security_cli.identifiers.providers.github import GitHub from anchore_security_cli.identifiers.providers.go import Go @@ -38,6 +39,7 @@ class Providers: cve5: CVE5 github: GitHub gcve: GCVE + enisa: ENISA cnvd: CNVD chainguard: Chainguard bitnami: Bitnami @@ -112,6 +114,7 @@ def fetch_all() -> Providers: github = executor.submit(GitHub) gcve = executor.submit(GCVE) cnvd = executor.submit(CNVD) + enisa = executor.submit(ENISA) openssf_malicious_packages = executor.submit(OpenSSFMaliciousPackages) ubuntu = executor.submit(Ubuntu) chainguard = executor.submit(Chainguard) @@ -143,6 +146,7 @@ def fetch_all() -> Providers: cve5=cve5.result(), github=github.result(), gcve=gcve.result(), + enisa=enisa.result(), cnvd=cnvd.result(), chainguard=chainguard.result(), bitnami=bitnami.result(), diff --git a/src/anchore_security_cli/identifiers/providers/enisa.py b/src/anchore_security_cli/identifiers/providers/enisa.py new file mode 100644 index 0000000..1350f69 --- /dev/null +++ b/src/anchore_security_cli/identifiers/providers/enisa.py @@ -0,0 +1,56 @@ +import logging + +import requests + +from anchore_security_cli.identifiers.aliases import Aliases +from anchore_security_cli.identifiers.providers.provider import Provider, ProviderRecord + + +class ENISA(Provider): + def __init__(self): + super().__init__( + name="ENISA", + ) + + def _normalise_identifier(self, identifier: str) -> str: + components = identifier.split("-", 1) + if len(components) < 2: + return identifier + + prefix = components[0].upper() + return f"{prefix}-{components[1]}" + + def _fetch(self) -> list[ProviderRecord]: + records = [] + r = requests.get( + url="https://euvdservices.enisa.europa.eu/api/dump/cve-euvd-mapping", + timeout=30, + stream=True, + ) + r.raise_for_status() + + for record in r.iter_lines(): + record = record.decode("utf-8") + + if not record.startswith("EUVD-"): + continue + + components = record.split(",", 1) + + if len(components) != 2: + logging.warning(f"{self.name}: Skipping unexpected row {record}") + + euvd_id = self._normalise_identifier(components[0]) + cve_id = self._normalise_identifier(components[1]) + + logging.trace(f"{self.name}: processing record for {euvd_id}") + + records.append( + ProviderRecord( + id=euvd_id, + published=self._parse_date(None), + aliases=Aliases.from_list([euvd_id, cve_id], provider=self.name), + ), + ) + + return records