I spent this morning reading the CISA KEV catalogue against a dozen real repositories, and I want to write down something that surprised me — because I think it's the thing most teams are going to get wrong about the EU Cyber Resilience Act.
The setup
Since 11 September 2026, Article 14 of the CRA has been enforceable. If a vulnerability in a product you've placed on the EU market is being actively exploited, you owe an early warning to your national CSIRT and ENISA within 24 hours.
Everyone reading that sentence hears "vulnerability" and thinks about their scanner output. That's the mistake.
What I actually found
I scanned a real project this morning — 649 distinct components across a package-lock.json and a Cargo.lock. The result:
Advisories 13 component(s) carry at least one known advisory
KEV feed 1709 entries, catalogue 2026.09.11
NO KEV MATCHES.
Thirteen components with published advisories. Zero on the known-exploited list.
Now, thirteen advisories would light up any dependency scanner. If you were treating scanner output as your CRA trigger, you'd have thirteen things that look like they might start a 24-hour clock. In reality, on this evidence, you have none.
The inverse is also true and more dangerous. I built a fixture with log4j-core 2.14.1 in it:
2 KEV MATCH(ES) - MANUAL DECISION REQUIRED NOW
CVE-2021-44228 (GHSA-jfh8-c2jp-5v3q)
component org.apache.logging.log4j:log4j-core 2.14.1 [Maven]
known as Apache Log4j2 Remote Code Execution Vulnerability
KEV listed 2021-12-10 ransomware use: Known
One component. Two known-exploited CVEs. That's the one that deserves a human in the room, and it would have been item #7 of 13 in a severity-sorted list.
Severity is not exploitation
This is the whole point and it's worth being blunt about it:
- CVSS 9.8 describes how bad it would be if exploited.
- KEV listing describes that it is being exploited, in the real world, right now.
Article 14 turns on the second one. Most vulnerability tooling optimises for the first. So the list you've been living with — sorted by severity, triaged by "critical/high/medium" — is not the list that answers the regulatory question. It's a different question with a different answer set, and the overlap is smaller than you'd guess.
There's a second-order effect that I think is underrated: treating every critical CVE as a potential reporting trigger doesn't just waste time, it produces over-reporting, and over-reporting to a regulator is its own kind of problem. You want the small true list, not the big cautious one.
The part where I'm honest about the limits
I wrote a tool to do the intersection (cra-watch, MIT, stdlib-only, no account). But I want to be straight about what it can't do, because compliance tooling that overclaims is worse than no tooling:
A KEV listing doesn't mean you must report. KEV says a vulnerability is being exploited somewhere in the world. Article 14 asks whether it's being exploited in your product. If the vulnerable code path isn't reachable in your build, the answer may genuinely be no. The Commission's July 2026 guidance is explicit that reachability matters for third-party components. No scanner can make that call — a person has to.
KEV isn't the legal definition either. It's a US government catalogue. It lags, and it isn't exhaustive. Your own telemetry or a customer's incident report can start the clock before CISA ever lists the CVE.
Clean output isn't a certificate. It means two public sources don't currently intersect for your dependency graph. Useful, dated, reproducible. Not immunity.
What I'd actually do on Monday
- Run the intersection, not the severity sort. You want the short list, and it probably is short.
- Put it on a schedule, not on push. This is the bit people miss. The KEV catalogue gains entries continuously. A repo that was clean yesterday can become a reporting question today with zero code changes on your side. A one-off audit answers a question about the past.
- Decide who presses send, before you need to know. Under a 24-hour clock, the most common failure isn't technical — it's four people each assuming someone else owns the decision.
- Write down what you knew and when. Whatever you decide, the contemporaneous record is what defends it later. A documented decision not to report is defensible. An undocumented one is very hard to defend.
The tool is free and I'd rather it were widely used than gated:
curl -fsSL https://raw.githubusercontent.com/ntoledo319/cra-watch/main/cra_watch.py -o cra-watch
chmod +x cra-watch && ./cra-watch scan
Three lines in CI:
- uses: ntoledo319/cra-watch@v1.0.0
with:
fail-on-match: 'false'
If you find a case where it gets the intersection wrong, open an issue — I'd rather fix it than be confidently wrong in public.
Not a lawyer, not legal advice. Verify against Regulation (EU) 2024/2847 and your coordinating national CSIRT's guidance before filing anything. I also sell a process kit for the decision-and-filing side; the scanner is free and always will be, and it isn't crippled or time-limited.
Top comments (0)