Download index.html from getreadystack/ssdf-attestation-workflow-audit: direct link, hf CLI and curl.
- Browser
- Download file 42 kB
-
https://huggingface.co/spaces/getreadystack/ssdf-attestation-workflow-audit/resolve/main/index.html
- Command line
-
hf download hf://spaces/getreadystack/ssdf-attestation-workflow-audit/index.html
-
curl -L -o index.html https://huggingface.co/spaces/getreadystack/ssdf-attestation-workflow-audit/resolve/main/index.html
42 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8"> | |
| <meta name="viewport" content="width=device-width,initial-scale=1"> | |
| <title>CISA SSDF Attestation requirements — check your workflow against SSDF</title> | |
| <meta name="description" content="This browser tool audits a GitHub Actions workflow against CISA secure software development attestation sections and NIST SSDF practices."> | |
| <style> | |
| :root{color-scheme:light}body{margin:0;font:15px/1.55 system-ui,-apple-system,"Segoe UI",sans-serif;background:#faf9f5;color:#1a1512} | |
| .wrap{max-width:960px;margin:0 auto;padding:28px 18px 60px}h1{font-size:26px;margin:0 0 6px}.lede{color:#4a403a;margin:0 0 16px} | |
| textarea{width:100%;min-height:260px;font:13px/1.45 ui-monospace,Menlo,Consolas,monospace;padding:12px;border:1px solid #d9d0c4;border-radius:8px;box-sizing:border-box} | |
| .bar{display:flex;gap:10px;align-items:center;margin:12px 0}button{font:600 15px system-ui;padding:10px 18px;border:0;border-radius:8px;background:#1a1512;color:#fff;cursor:pointer} | |
| input[type=date]{font:14px system-ui;padding:8px;border:1px solid #d9d0c4;border-radius:6px} | |
| table{border-collapse:collapse;width:100%;font-size:14px;margin-top:10px}th,td{text-align:left;padding:8px 10px;border-bottom:1px solid #eee;vertical-align:top}th{font-size:12px;letter-spacing:.06em;text-transform:uppercase;color:#7a6e65} | |
| .sev.error{color:#a8551f;font-weight:700}.sev.warn,.sev.warning{color:#96691f}.ok{color:#2a6446;font-weight:600}.sum{font-weight:600} | |
| .note{font-size:13px;color:#7a6e65;margin-top:18px}#paid{margin-top:22px;padding:14px 16px;border:1px solid #e2d8cb;border-radius:10px;background:#fffdfa} | |
| </style> | |
| <script data-grow-initializer="">!(function(){window.growMe||((window.growMe=function(e){window.growMe._.push(e);}),(window.growMe._=[]));var e=document.createElement("script");(e.type="text/javascript"),(e.src="https://faves.grow.me/main.js"),(e.defer=!0),e.setAttribute("data-grow-faves-site-id","U2l0ZTphZjljMzgyMC0yMmU5LTRhYjgtYTIxNS03OTRhMGY1N2ExYzM=");var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t);})();</script> | |
| <!--yj-canon-s101--><link rel="canonical" href="https://getreadystack.com/tools/ssdf-attestation-workflow-audit"></head> | |
| <body> | |
| <div class="wrap"> | |
| <h1>CISA SSDF Attestation requirements — check your workflow against SSDF</h1> | |
| <p class="lede">Audit a GitHub Actions workflow against the four CISA secure software development attestation sections and the NIST SSDF practices behind them. Runs entirely in your browser — nothing is uploaded.</p> | |
| <textarea id="src" spellcheck="false">name: deploy | |
| on: | |
| pull_request_target: | |
| branches: [main] | |
| permissions: write-all | |
| jobs: | |
| ship: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.event.pull_request.head.sha }} | |
| - name: install | |
| run: npm install | |
| - name: label the build | |
| run: echo "building ${{ github.event.pull_request.title }}" | |
| - name: publish | |
| run: echo "${{ secrets.NPM_TOKEN }}" > ~/.npmrc && npm publish | |
| - uses: actions/attest-build-provenance@c074443f1aee8d4aeeae555aebba3282517141b2 | |
| with: | |
| subject-path: dist/*.tgz</textarea> | |
| <div class="bar"><button id="go">Check</button><label>as of <input type="date" id="today"></label></div> | |
| <div id="out" aria-live="polite"></div> | |
| <div id="paid"><strong>Free here:</strong> Audit the workflow file you have open and list every attestation gap with its SSDF practice ID and the CISA form section it sits under.. <strong>In the editor:</strong> Audit every workflow in the repository at once and export one dated evidence table mapped to all four attestation sections. — that part asks for a licence key.</div> | |
| <p class="note">Same engine as the VS Code extension, byte for byte.</p> | |
| </div> | |
| <script>var SSDF_RULES = [ | |
| {"id": "unpinned_action", "mode": "line", "sev": "high", | |
| "re": "^\\s*-?\\s*uses:\\s*(?!\\./)(?!docker://)[A-Za-z0-9_.-]+/[A-Za-z0-9_./-]+@(?![0-9a-f]{40}([\\s#]|$))", | |
| "msg": "Action pinned to a moving tag, not a 40-character commit SHA - SSDF PW.4.4, attestation section 2 (trusted supply chain)."}, | |
| {"id": "fork_code_with_secrets", "mode": "line", "sev": "high", | |
| "re": "pull_request_target", | |
| "msg": "pull_request_target builds fork code with repository secrets - SSDF PO.5.1, attestation section 1 (secure build environment)."}, | |
| {"id": "untrusted_input_in_shell", "mode": "line", "sev": "high", | |
| "re": "\\$\\{\\{\\s*github\\.event\\.[A-Za-z0-9_.]*\\b(body|title|message|label|name|login|ref|head_ref)\\b", | |
| "msg": "Untrusted github.event text is interpolated into a shell command - SSDF PO.5.1, attestation section 1."}, | |
| {"id": "secret_in_build_log", "mode": "line", "sev": "high", | |
| "re": "(echo|printf|cat|tee)\\b[^\\n]*\\$\\{\\{\\s*secrets\\.", | |
| "msg": "A secret is written to the build log - SSDF PO.5.2, attestation section 1 (protect build data)."}, | |
| {"id": "runner_not_ephemeral", "mode": "line", "sev": "med", | |
| "re": "^\\s*runs-on:\\s*\\[?\\s*['\"]?self-hosted", | |
| "msg": "Self-hosted runner is not declared ephemeral - SSDF PO.5.1 separated build environment, attestation section 1."}, | |
| {"id": "script_piped_to_shell", "mode": "line", "sev": "high", | |
| "re": "(curl|wget)\\b[^|\\n]*\\|\\s*(sudo\\s+)?(ba|z|d)?sh\\b", | |
| "msg": "A downloaded script is piped straight into a shell - SSDF PW.4.1, attestation section 2."}, | |
| {"id": "container_not_digest_pinned", "mode": "line", "sev": "med", | |
| "re": "^\\s*(image|container):\\s*(?![^\\n]*@sha256:)['\"]?[A-Za-z0-9][^\\n]*$", | |
| "msg": "Container image is not pinned by @sha256 digest - SSDF PW.4.1, attestation section 2."}, | |
| {"id": "install_not_reproducible", "mode": "line", "sev": "med", | |
| "re": "\\b(npm install|yarn add|go get|pip install)\\b(?![^\\n]*(--require-hashes|-r requirements))", | |
| "msg": "Dependency install is not reproducible - no lockfile or hash pinning, SSDF PW.4.1, attestation section 2."}, | |
| {"id": "security_check_muted", "mode": "line", "sev": "high", | |
| "re": "(continue-on-error:\\s*true|\\|\\|\\s*true\\s*$)", | |
| "msg": "A security check cannot fail the build - SSDF RV.1.2, attestation section 4 (act on findings)."}, | |
| {"id": "token_write_all", "mode": "line", "sev": "high", | |
| "re": "^\\s*permissions:\\s*write-all", | |
| "msg": "permissions: write-all gives the job token more than it needs - SSDF PO.5.1 least privilege, attestation section 1."}, | |
| {"id": "no_build_provenance", "mode": "absent", "sev": "high", | |
| "re": "(attest-build-provenance|slsa-github-generator|cosign|sigstore|in-toto)", | |
| "msg": "No signed build provenance step - SSDF PS.3.1, attestation section 3 (provenance data)."}, | |
| {"id": "no_sbom", "mode": "absent", "sev": "high", "flags": "i", | |
| "re": "(sbom|cyclonedx|spdx|syft)", | |
| "msg": "No SBOM is produced - SSDF PS.3.2, attestation section 3 (provenance data)."}, | |
| {"id": "no_vulnerability_check", "mode": "absent", "sev": "high", "flags": "i", | |
| "re": "(codeql|dependency-review|trivy|grype|osv-scanner|semgrep|pip-audit|npm audit|snyk|dependabot)", | |
| "msg": "No automated vulnerability check runs - SSDF RV.1.1, attestation section 4."}, | |
| {"id": "no_permissions_block", "mode": "absent", "sev": "med", "flags": "m", | |
| "re": "^\\s*permissions:", | |
| "msg": "No explicit permissions block - SSDF PO.5.1 least privilege, attestation section 1."} | |
| ] | |
| ;</script> | |
| <script>/* SSDF Attestation Audit - one brain, used by the extension and by the free web page. */ | |
| (function (root) { | |
| 'use strict'; | |
| var RULES = (typeof module !== 'undefined' && module.exports) | |
| ? require('./rules.json') | |
| : (root.SSDF_RULES || []); | |
| // Federal obligations cluster at the end of the US federal fiscal year (30 September). | |
| function fiscalTail(today) { | |
| var m = String(today == null ? '' : today).match(/(\d{4})-(\d{2})-(\d{2})/); | |
| if (!m) return ''; | |
| var y = +m[1], mo = +m[2], d = +m[3]; | |
| if (mo < 1 || mo > 12 || d < 1 || d > 31) return ''; | |
| var now = Date.UTC(y, mo - 1, d); | |
| if (isNaN(now)) return ''; | |
| var end = Date.UTC(y, 8, 30); // 30 September of the same year | |
| if (end < now) end = Date.UTC(y + 1, 8, 30); | |
| var days = Math.round((end - now) / 86400000); | |
| var iso = (new Date(end)).toISOString().slice(0, 10); | |
| return ' Federal obligations cluster at fiscal-year end ' + iso + ' - ' + days + ' days from ' + m[0] + '.'; | |
| } | |
| function check(text, opts) { | |
| var src = String(text == null ? '' : text).replace(/\r\n?/g, '\n'); | |
| var lines = src.split('\n'); | |
| var tail = fiscalTail((opts && opts.today) || ''); | |
| var findings = []; | |
| for (var i = 0; i < RULES.length; i++) { | |
| var rule = RULES[i]; | |
| var flags = rule.flags || ''; | |
| if (rule.mode === 'absent') { | |
| var whole = new RegExp(rule.re, flags.indexOf('m') >= 0 ? flags : flags); | |
| if (!whole.test(src)) { | |
| findings.push({ check: rule.id, sev: rule.sev, msg: rule.msg + tail, line: 1 }); | |
| } | |
| continue; | |
| } | |
| var per = new RegExp(rule.re, flags); | |
| for (var l = 0; l < lines.length; l++) { | |
| var line = lines[l]; | |
| if (/^\s*#/.test(line)) continue; // a comment is not a build step | |
| if (per.test(line)) { | |
| findings.push({ check: rule.id, sev: rule.sev, msg: rule.msg, line: l + 1 }); | |
| } | |
| } | |
| } | |
| findings.sort(function (a, b) { return a.line - b.line || (a.check < b.check ? -1 : 1); }); | |
| return { findings: findings }; | |
| } | |
| var api = { engine: { check: check }, RULES: RULES, RULE_COUNT: RULES.length }; | |
| root.SSDFENGINE = api; | |
| if (typeof module !== 'undefined' && module.exports) module.exports = api; | |
| })(typeof window !== 'undefined' ? window : this); | |
| </script> | |
| <script>// ⛔뼈대 — kit/scaffold.py 가 찍는다 (s143). findings [{check,sev,msg,line?}] → 글/HTML. 같은 파일이 Node 와 브라우저에서 산다. | |
| ; | |
| function escapeHtml(s) { return String(s == null ? '' : s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"'); } | |
| function summaryLine(res) { | |
| const f = (res && res.findings) || []; const e = f.filter(function (x) { return String(x.sev || 'error').toLowerCase().startsWith('err'); }).length; | |
| return f.length ? (f.length + ' finding' + (f.length === 1 ? '' : 's') + ' (' + e + ' error' + (e === 1 ? '' : 's') + ')') : 'Clean — no findings.'; | |
| } | |
| function toText(res, label) { | |
| const f = (res && res.findings) || []; | |
| const lines = ['== ' + (label || '') + ' — ' + summaryLine(res)]; | |
| f.forEach(function (x) { lines.push(' ' + String(x.sev || 'error').toUpperCase().slice(0, 4) + ' ' + (x.line ? 'L' + x.line + ' ' : '') + (x.check || x.id || '') + ' ' + (x.msg || x.message || '')); }); | |
| return lines.join('\n'); | |
| } | |
| function toHtml(res) { | |
| const f = (res && res.findings) || []; | |
| if (!f.length) return '<p class="ok">' + escapeHtml(summaryLine(res)) + '</p>'; | |
| const rows = f.map(function (x) { return '<tr><td class="sev ' + escapeHtml(String(x.sev || 'error')) + '">' + escapeHtml(String(x.sev || 'error')) + '</td><td>' + (x.line ? 'L' + escapeHtml(x.line) : '') + '</td><td><code>' + escapeHtml(x.check || x.id || '') + '</code></td><td>' + escapeHtml(x.msg || x.message || '') + '</td></tr>'; }).join(''); | |
| return '<p class="sum">' + escapeHtml(summaryLine(res)) + '</p><table><thead><tr><th>Level</th><th>Line</th><th>Check</th><th>What</th></tr></thead><tbody>' + rows + '</tbody></table>'; | |
| } | |
| var SSDFREPORT = { escapeHtml: escapeHtml, summaryLine: summaryLine, toText: toText, toHtml: toHtml }; | |
| if (typeof module !== 'undefined' && module.exports) module.exports = SSDFREPORT; | |
| else window.SSDFREPORT = SSDFREPORT; | |
| </script> | |
| <script> | |
| (function(){ | |
| var src=document.getElementById('src'), out=document.getElementById('out'), go=document.getElementById('go'), today=document.getElementById('today'); | |
| today.value=new Date().toISOString().slice(0,10); | |
| function run(){ try { var res=window.SSDFENGINE.engine.check(src.value,{today:today.value}); out.innerHTML=window.SSDFREPORT.toHtml(res); } catch(e){ out.innerHTML='<p class="sev error">Could not run: '+String(e.message||e)+'</p>'; } } | |
| go.onclick=run; if (src.value.trim()) run(); | |
| })(); | |
| </script> | |
| <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://marketplace.visualstudio.com/items?itemName=ReadyStack.ssdf-attestation-workflow-audit" rel="noopener" data-yj-install-door="mp" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'ssdf-attestation-workflow-audit',src:'mp',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">VS Code Marketplace</a><a href="https://open-vsx.org/extension/readystack/ssdf-attestation-workflow-audit" rel="noopener" data-yj-install-door="ovsx" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'ssdf-attestation-workflow-audit',src:'ovsx',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">Open VSX (Cursor, VSCodium)</a><a href="https://www.npmjs.com/package/@readystack/ssdf-attestation-workflow-audit" rel="noopener" data-yj-install-door="npm" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'ssdf-attestation-workflow-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/ssdf-attestation-workflow-audit" rel="noopener" data-yj-install-door="mcp" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'ssdf-attestation-workflow-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/ssdf-attestation-workflow-audit" rel="noopener" data-yj-install-door="docker" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'ssdf-attestation-workflow-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">ext install ReadyStack.ssdf-attestation-workflow-audit | |
| npx @readystack/ssdf-attestation-workflow-audit <file> | |
| npx @readystack/ssdf-attestation-workflow-audit --mcp</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 14 rules SSDF Attestation Audit for GitHub Actions 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:"ssdf-attestation-workflow-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">14 rules, four CISA attestation sections, one workflow file</p><p style="margin:0 0 1rem;color:#5b6b7c;font-size:.94rem">A US application-security contractor bills roughly 150-250 an hour to review and re-pin a release pipeline by hand.</p><a href="https://getreadystack.com/api/buy/cl/polar_cl_p4a0TbqABh9QIIJlOPNxkwxm8shyqSsnSn23v05tEw7" 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><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><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-21">Updated 2026-09-21</time> · ReadyStack</p><script type="application/ld+json">{"@context":"https://schema.org","@type":"WebPage","dateModified":"2026-09-21","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/sku158108_result_card.jpg" alt="SSDF Attestation Audit for GitHub Actions" loading="lazy" style="max-width:100%;border-radius:6px"></p> | |
| <p>8 of 14 attestation rules fail on one AI-written GitHub Actions deploy workflow - the same pipeline a US federal software vendor stands behind when a chief executive signs the CISA Secure Software Development Attestation Common Form. The hardened release workflow sitting next to it, same audit, same day: 0 findings.</p> | |
| <p>Under OMB memoranda M-22-18 and M-23-16, a federal agency may use your software only once that form has been signed by a chief executive or a designated employee. The form is short. It carries four attestation sections: the software is developed and built in secure environments; trusted source code supply chains are maintained in good faith; provenance data is kept for internal and third-party code; and automated tooling checks for vulnerabilities on an ongoing basis. Three of those four are answered by your build pipeline, not by a policy document in a shared drive.</p> | |
| <p>That is where the gap opens: a policy document is written knowing an auditor will read it, while a workflow file is written by whoever needed the release out. Here is what the sample AI-written deploy workflow in this extension's fixtures actually contains.</p> | |
| <p>It triggers on pull_request_target, so fork code runs next to repository secrets - SSDF PO.5.1, section 1. It sets permissions: write-all, handing the job token everything - PO.5.1 again. It pins the code-fetch action to a moving tag instead of a 40-character commit SHA - PW.4.4, section 2. It runs npm install rather than a lockfile install, so the build is not reproducible - PW.4.1. It interpolates a pull request title straight into a shell command - PO.5.1. It echoes a publish token into the build log - PO.5.2. And two sections stay empty by omission: no SBOM anywhere, and no automated vulnerability check anywhere - PS.3.2 and RV.1.1, sections 3 and 4. Eight findings, three sections.</p> | |
| <p>Ask a general assistant to harden that file and you get back confident YAML. What you do not get is the sentence that matters: section 3 is still empty. It pins one action and moves on, leaving no line number tied to PS.3.1 that someone else can follow six months later. A false attestation is not a lint warning - it is a statement made to the United States government, and the Department of Justice has pursued cybersecurity misrepresentations in federal contracts under the False Claims Act.</p> | |
| <p>The audit takes two inputs: a workflow file and a date. The date is not decoration. Findings that come from an absent control - no SBOM, no provenance step, no scan - also report how many days remain to the 30 September federal fiscal-year end, the boundary where new awards and renewals land. Run the same file on 2026-09-21 and you get 9 days; run it in November and the clock points at the following year.</p> | |
| <p>The free audit reads the workflow file you have open and lists every attestation gap with its practice ID and form section. All 14 rules, all four sections, offline. That job finishes on its own: open a file, learn what your signature would rest on, fix the lines.</p> | |
| <p>The full version changes one axis only - scope. It audits every workflow in the repository at once and exports a single dated evidence table mapped to all four attestation sections, which is the artefact the person signing the form asks for. 29 once, one licence key per person or team seat. A US application-security contractor bills roughly 150 to 250 an hour for the same review by hand.</p> | |
| <p>Attestation is a signature on top of a build system. This makes the build system readable.</p></div><h2 style="font:600 19px/1.3 system-ui,sans-serif;margin:30px 0 10px;color:#1b1f24">15 seconds — what it actually does</h2><video controls muted playsinline preload="none" poster="/img/promo/sku158108_result_card.jpg" style="width:100%;max-width:360px;border-radius:10px;display:block;margin:0 auto"><source src="/media/sku158108_short.mp4" type="video/mp4"></video><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 the SSDF Attestation Audit actually do?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">It reads a GitHub Actions workflow file and marks every line that contradicts the CISA Secure Software Development Attestation Common Form, naming the NIST SSDF (SP 800-218) practice ID and the form section behind each mark. It ships 14 rules across the four attestation sections. On the bundled AI-written deploy workflow it returns 8 findings; on the hardened release workflow it returns 0.</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">Software vendors that sell to United States federal agencies, and the platform or release engineers who own their GitHub Actions pipelines. Under OMB M-22-18 and M-23-16 an agency may use your software only after a chief executive or a designated employee signs the attestation form. Those engineers are the people who have to produce the evidence under that signature.</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 can't I just ask a chatbot to harden my workflow?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">A general assistant rewrites YAML confidently but does not tell you which attestation section is still empty afterwards. It will pin one action and leave provenance and SBOM missing, and it gives you no line number tied to PS.3.1 that an auditor can follow. This audit is a fixed, readable rule table, not a generated opinion that changes between prompts.</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 does the full version add?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Free: audit the workflow file you have open and list every attestation gap with its practice ID and form section, all 14 rules, all four sections. That job finishes on its own. The full version changes the scope: it audits every workflow in the repository at once and exports one dated evidence table mapped to all four attestation sections.</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 done by hand?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">A United States application-security contractor bills roughly 150 to 250 an hour to review a release pipeline, re-pin actions to commit SHAs and map each control back to an SSDF practice. The full version is 29 once, one licence key per person or team seat,. The free audit costs nothing.</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. SSDF Attestation Audit for GitHub Actions checks the file you open against 14 rules from a rule set dated 2026-09-21, and points at the exact line. 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 the SSDF Attestation Audit actually do?", "acceptedAnswer": {"@type": "Answer", "text": "It reads a GitHub Actions workflow file and marks every line that contradicts the CISA Secure Software Development Attestation Common Form, naming the NIST SSDF (SP 800-218) practice ID and the form section behind each mark. It ships 14 rules across the four attestation sections. On the bundled AI-written deploy workflow it returns 8 findings; on the hardened release workflow it returns 0."}}, {"@type": "Question", "name": "Who is this for?", "acceptedAnswer": {"@type": "Answer", "text": "Software vendors that sell to United States federal agencies, and the platform or release engineers who own their GitHub Actions pipelines. Under OMB M-22-18 and M-23-16 an agency may use your software only after a chief executive or a designated employee signs the attestation form. Those engineers are the people who have to produce the evidence under that signature."}}, {"@type": "Question", "name": "Why can't I just ask a chatbot to harden my workflow?", "acceptedAnswer": {"@type": "Answer", "text": "A general assistant rewrites YAML confidently but does not tell you which attestation section is still empty afterwards. It will pin one action and leave provenance and SBOM missing, and it gives you no line number tied to PS.3.1 that an auditor can follow. This audit is a fixed, readable rule table, not a generated opinion that changes between prompts."}}, {"@type": "Question", "name": "What is free and what does the full version add?", "acceptedAnswer": {"@type": "Answer", "text": "Free: audit the workflow file you have open and list every attestation gap with its practice ID and form section, all 14 rules, all four sections. That job finishes on its own. The full version changes the scope: it audits every workflow in the repository at once and exports one dated evidence table mapped to all four attestation sections."}}, {"@type": "Question", "name": "What would this cost done by hand?", "acceptedAnswer": {"@type": "Answer", "text": "A United States application-security contractor bills roughly 150 to 250 an hour to review a release pipeline, re-pin actions to commit SHAs and map each control back to an SSDF practice. The full version is 29 once, one licence key per person or team seat,. The free audit costs nothing."}}, {"@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. SSDF Attestation Audit for GitHub Actions checks the file you open against 14 rules from a rule set dated 2026-09-21, and points at the exact line. 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:158108,slug:"ssdf-attestation-workflow-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/cron-schedule-lint"><span class="yj-rel-t">Cron Schedule Lint for crontab, Kubernetes CronJob, GitHub Actions, Spring and</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/actions-deprecation-lint-2026"><span class="yj-rel-t">GitHub Actions Deprecation Lint: the 2026 runner and action EOL dates</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/engineer-resume-template"><span class="yj-rel-t">Engineer Resume Template — 6 US Resumes: Mid-Career, New Grad with EIT, PE-Tra</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/n8n-social-media-workflow-agency-content-approval"><span class="yj-rel-t">n8n Social Media Workflow for Agency Content Approval — 6 Client Sign-Off Auto</span><span class="yj-rel-p">$59</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/robots-txt-ai-crawler-audit"><span class="yj-rel-t">AI Crawler Rules - robots.txt Audit for AI Search</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/bulk-sender-lint"><span class="yj-rel-t">Bulk Sender Lint - the Gmail, Yahoo and Outlook.com rules, checked in your mai</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></body> | |
| </html> | |