CRA Sentinel
Cyber Resilience Act compliance for products with digital elements. Zero dependencies. One command. Evidence an auditor will accept.
pip install cra-sentinel
cra scan . --report cra-report.html --sbom sbom.cdx.json
Why this exists
Regulation (EU) 2024/2847 applies to every manufacturer placing a product with digital elements on the EU market — including manufacturers established outside the EU, and including products already on the market.
| Date | Obligation |
|---|---|
| 11 September 2026 | Article 14: report actively exploited vulnerabilities within 24 hours to ENISA and the national CSIRT, full notification within 72 hours, final report within 14 days of a corrective measure |
| 11 June 2027 | Conformity assessment provisions |
| 11 December 2027 | Full application: Annex I essential requirements, CE marking |
Most manufacturers cannot comply, for a mundane reason: they have no SBOM, no vulnerability-monitoring process, and no record of the decisions they have made. The regulation does not require you to be free of vulnerabilities. It requires you to know what is in your product, watch it, decide, and be able to show the decision.
That last part is what tools usually skip. A vulnerability scan is not evidence. A documented triage decision is.
What it does
Builds the inventory. Resolves components from npm, PyPI, Go, Cargo, Maven, Composer, RubyGems, NuGet — lockfiles preferred over manifests, because the SBOM must describe what ships, not what was requested.
Separates "vulnerable" from "actively exploited". Every finding is enriched from OSV.dev, then intersected with the CISA Known Exploited Vulnerabilities catalogue. Only that intersection starts an Article 14 clock, and the tool says so explicitly rather than drowning you in 200 undifferentiated CVEs.
Computes its own severity. CVSS v3.1 base scores are calculated locally from the vector, validated against the published reference values, because under Annex I you must be able to justify your own severity assessment.
Assesses 14 controls against the actual text. Each one names its article — Article 14(1)(a), Annex I Part II(2), Article 13(8) — and reports Pass, Partial, Fail, or Attest for what no tool can verify. Manual checks are excluded from the score rather than silently counted as failures.
Keeps an append-only evidence ledger. Every triage decision is recorded with
its rationale, author and timestamp. A not_affected verdict is refused without
a CSAF justification, because an auditor will ask for one.
Exports the four formats that matter. CycloneDX 1.6 (with VEX analysis inline), SPDX 2.3, CSAF 2.0 advisories, and a pre-filled Article 14 notification draft so the 24-hour window is spent deciding, not formatting.
Commands
cra scan . # scan and assess
cra scan . --report r.html --sbom sbom.cdx.json --csaf advisory.json
cra scan . --fail-on exploited # CI gate: non-zero if a KEV component ships
cra scan . --fail-on score --min-score 75
cra init --contact security@acme.eu --ci
# scaffold SECURITY.md, the Article 14 runbook,
# security.txt and a CI workflow
cra triage CVE-2021-44228 --status affected \
-r "log4j-core is bundled in the firmware; JNDI reachable from the syslog parser" \
--plan "Upgrade to 2.17.1 in 3.2.2" --target-date 2026-09-24
cra triage CVE-2021-45046 --status not_affected \
--justification vulnerable_code_not_in_execute_path \
-r "Lookups disabled at build time via log4j2.formatMsgNoLookups"
cra ledger --history # the audit trail
cra notify CVE-2021-44228 # Article 14 early-warning draft
cra advisory --only-exploited # CSAF 2.0 advisory to publish
cra watch --fail-on-new # diff against baseline; alert on new exploitation
cra serve # local dashboard for non-terminal colleagues
cra deadlines # where you are in the timeline
Exit codes
0 clean · 1 threshold breached · 2 error. Use --fail-on to turn a scan
into a release gate.
Design decisions worth knowing
Zero dependencies. Standard library only. It installs inside an air-gapped build environment and it will still run in 2031, which matters when the support period you declared is five years.
--offline is real. Advisory data is cached to .cra-sentinel/cache/. On a
network failure the scan degrades to the cache and says so in the report rather
than silently reporting zero findings.
A malformed manifest never aborts a scan. A compliance scan that crashes on
one bad pom.xml is worse than useless in CI.
The HTML report is one self-contained file. No CDN, no fonts, no tracking. It survives being emailed to a notified body and printed to PDF. Dark and light themes, readable at 400px.
Nothing leaves your machine except package coordinates. Component names and versions are sent to OSV.dev to query advisories. No source code, no telemetry, no account.
Continuous monitoring
A scan you run by hand is not monitoring. cra watchtower sweeps a portfolio
of products on a schedule, records the moment of awareness when a component
becomes actively exploited — the fact from which every Article 14 deadline runs
— and alerts by email or webhook, escalating until someone acknowledges.
cra watchtower init
cra watchtower add "Acme — Gateway 3.2.1" --source https://github.com/acme/gw.git
cra watchtower run # put this on a timer
cra watchtower status # what is on the clock right now
cra watchtower ack acme-gateway-321 CVE-2021-44228 --by tom --note "not affected"
cra watchtower dashboard # portfolio view
The awareness timestamp is written before delivery is attempted: if SMTP is
down the alert does not arrive, but the evidence still exists. Deployment units
for systemd and cron are in deploy/.
Troubleshooting
TLS certificate verification failed on macOS. Python installed from
python.org ships without a populated system trust store. CRA Sentinel falls back
to certifi automatically when it is importable; otherwise run the installer's
one-time fix:
/Applications/Python\ 3.x/Install\ Certificates.command
# or
pip install certifi
The scanner never reports a clean result it could not verify — a scan with no reachable advisory source is marked Unverified in both the terminal and the report, and the affected controls report Attest rather than Pass.
Python version. Requires 3.10 or later. Tested on 3.10 and 3.14.
Scope and limitations
This covers third-party component risk — the part of Annex I that is mechanically checkable. It does not assess first-party source code, hardware, cryptographic implementation, or the organisational measures in Annex I Part I. Checks marked Attest remain the manufacturer's responsibility, and the tool says so in the report rather than implying coverage it does not have.
Automated output supports, and does not replace, the technical documentation required under Annex VII. It is not legal advice.
Development
python3 tests/test_suite.py # 33 tests, offline, no network required
Licence
MIT.
Metadata
Release files for cra-sentinel 1.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| cra_sentinel-1.0.1.tar.gz | 80.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cra_sentinel-1.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 161.6 kB
Release files / cra_sentinel-1.0.1.tar.gz
| Download URL | cra_sentinel-1.0.1.tar.gz |
|---|---|
| Size | 80.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
65dcf786af1016f574f714be5716e1a015c18e87e1da218bfe1aa81ffe5b617d
|
|
BLAKE2b-256 checksum How to use checksums |
ceadc89971373f184d7a235292660d707b34d383b35391ac2650934c71c6585d
|
| 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 12, 2026.
Transparency logRelease files / cra_sentinel-1.0.1-py3-none-any.whl
| Download URL | cra_sentinel-1.0.1-py3-none-any.whl |
|---|---|
| Size | 81.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3ce92b9e4d7f075ddf4aab8834060870fd333435b6e89146b48c07076375ae6d
|
|
BLAKE2b-256 checksum How to use checksums |
c787b75a4942b3660286f2186bca27090564e2b9449ccff1de70df0dc4c83830
|
| 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 12, 2026.
Transparency log