Skip to main content

cra24

Turn an embedded Linux build tree into a CRA Article 14 dossier before the 24-hour clock runs out.

licence: AGPL-3.0-only commercial licence available python 3.10+

Unofficial. Not affiliated with, endorsed by, or validated by ENISA, the European Commission, any national CSIRT, any market surveillance authority, or any notified body. cra24 organises evidence your build already produced and drafts documents for a human to review. It certifies nothing and submits nothing.


Why this exists

ENISA's Single Reporting Platform went live on 11 September 2026, the day reporting obligations started binding manufacturers, and it shipped without an API. So the 24-hour early warning is a human filling in a web form, in English, at whatever hour the news arrives.

Everyone else is building SBOM generators. The unserved job is the one after the SBOM:

  1. Given a CVE, is it actually in the image I ship — not merely in a recipe I built?
  2. Is the vulnerable code even compiled in?
  3. What do I put in the form, right now, in the platform's own field order?
  4. What do I hand my customers, machine-readably, so their scanners stop asking?
  5. How do I prove, in eight years, that the dossier came from the build I say it did?

cra24 answers those five and nothing else.

Three clocks, from the moment you become aware

Stage Vulnerability, Art. 14(1)–(2) Severe incident, Art. 14(3)–(4)
Early warning 24 hours 24 hours
Notification with initial assessment 72 hours 72 hours
Final report 14 days after a corrective or mitigating measure is available 1 month after the 72-hour notification

Two traps worth knowing before you need them:

  • The platform's own 72-hour counter has been reported to run from submission of the early warning rather than from the moment you became aware. Those are different instants and the second one is the legal test. cra24 clock uses awareness.
  • There is no platform counter at all for the vulnerability final report, because the deadline depends on when your fix ships. Nothing will remind you.

Quickstart

pip install 'cra24[validate]'

# 0. one-time: your identity, Member States, coordinator CSIRT
cra24 init --out cra24.json && $EDITOR cra24.json

# 1. read a build you already ran — no bitbake, no rebuild
cra24 scan --build-dir /path/to/yocto/build --config cra24.json

# 2. is this CVE actually in the shipped image, in code that runs?
cra24 check --product product.json CVE-2024-1086 -v

# 3. the dossier: portal fields, CSAF advisory, OpenVEX, evidence record
cra24 report --product product.json --config cra24.json CVE-2024-1086 \
  --aware-at 2026-09-17T08:30:00Z \
  --actively-exploited yes --severity high --malicious-intent yes \
  --summary "Exploited in the wild against internet-facing units."

# 4. let the feeds answer the hardest question for you
cra24 feeds sync
cra24 check --product product.json CVE-2024-1086      # now knows it is in CISA KEV

# 5. watch, so the next one finds you
cra24 watch --products products/ --once --notify email:psirt@example.com

# 6. just the clocks
cra24 clock --aware-at 2026-09-17T08:30:00Z

# 7. the ten-year question: has the evidence been tampered with?
cra24 verify dossier/evidence --summary

Try it against the fixture that ships with the repo:

cra24 check --build-dir tests/fixtures/yocto-build \
            --config tests/fixtures/demo-config.json CVE-2024-1086 -v

What it produces

File For whom What it is
*-early-warning.md you, at 2am The portal's fields, filled, in order, with the unanswered mandatory ones listed first. This is the one you read.
*-early-warning.json your tooling The same, structured, with the full triage evidence attached.
*.csaf.json your customers CSAF 2.0 advisory in the VEX profile. Validated against the OASIS schema on every emit.
*.openvex.json their scanners OpenVEX 0.2.0. Validated too.
evidence/ledger.jsonl the auditor in 2034 Hash-chained record of every artefact and the inputs that produced it.

What makes the triage worth trusting

Most tools turn "the scanner said CVE-X" into "you are affected". cra24 refuses to do that, and shows its work instead.

Every conclusion carries its evidence. A verdict is never just a status; it is a status, a confidence level, and the ordered list of rules that fired with the file each one read:

CVE-2023-42364 in AcmeGateway 2.4.0: not_affected

  busybox 1.36.1 -> not_affected
    justification: vulnerable_code_not_in_execute_path
    · build-cve-status: not-applicable-config -> not_affected
      not-applicable-config: CONFIG_AWK is not enabled in this defconfig

patched is evidence, not proof. When a layer reports a patch but records no reason, cra24 emits fixed and flags requires_human, rather than a quiet all-clear.

Kernel config gating can only downgrade, never upgrade. A CVE in a driver you never compiled is not your CVE — but the absence of gating data is never read as "not gated". That asymmetry is deliberate; the opposite one is how a triage tool becomes a liability.

Justifications come from the closed OpenVEX vocabulary, never free text, because a free-text justification is one a downstream scanner cannot act on. The Yocto CVE_STATUS reason strings are mapped to it explicitly in triage.py, and that table is where most of the false-positive reduction actually lives.


The field spec is data, not code

src/cra24/data/srp-fields.json carries the SRP field set, versioned, with its sources and — importantly — its known disagreements, where public guides contradict each other about what is mandatory at 24 hours.

When ENISA moves the form, and it will, the fix is a data edit. You can patch it yourself without waiting for a release:

cra24 spec --stage early_warning -v

If you find a drift, please open a field spec drift issue. That file should be the most accurate public description of the form that exists, and it only gets there if people report what they see.


Documentation

Document What it covers
docs/architecture.md The canonical-model design, and how to add an ingester or emitter
docs/feeds.md The five feeds, what each answers, and the air-gapped workflow
docs/watch.md Watch mode, notifiers, and the systemd unit
docs/article-14.md The regulation mapped to the code, clause by clause
docs/triage-rules.md Every rule, in order, with what it can and cannot conclude
docs/srp-field-spec.md The field spec, its sources, and how to correct it
docs/evidence.md What the ledger proves, what it does not, and how to anchor it
docs/cli.md Every command, flag and exit code
docs/roadmap.md Where this is going and why
LICENSING.md The dual-licence model in plain words

Licensing, briefly

cra24 is dual-licensed: AGPL-3.0-only, or a commercial licence.

Using cra24 to produce your own compliance dossiers is free, forever, for any company of any size. Running the CLI on your build is not distribution and the AGPL asks nothing of you.

You need a commercial licence only if you ship cra24 inside a product you distribute, or offer it to others over a network, and do not want to publish your source. That is the entire business model, stated plainly. See LICENSING.md.

Contributions require a CLA, which is what makes the dual licence possible. You keep your copyright; the project undertakes in writing that your contribution stays available under the AGPL.


Feeds, and the question they answer

Article 14(1) turns on active exploitation, not severity. That is the hardest input to supply at 3am, and 0.3.0 supplies it:

CVE-2024-1086 in AcmeGateway 2.4.0: affected

  linux-raspberrypi 6.6.22 -> affected
    · advisory-version-range: within the affected range
      6.6.22 is inside [introduced 3.15, fixed 6.6.25]
    · feed:cisa-kev: actively exploited
      listed in CISA KEV on 2024-05-30
    · feed:osv: fixed in 6.6.25

REPORTABLE — Article 14(1): the vulnerability is contained in the product
and is actively exploited. The 24-hour clock is running.

Five feeds: CISA KEV and ENISA EUVD for exploitation, OSV for version ranges, NVD for CVSS, EPSS for ordering your queue.

Three commitments they keep:

  • cra24 makes no network call you did not ask for. No telemetry, no update checks. cra24 feeds status names every host that would be contacted, without contacting it.
  • Offline is a first-class mode. Sync a cache where there is a network, tar it, copy it to the build machine. --offline never opens a socket and tells you how old the cache is instead of pretending it is current.
  • Absence from KEV is recorded as unknown, never as not exploited. KEV is conservative and US-federal; a customer reporting an attack starts your clock whether or not CISA has heard of it.

And feeds never overturn you — --actively-exploited no wins over KEV, because that is a human decision.

See docs/feeds.md.

Watch mode

cra24 watch --products products/ --once --notify email:psirt@example.com

The thing waiting for you at 07:00 is not an alert. It is a dossier with four fields left to fill and a clock that started at 03:14.

It fires once per finding, never rewrites an awareness timestamp it has already set, and survives a bad product file or a dead webhook. Exit code 1 means a reportable finding, so cron can act on it without parsing text. A hardened systemd unit and timer are in contrib/systemd/.

See docs/watch.md.

Status and roadmap

0.3.0 — everything in 0.2.0, plus five offline-capable feed clients, watch mode, notifiers and a systemd unit.

0.2.0 — Yocto and Buildroot ingest, SBOM ingest, kernel config gating, the triage engine, schema-validated CSAF and OpenVEX, hash-chained evidence.

Next, in order:

  1. CI integration — a GitHub Action and a GitLab template, deliberately Apache-2.0 rather than AGPL, because they are clients and permissive removes every excuse not to adopt them.
  2. Service and dashboard — the same engine behind a REST API and a local UI, self-hostable, EU-hosted.
  3. Curated VEX feed — triage decisions on the top few hundred embedded components, published as OpenVEX, plus the mapping of kernel CVEs to the config symbols that gate them. Timesys built exactly this and sold it to Lynx; nobody serves the shop building 2,000 units a year.

See docs/roadmap.md.


Caveats worth keeping in the README

  • The field spec mirrors the published required set. Verify it against ENISA's AR User Manual for your own account. The form is young.
  • Registration cannot be done retroactively. Named representatives need working EU Login accounts with MFA before an incident, and the coordinator CSIRT is chosen once — selecting the wrong one invalidates a notification.
  • patched in build metadata means a layer applied a patch, not that you are safe. Keep the human in the loop. cra24 is built to make that easy, not to remove it.
  • Article 14(1) turns on active exploitation, not on severity. An unpatched critical CVE with no exploitation evidence is a thing to fix under Annex I Part II, not a thing to file an early warning about. cra24 will tell you so.

Metadata

Release files for cra24 1.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cra24 1.0.1
File Size Uploaded
cra24-1.0.1.tar.gz 188.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for cra24 1.0.1
File Interpreter ABI Platform
cra24-1.0.1-py3-none-any.whl Python 3 none any Details

Total release size: 327.5 kB

Release files / cra24-1.0.1.tar.gz

Download URL cra24-1.0.1.tar.gz
Size 188.4 kB
Tags Source
SHA-256 checksum
How to use checksums
486b4c47ed4e7252305ec343661536acdd78aef23395195c697abe475ea2f221
BLAKE2b-256 checksum
How to use checksums
be422c43ae56897f4cf5b332ade6e37690a319bb91de15db348f65b9ca3744b0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.

Transparency log

Release files / cra24-1.0.1-py3-none-any.whl

Download URL cra24-1.0.1-py3-none-any.whl
Size 139.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4b85f49bc9d004f08c9829e8631a0c0a8e1fa4129f4a0e08b4d42dd40b80106d
BLAKE2b-256 checksum
How to use checksums
1458a313597127dc37e218645c30a0bc003fa890c498e95792e133d5e175b225
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.

Transparency log

Release history Release notifications | RSS feed

1.1.0

2 release files

This release

1.0.1 This release

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page