Skip to content

gcve-eu/gcve-eu-kev

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

49 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

gcve-eu-kev

Convert CISA KEV, ENISA CNW EUVD, KEVIntel, and Shadowserver Foundation honeypot feeds into GCVE-BCP-07 Known Exploited Vulnerability (KEV) assertions.

The goal is to turn list-based KEV feeds into attributable, structured exploitation assertions suitable for ingestion into GCVE-compatible systems, vulnerability databases, or analytical pipelines — such as a Vulnerability-Lookup instance.

GCVE-EU-KEV tooling integrated in Vulnerability-Lookup

What it does

For each source, the tool downloads the feed and converts every entry into a GCVE-BCP-07 assertion, preserving the source's semantics while adding:

  • Explicit attribution (evidence.source)
  • Separated timestamps (first_seen_at, asserted_at, last_seen_at, recorded_at)
  • A conservative evidence.confidence per source (to avoid overstating claims)

Assertions are written as NDJSON (default, one per line) or a single JSON array, and can be pushed to a Vulnerability-Lookup instance. Previously converted assertions can also be rendered as Atom/RSS feeds.

The tool preserves published KEV information and does not invent or infer extra metadata (geography, sector, etc.). When no CVE is available, provider identifiers (e.g. EUVD) are kept as references. recorded_at is the local ingestion time added by the collector.

Sources

Command Source Feed Confidence
gcve-from-cisa CISA KEV Catalog JSON 0.8
gcve-from-enisa ENISA CNW EUVD KEV JSON 0.75
gcve-from-kevintel KEVIntel Paginated JSON API 0.7
gcve-from-shadowserver Shadowserver Foundation HMAC-signed honeypot/exploited-vulnerabilities 0.7

Each command is also runnable as a module (python -m gcve_eu_kev.cisa --help, etc.).

Installation

git clone https://github.com/gcve-eu/gcve-eu-kev
cd gcve-eu-kev
poetry install

Python >= 3.12 is required. The only runtime dependency is requests.

Usage

Write assertions as NDJSON (default) or a JSON array:

gcve-from-cisa -o -                       # NDJSON to stdout
gcve-from-enisa -o kev_assertions.ndjson  # NDJSON to a file
gcve-from-cisa --json-array -o kev.json   # single JSON array

Convert existing assertions into an Atom or RSS feed:

gcve-from-cisa -o kev.ndjson
gcve-kev-to-feed --input kev.ndjson --format atom -o kev.atom.xml
gcve-kev-to-feed --input kev.ndjson --format rss  -o kev.rss.xml

Push assertions to a Vulnerability-Lookup instance (requires API key and origin UUID):

gcve-from-cisa --push
gcve-from-cisa --push --since-date 2026-02-02
gcve-from-enisa --push --since-date '10 days'

--since-date accepts ISO dates, relative expressions ('1 day', '2 weeks'), and yesterday.

KEVIntel

Fetches every page of the paginated kevintel.com API. Filter on the not_yet_in_cisa_kev key with --not-yet-in-cisa-kev {all,true,false}:

gcve-from-kevintel -o kevintel.ndjson                                 # all entries
gcve-from-kevintel --not-yet-in-cisa-kev true -o kevintel_new.ndjson  # not yet in CISA KEV
gcve-from-kevintel --push --not-yet-in-cisa-kev true --since-date '1 week'

Shadowserver

Queries the HMAC-signed Shadowserver Reports API. Provide credentials via the environment (SHADOWSERVER_API_KEY / SHADOWSERVER_API_SECRET / optional SHADOWSERVER_API_URI), via conf.py, or via the official ~/.shadowserver.api INI file (an [api] section with key / secret / uri).

Each record carries a first_seen field (when Shadowserver first observed exploitation), so timestamps are read straight from the feed — no inference or persistent state. first_seen becomes first_seen_at and asserted_at (a telemetry source asserts exploitation as of its first observation); the queried day becomes last_seen_at and status.status_updated_at.

# A single day (defaults to yesterday, since today is usually incomplete)
gcve-from-shadowserver --date 2026-06-24

# Backfill a range (one API call per day), optionally capped with --to-date
gcve-from-shadowserver --since-date '7 days' --json-array -o shadowserver.json
gcve-from-shadowserver --since-date 2026-01-01 --to-date 2026-02-01 --json-array -o jan.json

# Filter on iot / cisa_kev / geo, then push
gcve-from-shadowserver --iot yes --cisa-kev no --geo DE,FR --push

The output file is the full per-day time series (one assertion per vulnerability per day). On --push, those collapse to one merged assertion per vulnerability (the latest day's record), because the KEV API keys a record on (origin_uuid, vulnId).

Unlike the other converters, Shadowserver --push upserts: it POSTs to create each entry and, on HTTP 409 (an entry already exists for that (origin_uuid, vulnId)), falls back to PUT /kev/origin/{origin_uuid}/{vuln_id} to update it. So a re-run refreshes last_seen_at on the existing record instead of being rejected as a duplicate.

Configuration

By default the Vulnerability-Lookup API URL and key are read from gcve_eu_kev/conf.py (gitignored; falls back to public defaults if missing). To push to a different instance without editing that file, set environment variables:

VULNERABILITY_LOOKUP_API_URL=https://other-instance.example.com/api/kev \
VULNERABILITY_LOOKUP_API_KEY=mykey \
gcve-from-cisa --push

Further reading

Contributing

Please open issues or PRs for improvements, corner cases, or fixes.

License

gcve-eu-kev is licensed under the GNU General Public License version 3.

Copyright (c) 2026 Computer Incident Response Center Luxembourg (CIRCL)
Copyright (c) 2026 Alexandre Dulaunoy - https://github.com/adulau
Copyright (C) 2026 Cédric Bonhomme - https://github.com/cedricbonhomme

About

CISA/ENISA KEV to GCVE BCP-07 Converter.

Resources

License

Stars

6 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages