euvd-watch
EUVD-native software supply-chain vulnerability watch + EU Cyber Resilience Act (CRA) reporting toolkit.
⚠️ Status: work in progress. APIs and structure may change until
1.0.0. The Commands table below marks what is ✅ available today versus 🚧 planned — milestones M0–M4 (scan, match, VEX, the CRA workflow) and M5'swatchmode, Docker image, GitHub Action and GitLab template are implemented and tested; PyPI release automation is the rest of M5 (blocked on the PyPI name). The dashboard ships in1.1.
euvd-watch connects software supply-chain transparency to Europe's own vulnerability infrastructure and to the concrete reporting duties of the EU Cyber Resilience Act.
It ingests your SBOM, continuously matches every component against the European Union Vulnerability Database (EUVD) operated by ENISA — including its actively exploited flag and EPSS scores — automatically drafts machine-readable VEX statements to cut false-positive noise, and, when a component is hit by an actively exploited vulnerability, drafts the CRA Article 14 notification and starts the 24-hour clock with a tamper-evident audit trail.
Why this exists
SBOM generation (Syft, cdxgen) and scanning against US sources (NVD, OSV) are mature. But:
- Nothing open is built around the EUVD — Europe's own vulnerability database, operated by ENISA.
- Nothing connects "exploited" status to the CRA's actual reporting workflow (the 24-hour early warning to ENISA/CSIRTs).
- VEX generation is still mostly manual, so teams drown in non-applicable findings.
euvd-watch fills that gap as a self-hostable building block that runs in CI/CD and on a schedule. It does not reinvent SBOM generators or scanners — it reuses them.
Pipeline
flowchart LR
A[SBOM<br/>CycloneDX / SPDX] -->|ingest| B[Normalized<br/>components]
B -->|match| C[EUVD<br/>exploited + EPSS + KEV]
C --> D[OpenVEX<br/>statements]
C -->|trigger| E[CRA Article 14<br/>draft + 24h clock + audit log]
B -.->|CI/CD · CLI · watch| F[Dashboard]
Quickstart (everything below works today)
# Not yet on PyPI - install from a clone until the first release:
pip install -e .
euvd-watch version
# 1. Generate an SBOM for your project (using Syft, or bring your own)
syft dir:. -o cyclonedx-json > sbom.cdx.json
# 2. See what's inside it
euvd-watch scan sbom.cdx.json
# 3. Match it against the EUVD — show only actively exploited vulnerabilities
euvd-watch match sbom.cdx.json --exploited-only
# 4. Generate OpenVEX statements (conservative by design)
euvd-watch vex generate sbom.cdx.json -o openvex.json
# 5. Check whether anything crossed your CRA reporting threshold
euvd-watch cra check sbom.cdx.json
euvd-watch cra status
# 6. Watch it on a schedule - notify only new/resolved/changed findings
euvd-watch watch sbom.cdx.json --interval 6h
Commands
| Command | Status | What it does |
|---|---|---|
scan <sbom> |
✅ | Parse and normalize a CycloneDX (1.4–1.6) / SPDX (2.3) JSON SBOM into a component inventory. |
match <sbom> |
✅ | Match components against the EUVD, with confidence scoring and EPSS/KEV enrichment. Flags: --exploited-only, --min-confidence, --fail-on, --no-enrich, --save-findings, --timestamp. |
vex generate <sbom> |
✅ | Draft OpenVEX statements. Only provably safe findings become not_affected; everything uncertain stays under_investigation. Merges your vex-decisions.yaml (--fail-on-conflict for CI). |
vex init-decisions <sbom> |
✅ | Scaffold a vex-decisions.yaml from current findings for humans to fill in. |
cra check <sbom> |
✅ | Evaluate the configurable reporting trigger (EUVD exploited / CISA KEV / EPSS threshold) and open events. Exit 1 when a new event opens. |
cra status / cra draft <id> / cra mark <id> |
✅ | Track the staged deadline clocks (24 h / 72 h / final report), render a prefilled notification draft with TODO-HUMAN markers, record human completion. |
cra verify-log |
✅ | Verify the tamper-evident (hash-chained) audit log; names the first broken entry. |
watch <sbom> |
✅ | Re-match on a schedule (--interval 6h) or once (--once, the default) and notify only new/resolved/changed findings (stdout, and --webhook URL). See docs/watch.md. |
web serve |
🚧 post-1.0 (planned for 1.1) |
Self-hostable, WCAG-compliant dashboard: findings, VEX statuses, CRA countdowns, audit log. |
All implemented commands support --output json|table and CI-friendly exit codes
(0 clean, 1 findings, 2 error). Unimplemented commands exit 2 with a clear message.
Using it in CI (✅ implemented — published coordinates pending)
The GitHub Action (action.yml at the repo root), the GitLab include-template
(templates/euvd-watch.gitlab-ci.yml) and the Docker image (docker/Dockerfile) are
implemented, schema-linted, and dogfooded by this repository's own CI — see
docs/integrations.md for the full reference. The <org> placeholders below become real
coordinates once the public repository home and the first PyPI release exist.
GitHub Actions:
- uses: anchore/sbom-action@v0 # generate SBOM with Syft
with: { format: cyclonedx-json, output-file: sbom.cdx.json }
- uses: <org>/euvd-watch@v1 # 🚧 public coordinates pending
with:
sbom-path: sbom.cdx.json
fail-on: exploited
GitLab CI:
include: # 🚧 public coordinates pending
- remote: 'https://raw.githubusercontent.com/<org>/euvd-watch/main/templates/euvd-watch.gitlab-ci.yml'
euvd-watch:
variables: { EUVDWATCH_SBOM: "sbom.cdx.json", EUVDWATCH_FAIL_ON: "exploited" }
Docker (build locally today; ghcr.io/<org>/euvd-watch once published):
docker build -f docker/Dockerfile -t euvd-watch .
docker run --rm -v "$PWD:/work:ro" euvd-watch match /work/sbom.cdx.json
Configuration
euvd-watch.yaml (or --config, or EUVD_WATCH_* env vars):
cache_dir: ~/.cache/euvd-watch
epss_threshold: 0.5
min_confidence: medium
organization:
name: "Example S.R.L."
contact_email: security@example.com
product_name: "Example Product"
cra_trigger:
euvd_exploited: true
cisa_kev: true
epss_over_threshold: true
Design principles
- Reuse, don't reinvent — wrap Syft/cdxgen output, OpenVEX, EPSS, KEV; build only the missing glue.
- EUVD-first, with OSV/KEV/EPSS as supplements.
- Conservative VEX — never auto-suppress something that might be real risk.
- Human-in-the-loop reporting —
euvd-watchdrafts; a human confirms before anything is filed. The tool never submits anything automatically. - Auditable — every decision carries a human-readable explanation and lands in a hash-chained audit log.
- Deterministic — same inputs produce byte-identical outputs.
What euvd-watch is NOT
- Not an SBOM generator (use Syft/cdxgen).
- Not a general-purpose scanner replacement (Grype/Trivy remain great for NVD/OSV coverage).
- Not legal advice, and not an automatic filing tool — CRA notifications are always reviewed and submitted by a human through official channels.
Architecture & docs
- docs/matching.md — matching strategies & confidence scoring
- docs/cra.md — the CRA Article 14 workflow, deadline stages, and the audit log's honest threat model
- docs/euvd-api.md — the verified EUVD API surface this tool uses
- README.simple.md — the same story, explained so a child can follow it
- GLOSSARY.md — every technical term (SBOM, VEX, CRA, EPSS…) explained in plain language
- 🚧 coming with their milestones:
ARCHITECTURE.md,docs/deploy.md(self-hosting),CONTRIBUTING.md
Contributing
Early contributors very welcome — CONTRIBUTING.md is coming; until then, open an issue.
License
EUPL-1.2. Documentation provided in English and Romanian — see README.ro.md / vezi README.ro.md.
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_watch-0.3.1.tar.gz.
File metadata
- Download URL: euvd_watch-0.3.1.tar.gz
- Upload date:
- Size: 592.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7e61b1600e50b26871b652afff95f683d2efba043f055307948498b9f9199bd0
|
|
| MD5 |
d8b4c365b2c3432def060dae34e8027c
|
|
| BLAKE2b-256 |
8fe387505c9e42b503edd20efca6a2bcb0a2d4c7843800861ecbe062f7b37b32
|
Provenance
The following attestation bundles were made for euvd_watch-0.3.1.tar.gz:
Publisher:
workflow.yaml on caisarus/euvd
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
euvd_watch-0.3.1.tar.gz -
Subject digest:
7e61b1600e50b26871b652afff95f683d2efba043f055307948498b9f9199bd0 - Sigstore transparency entry: 2381663230
- Sigstore integration time:
-
Permalink:
caisarus/euvd@7508f49ffa28040873aa8526a67ce62867c143a3 -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/caisarus
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
workflow.yaml@7508f49ffa28040873aa8526a67ce62867c143a3 -
Trigger Event:
push
-
Statement type:
File details
Details for the file euvd_watch-0.3.1-py3-none-any.whl.
File metadata
- Download URL: euvd_watch-0.3.1-py3-none-any.whl
- Upload date:
- Size: 81.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7610fbbbe8b54e1e7ed526dc6b18379bc0676aad10a3d11d6b40427821184e93
|
|
| MD5 |
c367fc014d4a9648895909755df6163d
|
|
| BLAKE2b-256 |
c5399196a0ccfc18b29b8145ae5abba3ac5b1215ef6ead04fd38dd0f37be6bbf
|
Provenance
The following attestation bundles were made for euvd_watch-0.3.1-py3-none-any.whl:
Publisher:
workflow.yaml on caisarus/euvd
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
euvd_watch-0.3.1-py3-none-any.whl -
Subject digest:
7610fbbbe8b54e1e7ed526dc6b18379bc0676aad10a3d11d6b40427821184e93 - Sigstore transparency entry: 2381663331
- Sigstore integration time:
-
Permalink:
caisarus/euvd@7508f49ffa28040873aa8526a67ce62867c143a3 -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/caisarus
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
workflow.yaml@7508f49ffa28040873aa8526a67ce62867c143a3 -
Trigger Event:
push
-
Statement type: