File size: 45,036 Bytes
2fda961 3c7e826 2fda961 25b3f56 2fda961 3c7e826 2fda961 3c7e826 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | <!doctype html><html lang="en"><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>CRA Readiness Audit for EU Cyber Resilience Act</title>
<meta name="description" content="Flags the lines in your repo that break the EU Cyber Resilience Act - default passwords, disabled TLS checks, floating base images, an expired security.txt - naming the Annex each one fails.">
<style>
:root{color-scheme:light dark}
body{margin:0;font:16px/1.6 system-ui,-apple-system,"Segoe UI",sans-serif;
background:#fbfbfa;color:#16161d}
@media(prefers-color-scheme:dark){body{background:#14141a;color:#e8e8ea}}
.wrap{max-width:880px;margin:0 auto;padding:28px 20px 64px}
h1{font-size:1.7rem;line-height:1.25;margin:.2em 0 .3em}
p.sub{margin:0 0 1.4em;opacity:.8}
textarea{width:100%;min-height:230px;font:13px/1.5 ui-monospace,Menlo,Consolas,monospace;
padding:12px;border:1px solid #c9c9d1;border-radius:10px;background:transparent;color:inherit;
box-sizing:border-box}
button{font:600 15px/1 inherit;padding:12px 22px;border:0;border-radius:10px;
background:#1d4ed8;color:#fff;cursor:pointer;margin:12px 0}
button:hover{background:#1a43bd}
table{border-collapse:collapse;width:100%;margin-top:14px;font-size:14px}
th,td{border-bottom:1px solid #d8d8e0;padding:8px 6px;text-align:left;vertical-align:top}
th{font-weight:600;opacity:.75}
td.n{width:64px;font:13px ui-monospace,monospace;opacity:.7}
.ok{padding:14px;border-radius:10px;background:#e8f5ec;color:#14532d;margin-top:14px}
@media(prefers-color-scheme:dark){.ok{background:#15321f;color:#bbf7d0}}
.next{margin-top:30px;padding:18px;border:1px solid #d8d8e0;border-radius:12px}
.next h2{font-size:1.05rem;margin:0 0 .5em}
.cta{margin-top:18px;padding:16px 18px;border:1px solid #1d4ed8;border-radius:12px}
.cta p{margin:0 0 .6em}
.cta a.buy{display:inline-block;font:600 15px/1 inherit;padding:12px 22px;border-radius:10px;background:#1d4ed8;color:#fff;text-decoration:none}
.cta p.terms{margin:.8em 0 0;font-size:13px;opacity:.75}
.next ul{margin:.3em 0 1em 1.1em;padding:0}
</style>
<div class="wrap">
<h1>CRA Readiness Audit for EU Cyber Resilience Act</h1>
<p class="sub">Flags the lines in your repo that break the EU Cyber Resilience Act - default passwords, disabled TLS checks, floating base images, an expired security.txt - naming the Annex each one fails.</p>
<textarea id="src" placeholder="Paste a Dockerfile, security.txt, manifest, CI workflow or source file here"></textarea>
<button id="go">Check this file</button>
<div id="out"></div>
<div class="yj-home-find"><div class="yj-lgbar"><a href="/" class="yj-lg on">EN</a><a href="/de/" class="yj-lg">DE</a><a href="/ja/" class="yj-lg">JA</a><a href="/es/" class="yj-lg">ES</a><a href="/pt/" class="yj-lg">PT</a></div><section class="yj-find"><h2>Find a tool</h2><input id="yjq" type="search" autocomplete="off" placeholder="Type a product name, format or task"><div id="yjr"></div></section><style>.yj-lgbar{position:absolute;top:14px;right:18px;display:flex;gap:6px;z-index:60}.yj-lg{font:600 12px/1 system-ui,sans-serif;padding:6px 9px;border-radius:4px;text-decoration:none;color:#8b96a3;border:1px solid rgba(140,150,165,.28)}.yj-lg:hover{color:#e8eaed;border-color:rgba(160,175,195,.6)}.yj-lg.on{color:#0e1116;background:#cfd6de;border-color:#cfd6de}.yj-find{max-width:760px;margin:34px auto 8px;padding:0 20px}.yj-find h2{font:600 20px/1.3 system-ui,sans-serif;margin:0 0 10px}#yjq{width:100%;box-sizing:border-box;padding:13px 15px;font:400 15.5px system-ui,sans-serif;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:rgba(255,255,255,.04);color:inherit}#yjq:focus{outline:none;border-color:#7FD1C4}#yjr{margin-top:10px;display:flex;flex-direction:column;gap:6px}#yjr a{display:flex;justify-content:space-between;gap:12px;padding:11px 14px;border:1px solid rgba(140,150,165,.22);border-radius:6px;text-decoration:none;font:400 14.5px/1.4 system-ui,sans-serif;color:inherit}#yjr a:hover{border-color:#7FD1C4}#yjr .pr{font:600 13px system-ui,sans-serif;color:#5fbf9a;white-space:nowrap}</style><script>(function(){var q=document.getElementById("yjq"),r=document.getElementById("yjr"),D=null;function load(){if(D)return Promise.resolve(D);return fetch("/search-index.json").then(function(x){return x.json();}).then(function(j){D=j;return j;}).catch(function(){D=[];return D;});}function esc(t){return String(t).replace(/[&<>"]/g,function(c){return {"&":"&","<":"<",">":">",'"':"""}[c];});}function run(){var v=q.value.trim().toLowerCase();if(v.length<2){r.innerHTML="";return;}load().then(function(j){var w=v.split(/\s+/),o=[];for(var i=0;i<j.length&&o.length<12;i++){var t=j[i].t.toLowerCase();var ok=true;for(var k=0;k<w.length;k++){if(t.indexOf(w[k])<0){ok=false;break;}}if(ok)o.push(j[i]);}r.innerHTML=o.length?o.map(function(x){return "<a href=\""+esc(x.u)+"\">"+"<span>"+esc(x.t)+"</span>"+(x.p?"<span class=\"pr\">$"+x.p+"</span>":"")+"</a>";}).join(""):"";});}q.addEventListener("input",run);q.addEventListener("focus",load);})();</script></div><div class="cta" id="cta" hidden><p>Sweep every file in the repository in one pass, write the findings report you keep with the Annex VII technical documentation or hand to the customer who asked, and fail the build in CI when a new commit reintroduces a default password or an unpinned base image.</p>
<a href="https://getreadystack.com/api/buy/cl/polar_cl_isVXdaMI7ZlmmjzagEfs13ZoOJs3Y1ctvxmYt22iEPr" class="buy">Get the full version - $29</a>
<p class="terms">$29 once · one licence key per person or team seat · Blended EU engineering-and-consulting effort on CRA work is estimated at EUR 45 an hour, and the cheapest tooled route published runs EUR 4,000 per project per year.</p></div>
<div class="next"><h2>In the full version</h2><ul><li>Check every file in the repository</li><li>Findings report as CSV, JSON or HTML</li><li>JSON output a build step can fail on</li><li><a href="https://getreadystack.com/api/buy/cl/polar_cl_isVXdaMI7ZlmmjzagEfs13ZoOJs3Y1ctvxmYt22iEPr">Get the full version</a></li></ul></div>
</div>
<script>
var RULES=[{"pattern": "NODE_TLS_REJECT_UNAUTHORIZED\\s*[=:]\\s*[\\\"']?0\\b", "flags": "", "sev": "error", "message": "TLS certificate verification is switched off process-wide. Annex I Part I(2) requires the product to be delivered with a secure by default configuration; this line makes every outbound HTTPS call trust any certificate.", "fix": "Remove the variable and pin the CA instead: NODE_EXTRA_CA_CERTS=/path/to/ca.pem"}, {"pattern": "rejectUnauthorized\\s*:\\s*false", "flags": "", "sev": "error", "message": "Node TLS peer verification disabled on this request. Annex I Part I(2)(e) requires the confidentiality and integrity of transmitted data to be protected; an unverified peer defeats both.", "fix": "rejectUnauthorized: true, ca: fs.readFileSync('ca.pem')"}, {"pattern": "verify\\s*=\\s*False\\b", "flags": "", "sev": "error", "message": "Python request sent with certificate verification disabled. Annex I Part I(2)(e): transmitted data must be protected in transit. This is the single most copied insecure default in generated code.", "fix": "verify='/etc/ssl/certs/ca-certificates.crt'"}, {"pattern": "InsecureSkipVerify\\s*:\\s*true", "flags": "", "sev": "error", "message": "Go TLS config skips certificate verification. Annex I Part I(2)(e) requires protected transmission; this accepts any presented certificate, including an interception proxy.", "fix": "InsecureSkipVerify: false, RootCAs: pool"}, {"pattern": "\\bcurl\\b[^\\n]*\\s(?:-k|--insecure)\\b", "flags": "", "sev": "error", "message": "curl invoked with verification disabled. When this runs in a build or an update path it is an unauthenticated code fetch, which Annex I Part I(2)(c) treats as a broken secure update mechanism.", "fix": "curl --proto '=https' --tlsv1.2 -sSf https://..."}, {"pattern": "(?:ssl_verify|sslVerify|CURLOPT_SSL_VERIFYPEER)[\\\"\\']?\\s*[=:]\\s*[\\\"\\']?(?:false|0|off)\\b", "flags": "i", "sev": "error", "message": "Certificate verification disabled in configuration. Annex I Part I(2)(e). A setting shipped in this state is the default the customer receives, which is what the CRA judges.", "fix": "Set the value to true and supply the CA bundle path"}, {"pattern": "(?:password|passwd|pwd)\\s*[=:]\\s*[\\\"'](?:admin|password|changeme|change_me|root|123456|secret|test|letmein)[\\\"']", "flags": "i", "sev": "error", "message": "A well-known default password is hard-coded. Annex I Part I(2)(b) requires products to ship without a default password unless it is unique per device and forced to change on first use.", "fix": "Read the value from the environment and require a change on first login"}, {"pattern": "DEFAULT_(?:PASSWORD|PASSWD|CREDENTIALS|SECRET|API_KEY)\\s*[=:]", "flags": "", "sev": "error", "message": "A default credential is baked into the shipped configuration. Annex I Part I(2)(b) forbids this outright; it is one of the few CRA requirements written as an absolute rather than a risk judgement.", "fix": "Generate a unique secret at first start and store it outside the image"}, {"pattern": "-----BEGIN (?:RSA |EC |DSA |OPENSSH |PGP )?PRIVATE KEY-----", "flags": "", "sev": "error", "message": "Private key material is committed in the source tree. It reaches every clone and every layer of the built image, and it lands in the SBOM's provenance rather than in a key store.", "fix": "Remove the key, rotate it, and mount it at runtime from a secret store"}, {"pattern": "\\b(?:AKIA|ASIA)[0-9A-Z]{16}\\b", "flags": "", "sev": "error", "message": "An AWS access key id is committed. Once published this is an actively exploitable credential, and exploitation of it becomes a reportable event under Article 14 once you become aware.", "fix": "Rotate the key in IAM and load credentials from the instance role"}, {"pattern": "\\bgh[pousr]_[A-Za-z0-9]{36}\\b", "flags": "", "sev": "error", "message": "A GitHub personal access token is committed. Treat it as burned: rotate first, then remove. A token with write scope turns any leak into a supply-chain incident on your own releases.", "fix": "Revoke the token and use a short-lived OIDC token in the workflow"}, {"pattern": "^\\s*FROM\\s+\\S+:latest\\b", "flags": "i", "sev": "error", "message": "Base image pinned to :latest. The SBOM required by Annex I Part II(1) has to name the components actually included; a floating tag means the bill of materials you generate does not describe the artefact you shipped.", "fix": "FROM node:22.11.0-bookworm-slim@sha256:<digest>"}, {"pattern": "^\\s*FROM\\s+[^:@\\s]+(?:\\s+[Aa][Ss]\\s+\\S+)?\\s*$", "flags": "", "sev": "warn", "message": "Base image has no tag, which Docker resolves as :latest. Same consequence as an explicit floating tag: the SBOM cannot be reproduced from the source.", "fix": "Add an explicit version tag and a digest"}, {"pattern": "[\\\"'][^\\\"']+[\\\"']\\s*:\\s*[\\\"']\\*[\\\"']", "flags": "", "sev": "error", "message": "Dependency accepts any version. Annex I Part II(1) requires an SBOM covering at least the top-level dependencies; a wildcard means the top-level dependency set changes without a commit.", "fix": "Pin the range and commit the lockfile"}, {"pattern": "^\\s*RUN\\s+[^\\n]*\\b(?:apt-get install|apk add|pip3? install|gem install)\\b(?![^\\n]*[=@][0-9])", "flags": "", "sev": "warn", "message": "Package installed without a pinned version. The resulting image is not reproducible, so the SBOM held in the technical documentation drifts from what customers actually run.", "fix": "apt-get install -y --no-install-recommends curl=7.88.1-10+deb12u5"}, {"pattern": "\\b(?:sbom|bill of materials)\\b[^\\n]{0,40}\\b(?:\\.txt|\\.md|\\.pdf|\\.xlsx?|\\.docx?)\\b", "flags": "i", "sev": "warn", "message": "The bill of materials appears to be produced in a human-readable format. Annex I Part II(1) requires it to be machine-readable; in practice that means CycloneDX or SPDX JSON.", "fix": "syft . -o cyclonedx-json=sbom.cdx.json"}, {"pattern": "^\\s*FROM\\s+node:(?:0|4|6|8|10|12|14|16|18)(?:[.\\-]|\\s|$)", "flags": "i", "sev": "error", "message": "Base image is an end-of-life Node release. It no longer receives security fixes, so you cannot meet the duty to supply security updates for the support period, and every new CVE in it is unfixable by you.", "fix": "FROM node:22-bookworm-slim"}, {"pattern": "^\\s*FROM\\s+python:(?:2\\.7|3\\.[0-8])(?:[.\\-]|\\s|$)", "flags": "i", "sev": "error", "message": "Base image is an end-of-life Python release. Article 13(5) requires due diligence on integrated components; shipping one that upstream has stopped patching fails it at the first audit question.", "fix": "FROM python:3.12-slim-bookworm"}, {"pattern": "^\\s*FROM\\s+(?:debian:(?:jessie|stretch|buster)|ubuntu:(?:14|16|18)\\.04|centos:[67])\\b", "flags": "i", "sev": "error", "message": "Base image is an end-of-life distribution. Its package feeds are frozen or gone, so the security updates Annex I Part II(8) obliges you to distribute cannot be built.", "fix": "FROM debian:bookworm-slim"}, {"pattern": "^\\s*Expires\\s*:\\s*(?:19\\d\\d|20[01]\\d|202[0-5])-", "flags": "i", "sev": "error", "message": "The Expires date in security.txt has already passed, which under RFC 9116 makes the whole file invalid. Your coordinated disclosure channel under Annex I Part II(5) is therefore not published at all.", "fix": "Expires: 2027-09-11T00:00:00.000Z"}, {"pattern": "^\\s*(?:Contact|Encryption|Policy)\\s*:\\s*[^\\n]*(?:example\\.(?:com|org|net)|CHANGEME|TODO|your[-_]?(?:email|domain))", "flags": "i", "sev": "error", "message": "The disclosure contact is still a placeholder. Annex I Part II(5) requires a policy with a working point of contact; a researcher who cannot reach you discloses publicly instead, and the 24-hour clock then starts from a tweet.", "fix": "Contact: mailto:security@yourdomain.tld"}, {"pattern": "^\\s*Contact\\s*:\\s*$", "flags": "i", "sev": "error", "message": "Empty Contact field in security.txt. RFC 9116 requires at least one, and Annex I Part II(5) requires the channel to actually exist.", "fix": "Contact: https://yourdomain.tld/security or mailto:security@yourdomain.tld"}, {"pattern": "(?:auto[-_]?updates?|autoUpdate|automatic[-_]?updates?)\\s*[=:]\\s*[\\\"']?(?:false|0|off|no|disabled)\\b", "flags": "i", "sev": "error", "message": "Automatic updates are off in the shipped configuration. Annex I Part I(2)(c) requires security updates to be distributed automatically where appropriate and the default state is what is assessed.", "fix": "Ship with automatic security updates enabled and let the operator opt out"}, {"pattern": "\\b(?:CRA|Cyber Resilience Act)\\b[^\\n]{0,80}\\b(?:applies|apply|applicable|in force|effective|deadline|starts?|from)\\b[^\\n]{0,25}\\b20(?:2[789]|3\\d)\\b", "flags": "i", "sev": "warn", "message": "This line dates the CRA at 2027 or later. That is right for most obligations, which apply from 11 December 2027, but the Article 14 duty to report actively exploited vulnerabilities and severe incidents to ENISA and your CSIRT has applied since 11 September 2026, and it covers products already on the market. State which of the two this line means.", "fix": "Reporting under Article 14: since 11 September 2026. Remaining obligations: 11 December 2027."}, {"pattern": "\\b(?:end[-\\s]?of[-\\s]?support|support(?:ed)?\\s+until|end[-\\s]?of[-\\s]?life|EOL)\\b[^\\n]{0,30}\\b20[23]\\d\\b", "flags": "i", "sev": "warn", "message": "A support end date is declared here. Check it against Article 13(8): the support period must reflect how long users can reasonably expect to use the product and may not fall below five years from placing on the market, and each security update, once issued, must stay available for ten years or the rest of the support period, whichever is longer.", "fix": "State the placing-on-market date next to it so the five-year floor is checkable"}, {"pattern": "(?:console\\.(?:log|info|debug|warn)|System\\.out\\.println|logger?\\.(?:info|debug|warn|error))\\s*\\([^)\\n]*\\b(?:password|passwd|secret|api[-_]?key|token|credential)\\b", "flags": "i", "sev": "error", "message": "A credential is written to the log. Annex I Part I(2)(k) asks for security-relevant activity to be recorded, not for secrets to be; logs are shipped to aggregators, support bundles and crash reports.", "fix": "Log the identifier only, never the secret: logger.info('auth ok for %s', userId)"}, {"pattern": "\\b(?:host|bind|listen|address)\\b[^\\n]{0,12}\\b0\\.0\\.0\\.0\\b", "flags": "i", "sev": "warn", "message": "The service binds to every interface. Annex I Part I(2)(d) requires the attack surface to be minimised, including exposed interfaces; bind to loopback unless external exposure is the documented intent.", "fix": "Bind to 127.0.0.1 and put the published port behind the reverse proxy"}, {"pattern": "^\\s*(?:ENV\\s+|export\\s+|-\\s+)?DEBUG\\s*[=:]\\s*[\\\"']?(?:True|true|1|on|yes)[\\\"']?\\s*$", "flags": "", "sev": "warn", "message": "Debug mode is on in the shipped configuration. Debug endpoints leak stack traces, configuration and sometimes secrets, which is the opposite of the secure by default state Annex I Part I(2) requires.", "fix": "DEBUG=false, and enable it only from a local override file"}];
function scan(t){var L=String(t).split(/\r?\n/),h=[],i,r,re;
for(i=0;i<L.length;i++){for(var k=0;k<RULES.length;k++){r=RULES[k];
try{re=new RegExp(r.pattern,r.flags||'')}catch(e){continue}
if(re.test(L[i]))h.push({line:i+1,msg:r.message})}}
return h}
document.getElementById('go').onclick=function(){
var t=document.getElementById('src').value,o=document.getElementById('out');
if(!t.trim()){o.innerHTML='';return}
var h=scan(t),c=document.getElementById('cta');
if(c)c.hidden=false;
if(!h.length){o.innerHTML='<div class="ok">No CRA finding in this file</div>';return}
var s='<table><tr><th>Line</th><th>Finding</th></tr>';
for(var i=0;i<h.length;i++){s+='<tr><td class="n">'+h[i].line+'</td><td>'+
String(h[i].msg).replace(/&/g,'&').replace(/</g,'<')+'</td></tr>'}
o.innerHTML=s+'</table>'};
</script>
</html>
<section data-yj-install="free" style="margin:2rem auto 1rem;max-width:46rem;padding:1.25rem 1.25rem;border:1px solid #cfe3d6;border-radius:12px;background:#f6fbf8;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .4rem;font-size:1.2rem">Install free</h2><p style="margin:0 0 .8rem;color:#41505f">Free for the file open in your editor - no key, no limit. The workspace sweep and the report ask for a key.</p><a href="https://www.npmjs.com/package/@readystack/cra-readiness-audit" rel="noopener" data-yj-install-door="npm" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'cra-readiness-audit',src:'npm',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">npm</a><a href="https://www.npmjs.com/package/@readystack/cra-readiness-audit" rel="noopener" data-yj-install-door="mcp" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'cra-readiness-audit',src:'mcp',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">MCP server (npx)</a><a href="https://hub.docker.com/r/getreadystack/cra-readiness-audit" rel="noopener" data-yj-install-door="docker" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'cra-readiness-audit',src:'docker',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">Docker Hub</a><pre style="margin:.5rem 0 0;padding:.6rem .8rem;background:#f3f5f8;border-radius:8px;font-size:.9rem;overflow:auto">npx @readystack/cra-readiness-audit <file>
npx @readystack/cra-readiness-audit --mcp
docker run --rm -v "$PWD:/w" getreadystack/cra-readiness-audit /w</pre><p style="margin:.6rem 0 0;font-size:.92rem;color:#41505f"><a href="https://getreadystack.com/api/buy/cl/polar_cl_l6iN1uWt0FwWu7tBsczD0jWpP2vxFM54Wdwqb3KPi1G" rel="nofollow" style="color:#0f7b3f;text-decoration:underline">Team? One key for every ReadyStack linter, 5 seats, $149 once</a></p></section>
<section data-yj-alert="rule" style="margin:1rem auto 1.5rem;max-width:46rem;padding:1.1rem 1.25rem;border:1px solid #d9dee6;border-radius:12px;background:#fbfcfd;font:15px/1.55 system-ui,-apple-system,Segoe UI,sans-serif"><b style="font-size:1.05rem">Get the full rule table as a PDF</b><div style="opacity:.78;margin:.35rem 0 .8rem">All 28 rules CRA Readiness Audit for EU Cyber Resilience Act checks, each with its source and date, on printable pages you can hand to your team or auditor. We email it now, and once more only if a rule changes. No newsletter.</div><div style="display:flex;gap:8px;flex-wrap:wrap"><input id="yjae" type="email" placeholder="you@example.com" style="flex:1;min-width:210px;padding:11px 13px;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:#fff;color:inherit"><button id="yjab" style="padding:11px 18px;border-radius:8px;border:0;cursor:pointer;background:#0f7b3f;color:#fff;font:600 15px system-ui,sans-serif">Email me the PDF</button></div><div id="yjao" hidden style="margin-top:.6rem;color:#0f7b3f">Sent. It arrives within the hour - check spam too.</div><script>(function(){var b=document.getElementById("yjab"),e=document.getElementById("yjae");if(!b||!e)return;b.addEventListener("click",function(){var v=e.value.trim();if(!/.+@.+\..+/.test(v))return;try{localStorage.setItem("yj_em","1");}catch(_){}fetch(location.origin+"/api/subscribe",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:v,slug:"cra-readiness-audit",lang:"en",source:"rule_table"+((b.closest&&b.closest("section")&&b.closest("section").getAttribute("data-yj-moved"))?"_at_result":"")})}).then(function(r){var o=document.getElementById("yjao");o.hidden=false;if(!r.ok){o.textContent="Could not save - please try again.";}}).catch(function(){var o=document.getElementById("yjao");o.hidden=false;o.textContent="Could not save - please try again.";});});})();</script><script>(function(){var e=document.getElementById("yjae"),s=e&&e.closest?e.closest("section"):null,o=document.getElementById("out");if(!s||!o)return;var d=false;function mv(ev){if(d)return;if(ev&&ev.target&&s.contains(ev.target))return;d=true;var b=o;while(b.parentNode&&b.parentNode!==document.body&&b.parentNode.tagName!=="MAIN"&&b.parentNode.tagName!=="ARTICLE")b=b.parentNode;if(!b.parentNode||b===s||b.contains(s))return;try{b.parentNode.insertBefore(s,b.nextSibling);s.setAttribute("data-yj-moved","1");}catch(_){}}document.addEventListener("input",mv,true);document.addEventListener("change",mv,true);})();</script></section>
<section data-yj-cta="paid" style="margin:3rem auto;max-width:46rem;padding:1.5rem 1.25rem;border:1px solid #d8dee9;border-radius:12px;background:#fbfcfe;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .5rem;font-size:1.25rem">Get the complete version <span class="yj-price">$29</span></h2><p style="margin:.25rem 0 1rem;color:#41505f">This page is the working piece. The full pack has everything below.</p><p style="margin:0 0 1rem;color:#41505f">Flags the lines in your repo that break the EU Cyber Resilience Act - default passwords, disabled TLS checks, floating base images, an expired security.txt - naming the Annex each one fails.</p><p style="margin:0 0 1rem;color:#5b6b7c;font-size:.94rem">Blended EU engineering-and-consulting effort on CRA work is estimated at EUR 45 an hour, and the cheapest tooled route published runs EUR 4,000 per project per year.</p><a href="https://getreadystack.com/api/buy/cl/polar_cl_isVXdaMI7ZlmmjzagEfs13ZoOJs3Y1ctvxmYt22iEPr" rel="nofollow" style="display:inline-block;padding:.7rem 1.3rem;border-radius:8px;background:#1b64f2;color:#fff;text-decoration:none;font-weight:600">Buy the full version — $29</a></section>
<!--yj-share-s101--><div style="margin:28px 0 8px;text-align:center"><button id="yjcp" type="button" style="font:inherit;padding:10px 18px;border:1px solid #8884;border-radius:10px;background:transparent;cursor:pointer">Copy link</button><button id="yjimg" type="button" style="font:inherit;padding:10px 18px;margin-left:8px;border:1px solid #8884;border-radius:10px;background:transparent;cursor:pointer">Save result as image</button></div><script>(function(){var b=document.getElementById("yjimg");if(!b)return;b.addEventListener("click",function(){var o=document.getElementById("out");var txt=(o&&(o.innerText||o.textContent)||"").trim();if(!txt){b.textContent="(no result yet)";setTimeout(function(){b.textContent="Save result as image";},1500);return;}var lines=txt.split(String.fromCharCode(10)).filter(function(x){return x.trim();}).slice(0,10);var W=1200,P=56,c=document.createElement("canvas");c.width=W;var lh=38,H=P*2+70+lines.length*lh+60;c.height=H;var x=c.getContext("2d");x.fillStyle="#1a1512";x.fillRect(0,0,W,H);x.fillStyle="#f3e9d2";x.font="600 34px system-ui,Segoe UI,Arial";var t=(document.title||"").split("|")[0].trim();x.fillText(t.slice(0,54),P,P+34);x.font="22px ui-monospace,Menlo,Consolas,monospace";x.fillStyle="#e8dcc4";for(var i=0;i<lines.length;i++){x.fillText(lines[i].slice(0,88),P,P+70+i*lh+22);}x.font="600 22px system-ui,Segoe UI,Arial";x.fillStyle="#c9a227";var d=location.hostname.replace("www.","");x.fillText(d,W-P-x.measureText(d).width,H-P+6);c.toBlob(function(bl){var u=URL.createObjectURL(bl),a=document.createElement("a");a.href=u;a.download=(t||"result").replace(/[^a-z0-9]+/gi,"-").toLowerCase()+"-"+d+".png";document.body.appendChild(a);a.click();setTimeout(function(){URL.revokeObjectURL(u);a.remove();},2000);},"image/png");});})();</script><script>(function(){var b=document.getElementById("yjcp");if(!b)return;var t=b.textContent;b.addEventListener("click",function(){var u=location.href.split("#")[0];function d(){b.textContent="Link copied";setTimeout(function(){b.textContent=t;},1800);}if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(u).then(d,function(){prompt("",u);});}else{prompt("",u);}});})();</script><script data-yj="yj-view-beacon">(function(){try{var p=location.pathname;var k='yjv:'+p;if(sessionStorage.getItem(k))return;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",ref:(document.referrer||''),src:(function(){try{return (new URLSearchParams(location.search).get('ref')||'').replace(/[^a-z_]/g,'').slice(0,12)}catch(e){return ''}})()})}).catch(function(){});}catch(e){}})();</script>
<script data-yj="yj-use-beacon">(function(){try{var o=document.getElementById('out');if(!o)return;var p=location.pathname,k='yju:'+p,sent=false;function fire(){if(sent||sessionStorage.getItem(k))return;if(!(o.textContent||'').replace(/\s+/g,'').length)return;sent=true;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",t:'use',src:(function(){try{return (new URLSearchParams(location.search).get('ref')||'').replace(/[^a-z_]/g,'').slice(0,12)}catch(e){return ''}})()})}).catch(function(){});}var mo=new MutationObserver(function(){fire();if(sent)mo.disconnect();});mo.observe(o,{childList:true,characterData:true,subtree:true});}catch(e){}})();</script>
<section data-yj="yj-facts-v1" style="max-width:920px;margin:48px auto 0;padding:0 20px;font-family:system-ui,sans-serif"><style>.yj-article p{font:400 15.5px/1.7 system-ui,sans-serif;color:#26313b;margin:0 0 12px;max-width:72ch}.yj-article h2,.yj-article h3,.yj-article h4{font:600 17px/1.35 system-ui,sans-serif;color:#1b1f24;margin:22px 0 8px}.yj-article table{border-collapse:collapse;margin:10px 0 16px;font:14px/1.45 system-ui,sans-serif}.yj-article th,.yj-article td{border:1px solid #e2e6ea;padding:6px 10px;text-align:left}.yj-article th{background:#f4f6f8}.yj-article pre{background:#0f1720;color:#e6edf3;padding:12px 14px;border-radius:8px;overflow:auto;font:13px/1.5 ui-monospace,Menlo,monospace}.yj-article code{font:13px ui-monospace,Menlo,monospace;background:#f1f3f5;padding:1px 4px;border-radius:3px}.yj-article pre code{background:none;padding:0}.yj-article ul,.yj-article ol{font:15px/1.6 system-ui,sans-serif;color:#26313b}</style><p style="font:400 13px/1.5 system-ui,sans-serif;color:#6b7680;margin:0 0 14px"><time datetime="2026-09-08">Updated 2026-09-08</time> · ReadyStack</p><script type="application/ld+json">{"@context":"https://schema.org","@type":"WebPage","dateModified":"2026-09-08","author":{"@type":"Organization","name":"ReadyStack"}}</script><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:0 0 6px;color:#1b1f24">Worked example</h2><p style="font:400 14.5px/1.5 system-ui,sans-serif;margin:0 0 16px;color:#5b6570">Real numbers from this tool, line by line.</p><div class="yj-article"><p><img src="https://getreadystack.com/img/promo/sku15952_result_card.jpg" alt="CRA Readiness Audit for EU Cyber Resilience Act" loading="lazy" style="max-width:100%;border-radius:6px"></p>
<p>Eight lines out of eight in this Dockerfile break the EU Cyber Resilience Act, and the vendor shipping it into the EU still believes the deadline is 2027.</p>
<p>It is not. Since 11 September 2026, Article 14 has required manufacturers of products with digital elements to report an actively exploited vulnerability to ENISA and their national CSIRT with an early warning inside 24 hours, a technical notification inside 72 hours, and a final report inside 14 days. That duty covers products already on the market. The December 2027 date everyone quotes is real, but it is the date for the *remaining* obligations - conformity assessment, CE marking, technical documentation. The reporting clock has already started.</p>
<p>Here is the file, and here is what the check returns:</p>
<pre><code>L1 [error] FROM node:16
End-of-life Node release. No upstream security fixes, so the duty to
supply security updates for the support period cannot be met.
fix: FROM node:22-bookworm-slim
L2 [error] ENV NODE_TLS_REJECT_UNAUTHORIZED=0
TLS verification off process-wide. Annex I Part I(2): secure by default.
fix: NODE_EXTRA_CA_CERTS=/path/to/ca.pem
L3 [error] ENV DEFAULT_PASSWORD=changeme
Annex I Part I(2)(b) forbids a default password outright - one of the
few CRA requirements written as an absolute, not a risk judgement.
L4 [warn] RUN apt-get install -y curl
Unpinned package. The SBOM in your technical documentation drifts from
what customers actually run. fix: curl=7.88.1-10+deb12u5
L5 [error] RUN curl -k https://updates.example.com/agent.sh | sh
Unauthenticated code fetch in an update path. Annex I Part I(2)(c).
L6 [error] ENV AUTO_UPDATE=false
Automatic security updates off in the shipped default state.
L7 [warn] ENV DEBUG=true
Debug endpoints leak stack traces and configuration.
L8 [warn] CMD ["node","--host","0.0.0.0","server.js"]
Binds every interface. Annex I Part I(2)(d): minimise attack surface.</code></pre>
<p>None of these are exotic. Every one of them is a line somebody wrote on purpose, for a good local reason, and then shipped.</p>
<p>The interesting part is what the free scanners say about this file: nothing. Syft will produce a perfectly good bill of materials for it. Trivy will list the CVEs in the node:16 layer. Neither tool has an opinion about <code>DEFAULT_PASSWORD</code>, because a default password is not a CVE - it is a design requirement in Annex I Part I(2)(b). Neither knows that Article 13(8) sets a five-year floor on your support period, or that a <code>security.txt</code> whose <code>Expires:</code> date has passed is invalid under RFC 9116 and therefore publishes no disclosure channel at all - which is the channel Annex I Part II(5) requires you to have.</p>
<p>That last one is worth dwelling on. Run the check against a typical <code>security.txt</code>:</p>
<pre><code>L1 [error] Contact:
Empty Contact field. RFC 9116 requires at least one.
L2 [error] Contact: mailto:security@example.com
Placeholder contact. A researcher who cannot reach you discloses
publicly instead - and then the 24-hour clock starts from a tweet.
L3 [error] Expires: 2025-06-30T00:00:00.000Z
Already passed. The whole file is invalid.
L4 [error] Policy: https://your-domain.tld/security-policy
Still the template domain.</code></pre>
<p>Four errors, and the repository looked compliant because the file existed.</p>
<p>And the rule that catches the belief itself, run against a planning document:</p>
<pre><code>L2 [warn] The CRA applies from 11 December 2027, so there is nothing to do yet.
Right for most obligations, wrong for Article 14 - which has applied
since 11 September 2026, and covers products already on the market.</code></pre>
<p>Ask a chatbot when the CRA applies and it will tell you December 2027. It is repeating the most-published sentence, and the most-published sentence is about a different set of obligations than the one with the 24-hour clock.</p>
<p>CRA Readiness Audit is a VS Code extension with 28 rules of this kind. Open a file, run the check, and every line that breaks a requirement comes back with the Annex or Article it fails, the severity, and the corrected value. The file never leaves your machine. That much is free and it is not a demo - nothing is hidden, blurred or counted down, and the open file is finished when the panel is empty.</p>
<p>What is paid is scale: one pass over the whole repository instead of the file that happens to be open, an exported findings report you can keep with the Annex VII technical documentation, and JSON a CI step can fail on so the next commit does not put the default password back. $29 once, per person or team seat, seven-day full refund. For comparison, blended EU engineering-and-consulting effort on CRA work is estimated at around EUR 45 an hour, and the cheapest tooled route published runs EUR 4,000 per project per year.</p>
<p>It does not replace a conformity assessment, and it will not write your technical file. It reads the repository, which is where the evidence for most of Annex I actually lives, and tells you which lines an auditor - or a researcher with your broken security.txt in front of them - is going to find first.</p></div><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:34px 0 6px;color:#1b1f24">Questions people ask</h2><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What does CRA Readiness Audit actually do?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">It reads the file open in your editor and flags every line that breaks a Cyber Resilience Act requirement: a hard-coded default password, TLS verification switched off, a base image pinned to latest, an expired security.txt. Each finding names the Annex or Article it fails and gives the corrected value.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Who is this for?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Maintainers and product vendors who ship software into the EU. If you place a product with digital elements on the EU market you are a manufacturer under the CRA, and the Article 14 duty to report actively exploited vulnerabilities has applied since 11 September 2026. Unmonetised open-source stewards are outside it.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Why not just use a free scanner?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Syft and Trivy answer a different question: what is in the image and which CVEs it carries. Neither knows that Annex I Part I(2)(b) forbids a default password outright, that Article 13(8) sets a five-year support floor, or that a security.txt with a passed Expires date publishes no disclosure channel at all.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What is free and what is paid?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Free checks the file open in your editor, or just the lines you select, against all 28 rules with severity, line number and fix. Nothing is hidden or counted down. Paid adds scale: one pass over the whole repository, an exported findings report, and JSON a CI step can fail on.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What would this cost otherwise?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Blended EU engineering-and-consulting effort on CRA work is estimated at around EUR 45 an hour, and the cheapest tooled route published runs EUR 4,000 per project per year. This is $29 once, per person or team seat,. It does not replace a conformity assessment.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Why not just ask ChatGPT or another AI chat?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">A general AI chat answers from training data with a cutoff date, cannot read your repository and names no rule version. CRA Readiness Audit checks the file you open against 28 rules from a rule set dated 2026-09-20, and points at the exact line with the fix. For a filing, an audit or a client you need that dated result on your own files.</p></details><script type="application/ld+json" data-yj="yj-faq-ld">{"@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [{"@type": "Question", "name": "What does CRA Readiness Audit actually do?", "acceptedAnswer": {"@type": "Answer", "text": "It reads the file open in your editor and flags every line that breaks a Cyber Resilience Act requirement: a hard-coded default password, TLS verification switched off, a base image pinned to latest, an expired security.txt. Each finding names the Annex or Article it fails and gives the corrected value."}}, {"@type": "Question", "name": "Who is this for?", "acceptedAnswer": {"@type": "Answer", "text": "Maintainers and product vendors who ship software into the EU. If you place a product with digital elements on the EU market you are a manufacturer under the CRA, and the Article 14 duty to report actively exploited vulnerabilities has applied since 11 September 2026. Unmonetised open-source stewards are outside it."}}, {"@type": "Question", "name": "Why not just use a free scanner?", "acceptedAnswer": {"@type": "Answer", "text": "Syft and Trivy answer a different question: what is in the image and which CVEs it carries. Neither knows that Annex I Part I(2)(b) forbids a default password outright, that Article 13(8) sets a five-year support floor, or that a security.txt with a passed Expires date publishes no disclosure channel at all."}}, {"@type": "Question", "name": "What is free and what is paid?", "acceptedAnswer": {"@type": "Answer", "text": "Free checks the file open in your editor, or just the lines you select, against all 28 rules with severity, line number and fix. Nothing is hidden or counted down. Paid adds scale: one pass over the whole repository, an exported findings report, and JSON a CI step can fail on."}}, {"@type": "Question", "name": "What would this cost otherwise?", "acceptedAnswer": {"@type": "Answer", "text": "Blended EU engineering-and-consulting effort on CRA work is estimated at around EUR 45 an hour, and the cheapest tooled route published runs EUR 4,000 per project per year. This is $29 once, per person or team seat,. It does not replace a conformity assessment."}}, {"@type": "Question", "name": "Why not just ask ChatGPT or another AI chat?", "acceptedAnswer": {"@type": "Answer", "text": "A general AI chat answers from training data with a cutoff date, cannot read your repository and names no rule version. CRA Readiness Audit checks the file you open against 28 rules from a rule set dated 2026-09-20, and points at the exact line with the fix. For a filing, an audit or a client you need that dated result on your own files."}}]}</script><div id="yj-ask" style="margin:36px 0 0;padding:18px 18px 16px;border:1px solid #e2e6ea;border-radius:10px;background:#fbfbfc"><h2 style="font:600 19px/1.3 system-ui,sans-serif;margin:0 0 4px;color:#1b1f24">Ask about this tool</h2><p style="font:400 13.5px/1.5 system-ui,sans-serif;margin:0 0 12px;color:#5b6570">One question, answered by the person who built it. Your email only if you want the answer sent.</p><form onsubmit="return yjAsk(event)" style="display:grid;gap:8px"><textarea name="question" required minlength="10" maxlength="2000" rows="3" placeholder="Your question" style="font:14px/1.5 system-ui,sans-serif;padding:10px;border:1px solid #cfd6dd;border-radius:6px"></textarea><div style="display:flex;gap:8px;flex-wrap:wrap"><input type="email" name="email" placeholder="Email (optional)" style="flex:1;min-width:200px;font:14px system-ui,sans-serif;padding:9px 10px;border:1px solid #cfd6dd;border-radius:6px"><button type="submit" style="font:600 14px system-ui,sans-serif;padding:9px 18px;border:0;border-radius:6px;background:#1b1f24;color:#fff;cursor:pointer">Send</button></div><p id="yj-ask-msg" style="font:13px system-ui,sans-serif;margin:2px 0 0;color:#0a6238;min-height:1em"></p></form><script>function yjAsk(e){e.preventDefault();var f=e.target,m=document.getElementById("yj-ask-msg");m.style.color="#0a6238";fetch(location.origin+"/api/ask",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sku:15952,slug:"cra-readiness-audit",lang:"en",question:f.question.value,email:f.email.value,page:location.pathname})}).then(function(r){if(!r.ok)throw 0;m.textContent="Thanks \u2014 noted. Answers land on this page and, if you left an email, in your inbox.";f.question.value="";}).catch(function(){m.style.color="#a8551f";m.textContent="Could not send. Please try again in a minute.";});return false;}</script></div></section><section class="yj-related" style="max-width:920px;margin:56px auto 40px;padding:0 20px"><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:0 0 6px;color:#1b1f24">Same job, next tool</h2><p style="font:400 14.5px/1.5 system-ui,sans-serif;margin:0 0 20px;color:#5b6570">Free in the browser. The full version is a one-time licence.</p><div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:14px"><a class="yj-rel-c" href="https://getreadystack.com/tools/cra-24-72-14-reporting-lint"><span class="yj-rel-t">CRA 24/72/14 Reporting Lint (Article 14)</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/security-txt-cra-lint"><span class="yj-rel-t">security.txt Lint - RFC 9116 + CRA contact point</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/cra-annex-ii-docs-lint"><span class="yj-rel-t">CRA Annex II User Docs Lint</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/csaf-advisory-check-cra-2026"><span class="yj-rel-t">CSAF Advisory Check for CRA 2026</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/en301549-lint"><span class="yj-rel-t">EN 301 549 Lint — European Accessibility Act rules, with the clause number, in</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/ai-act-article-50-audit"><span class="yj-rel-t">EU AI Act Article 50 Transparency Audit for Your Codebase</span><span class="yj-rel-p">$29</span></a></div><style>.yj-rel-c{display:flex;flex-direction:column;gap:8px;padding:15px 16px;border:1px solid #e2e6ea;border-radius:6px;text-decoration:none;background:#fff}.yj-rel-c:hover{border-color:#b9c2cb;background:#fafbfc}.yj-rel-t{font:500 14.5px/1.42 system-ui,sans-serif;color:#1b1f24}.yj-rel-p{font:600 13px system-ui,sans-serif;color:#0a6238}</style></section> |