Concluded·28 Aug 2026 · 5 min read
What a plant stack is actually made of
syft 1.51.1 · grype 0.118.0 · CISA KEV catalogue 2026-08-27 · Ignition 8.3.8, Node-RED 4.1, EMQX, Mosquitto 2.0.22, N3uron 1.22.4, TimescaleDB pg16, Grafana
The EU Cyber Resilience Act's reporting obligation starts 11 September 2026. It does not ask whether you are secure. It asks whether you can determine, within 24 hours, that a product you placed on the market contains a vulnerability somebody is actively exploiting.
That is an inventory question, so I measured the inventory.
The rig
| Subject | seven products forming a small plant stack |
| SBOM | syft 1.51.1, run against local images via the Docker socket |
| Vulnerabilities | grype 0.118.0 |
| Exploited-in-the-wild | CISA Known Exploited Vulnerabilities catalogue, 1,685 entries, fetched 2026-08-27 |
| Exploit prediction | EPSS scores as carried in grype's output |
$ syft -q docker:inductiveautomation/ignition:8.3.8 -o json
$ grype -q docker:inductiveautomation/ignition:8.3.8 -o json
No agents, no registry account, no commercial scanner. Both tools are run as containers against images already on the host.
What is in there
| product | components | matches | distinct CVEs | critical | high |
|---|---|---|---|---|---|
| Grafana | 796 | 65 | 13 | 0 | 43 |
| Node-RED 4.1 | 763 | 194 | 94 | 9 | 102 |
| N3uron 1.22.4 | 551 | 312 | 154 | 13 | 61 |
| Ignition 8.3.8 | 429 | 286 | 155 | 1 | 12 |
| EMQX | 96 | 191 | 106 | 15 | 50 |
| TimescaleDB pg16 | 80 | 211 | 74 | 0 | 125 |
| Mosquitto 2.0.22 | 20 | 27 | 15 | 0 | 17 |
| total | 2,735 | 1,286 | 342 distinct |
Ignition's 429 break down as 288 Java archives, 127 Debian packages, 9 binaries, 4 Go modules and one Python package — which is worth knowing on its own, because "a Java application" turns out to mean 288 separately-versioned things.
Note that matches exceed distinct CVEs everywhere: the same vulnerability is frequently found in several components of the same image.
The intersection that matters
The CRA clock is triggered by active exploitation, not by a CVE existing. The closest public proxy is CISA's Known Exploited Vulnerabilities catalogue.
342 distinct CVEs. 1,685 KEV entries. Intersection: zero.
EPSS agrees. Exactly one CVE in the stack scores above 0.5 predicted probability of exploitation:
epss=0.733 pct=0.994 CVE-2011-3389 libgnutls30t64 (n3uron)
epss=0.070 pct=0.938 CVE-2017-17740 libldap2 (n3uron)
epss=0.053 pct=0.920 CVE-2015-3276 libldap2 (n3uron)
CVE-2011-3389 is BEAST — a TLS 1.0 CBC weakness from 2011, mitigated in practice by everything that has since moved to TLS 1.2+.
An honest caveat on the zero. This is one stack on one day. KEV grows weekly, and a CVE moving onto it is precisely the event the 24-hour clock exists for. The finding is not "this stack is safe" — it is that the reportable set today is empty, and that the check to establish this is cheap and repeatable.
Age distribution, or: this is not a museum
| period | CVEs | share |
|---|---|---|
| 2026 | 275 | 80% |
| 2025 | 34 | 10% |
| 2021–2024 | 12 | 4% |
| 2020 and earlier | 21 | 6% |
The oldest is from 2005, the newest from this year. The industrial-software-is- ancient story does not survive contact with the dates: 90% of what a scanner flags here was assigned in the last two years.
This is a measurement of advisory activity, not of rot. A component with an active security team generates CVEs; an abandoned one generates silence. Reading the count as a danger score gets the sign wrong.
The duplication problem
This is the finding that would actually cost you the 24 hours.
tar3 versionsbusybox2 versionsopenssl2 versionsutil-linux3 versionsperl-base2 versionslibssl3consistent63 CVE-carrying components appear in more than one of the seven products. 50 of those 63 appear at more than one version.
| component | products | versions present |
|---|---|---|
tar | 4 | 1.35+dfsg-3.1, 1.35+dfsg-3build1, 6.2.1 |
busybox | 4 | 1.37.0-r30, 1.37.0-r31 |
libssl3 | 4 | 3.5.7-r0 |
openssl | 3 | 3.5.6-1~deb13u2, 3.5.7-r0 |
util-linux | 3 | 2.39.3-9ubuntu6.5, 2.41-5, 2.41.5-0+deb13u1 |
perl-base | 3 | 5.38.2-3.2ubuntu0.2, 5.40.1-6 |
zlib1g | 3 | two Debian/Ubuntu builds |
When an advisory names openssl 3.5.6, the answer to "do we ship that?" is
neither yes nor no. It is yes in one product, no in another, and the difference
is a base-image choice made by a vendor you do not control.
That is why the inventory has to be a stored artifact rather than a question you answer by thinking. Under time pressure, from memory, nobody gets this right.
Cost of the census
| stage | time |
|---|---|
| Ignition 8.3.8 (429 components) | 113 s |
| Grafana + EMQX | 72 s |
| TimescaleDB | 46 s |
| Node-RED | 30 s |
| N3uron | 13 s |
| Mosquitto | 2 s |
| total | 4 m 36 s |
Against a 24-hour reporting obligation, the technical work is four and a half minutes and produces a file. Everything else about CRA compliance is organisational.
One failure worth recording
The first EMQX scan produced a zero-byte result and a non-obvious error:
ERROR failed to catalog: an error occurred attempting to resolve
'emqx/emqx:latest': docker: unable to provide image from tarball:
file blobs/sha256/37416ecf…4c not found in tar
The locally cached image was missing a layer blob. A re-pull fixed it. Worth noting because a scanner that silently produces nothing for one product of seven is exactly how an inventory ends up 14% short without anybody noticing — the failure mode is an empty file, not an error in a report.
Verdict
The obligation is answerable and the tooling is free. The number to watch is not the CVE count, which measures how carefully your suppliers are looking; it is the intersection with what is actually being exploited, which today is empty and next month may not be.
Do the boring thing: generate the inventory, date it, store it, and reconcile the fifty components that exist at more than one version before an advisory forces you to do it in a hurry.
The argument for a general audience is in nobody knows what software is inside your plant.
Newsletter
New essays, by email.
SCADA, cloud, AI, and the plant floor — a short email when something new is published. No noise, unsubscribe anytime.