A Python CLI tool for querying the ENISA EU Vulnerability Database (EUVD) API
Project description
EUVD CLI
Query the ENISA EU Vulnerability Database (EUVD) from the command line and from Python
Overview
EUVD CLI is both a command-line tool and a Python library that fetches vulnerabilities, advisories, and KEV entries from the ENISA EU Vulnerability Database, validates the responses with Pydantic, and emits JSON or SARIF 2.1 output. The same client and validation rules apply when used as a library.
Current feature set
| Area | Capabilities |
|---|---|
| Endpoints | latest, critical, exploited, ENISA ID lookup, advisory lookup, search, KEV dump |
| Search filters | --text, --vendor, --product, --assigner, --from-score/--to-score (0-10), --from-epss/--to-epss (0-100), --from-date/--to-date (YYYY-MM-DD), --exploited/--not-exploited, --size (1-100), --page |
| Output | JSON (default), JSONL, CSV, TOON, and SARIF 2.1, written to stdout cleanly for piping |
| Save to file | --output <path> and --save (timestamped filename) for kev-dump, with overwrite warning |
| Validation | CVSS/EPSS bounds, strict ISO date format, from_X <= to_X cross-pair, page-size cap enforced via Pydantic before the request |
| Streams | banner/status/errors on stderr, machine-readable data on stdout, no ANSI escapes in JSON output |
| Errors | typed exceptions (API_ERRORS, SARIFConversionError, OSError) handled with a single-line user message; SIGINT exits 130, SIGPIPE exits cleanly |
| Library API | from euvd_python import EUVDAPIClient, SearchFilters, to_sarif_json, ... (32 public symbols) |
| Resilience | client-side rate limiter (1 request / 6 s) and 30-second HTTP timeout (calibrated for slow /search?text= queries) |
| Self-test | 39-check regression suite against the live API (euvd-cli selftest) |
Supported endpoints
| Path | Subcommand |
|---|---|
/lastvulnerabilities |
latest |
/criticalvulnerabilities |
critical |
/exploitedvulnerabilities |
exploited |
/enisaid?id= |
search-enisa |
/advisory?id= |
search-advisory |
/search |
search |
/kev/dump |
kev-dump |
Base URL: https://euvdservices.enisa.europa.eu/api.
Installation
From PyPI
pip install euvd-python-cli
From source
git clone https://github.com/seifreed/euvd-cli.git
cd euvd-cli
python3 -m venv venv
source venv/bin/activate
pip install -e .
Development
pip install -e ".[dev]"
Quick start
euvd-cli latest
euvd-cli critical
euvd-cli exploited
euvd-cli search-enisa EUVD-2025-4893
euvd-cli search-advisory oxas-adv-2024-0002
euvd-cli search --text "OpenSSL" --from-score 7.0 --size 20
euvd-cli stats
euvd-cli kev-dump --save
euvd-cli --format sarif latest > latest.sarif.json
CLI usage
Inputs
euvd-cli latest
euvd-cli critical
euvd-cli exploited
euvd-cli search-enisa EUVD-2025-4893
euvd-cli search-advisory oxas-adv-2024-0002
euvd-cli kev-dump
Search
euvd-cli search --text "Windows" --size 10
euvd-cli search --exploited --size 5
euvd-cli search --from-score 9.0 --to-score 10.0
euvd-cli search --from-date 2024-01-01 --to-date 2024-12-31
euvd-cli search --from-epss 50 --to-epss 100
Output formats
The --format flag is on the group, before the subcommand. Supported: json (default, indented), jsonl (one JSON object per line), csv, toon (Token-Oriented Object Notation, requires pip install "euvd-python-cli[toon]" --pre), sarif (SARIF 2.1).
euvd-cli --format json latest
euvd-cli --format jsonl latest
euvd-cli --format csv latest
euvd-cli --format toon latest
euvd-cli --format sarif search-advisory oxas-adv-2024-0002
For search, the jsonl and csv formats emit only the items (the total wrapper is dropped because flat formats can't represent it).
Save to file
euvd-cli kev-dump -o kev.json
euvd-cli kev-dump --save # kev_dump_YYYYMMDD_HHMMSS.json
euvd-cli --format sarif kev-dump -o kev.sarif.json
Statistics and self-test
euvd-cli stats
euvd-cli selftest
Common options
| Option | Meaning |
|---|---|
-v, --verbose |
Enable DEBUG logging (rate-limiter timing) |
--format json|sarif |
Output format. Group-level flag, before the subcommand |
-h, --help |
Help. Works on the group and on every subcommand |
-o, --output <path> |
Save destination for kev-dump |
--save |
Save with auto-generated timestamped filename |
Python library
from euvd_python import EUVDAPIClient, SearchFilters, to_sarif_json
with EUVDAPIClient() as client:
latest = client.get_latest_vulnerabilities()
advisory = client.get_advisory_by_id("oxas-adv-2024-0002")
results = client.search_vulnerabilities(
SearchFilters(text="OpenSSL", from_score=7.0, size=20)
)
sarif_json = to_sarif_json(latest)
The same validation rules apply: SearchFilters rejects out-of-range CVSS/EPSS, non-ISO dates, inverted ranges, and out-of-bounds size before the request is made.
Public API
from euvd_python import (
EUVDAPIClient,
RateLimiter,
APIResponseError,
API_ERRORS,
SearchFilters,
LatestVulnerability,
CriticalVulnerability,
ExploitedVulnerability,
SearchResultVulnerability,
ENISAVulnerabilityByID,
VulnerabilityByID,
AdvisoryByID,
AdvisorySource,
KevEntry,
VulnerabilityQueryResponse,
to_sarif_json,
SARIFConversionError,
CVSS_CRITICAL_THRESHOLD,
CVSS_HIGH_THRESHOLD,
CVSS_MEDIUM_THRESHOLD,
)
Importing euvd_python does not load Click, does not load rich.console, and does not install signal handlers. Those are only loaded by euvd_python.cli and euvd_python.main, which library users have no reason to import.
Output notes
- JSON: pretty-printed with
indent=2, written viasys.stdout.writeso the output is pipe-safe and free of ANSI escapes. - SARIF: SARIF 2.1 with stable rule deduplication, EPSS mapped to
rankon the API's native 0-100 scale, severity derived from CVSS thresholds. - Diagnostics (banner, "Fetching...", "Saved to...", error messages) all go to stderr so stdout stays parseable.
- Models preserve unknown wire fields via Pydantic
extra="allow", so future EUVD additions show up in JSON output without a code change.
Validation
SearchFilters enforces all input invariants in one place:
| Field | Rule |
|---|---|
from_score, to_score |
float in [0, 10] |
from_epss, to_epss |
float in [0, 100] |
from_date, to_date |
string matching ^\d{4}-\d{2}-\d{2}$, valid calendar date |
from_X, to_X |
from_X <= to_X |
size |
int in [1, 100] |
page |
int >= 0 |
Invalid input prints Invalid input: --<flag>: <reason> to stderr and exits 1, before any HTTP request.
Testing
euvd-cli selftest
The self-test hits the live EUVD API. There are no mocks — a passing self-test means real usage works against the real endpoints. CVSS/EPSS bounds, ISO date parsing, SARIF correctness, stream routing, signal handling, and library re-exports are all locked by synthetic checks within the same suite.
Quality gates run with default rules and zero suppressions:
black --check euvd_python/
ruff check euvd_python/
mypy euvd_python/
bandit -r euvd_python/
pip-audit -r requirements.txt
License
MIT — see LICENSE.
Author: Marc Rivero Lopez · mriverolopez@gmail.com
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file euvd_python_cli-1.0.2.tar.gz.
File metadata
- Download URL: euvd_python_cli-1.0.2.tar.gz
- Upload date:
- Size: 24.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
141f0cb547e0cb9b388d9dfeeef4f8de2c84d124dbb35da7544d2832db62d7ef
|
|
| MD5 |
d3414bdc05b7ffdfbe9c653335257cff
|
|
| BLAKE2b-256 |
3cabdb431964a58c4309db092f037943cc5621edc2452a2782b57b6cb0631f48
|
Provenance
The following attestation bundles were made for euvd_python_cli-1.0.2.tar.gz:
Publisher:
release.yml on seifreed/euvd-cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
euvd_python_cli-1.0.2.tar.gz -
Subject digest:
141f0cb547e0cb9b388d9dfeeef4f8de2c84d124dbb35da7544d2832db62d7ef - Sigstore transparency entry: 1485711160
- Sigstore integration time:
-
Permalink:
seifreed/euvd-cli@a4e21d9051dd89d4f2b8f88f4079f463d13ae503 -
Branch / Tag:
refs/tags/v1.0.2 - Owner: https://github.com/seifreed
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a4e21d9051dd89d4f2b8f88f4079f463d13ae503 -
Trigger Event:
push
-
Statement type:
File details
Details for the file euvd_python_cli-1.0.2-py3-none-any.whl.
File metadata
- Download URL: euvd_python_cli-1.0.2-py3-none-any.whl
- Upload date:
- Size: 23.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
25cf1a495180addfeb7090cddf3536e58bf777516fa1e134fca030d5381130d0
|
|
| MD5 |
a1a347ee9b97aa735af1596d3f6499ef
|
|
| BLAKE2b-256 |
48d1481c7da4512f61f47e13f6ba5a1d221d10d95da9a91ae1e06ae58c2a0146
|
Provenance
The following attestation bundles were made for euvd_python_cli-1.0.2-py3-none-any.whl:
Publisher:
release.yml on seifreed/euvd-cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
euvd_python_cli-1.0.2-py3-none-any.whl -
Subject digest:
25cf1a495180addfeb7090cddf3536e58bf777516fa1e134fca030d5381130d0 - Sigstore transparency entry: 1485711193
- Sigstore integration time:
-
Permalink:
seifreed/euvd-cli@a4e21d9051dd89d4f2b8f88f4079f463d13ae503 -
Branch / Tag:
refs/tags/v1.0.2 - Owner: https://github.com/seifreed
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a4e21d9051dd89d4f2b8f88f4079f463d13ae503 -
Trigger Event:
push
-
Statement type: