Cold read of https://bounded.tools, 2026-09-03, against the live deploy (commit 3a4ac1d).
Two defects that hide each other
scripts/emit-artifacts.mjs:92 explains the report-only choice:
shipped REPORT-ONLY first: a wrong directive only REPORTS to the console, never blocks, so it can't break the page; flip to enforcing once verified clean in a browser.
That plan needs reports. The emitted policy has ten directives and none of them is report-uri or report-to:
default-src 'self'; script-src 'self' 'sha256-+z3guWA9…'; style-src 'self' 'unsafe-inline';
img-src 'self'; font-src 'self'; connect-src 'self' https://api.github.com https://rekor.sigstore.dev;
base-uri 'self'; form-action 'self'; frame-ancestors 'none'; object-src 'none'
A report-only policy with nowhere to report is inert. It does not block, and it tells no one. The header has been shipping in the one mode whose entire purpose is telemetry, with the telemetry off.
And it was hiding a real violation.
Measured
I served the live pages with that exact policy as an enforcing Content-Security-Policy and loaded each in Chromium:
| Page |
Result |
/, /map, /contracts, /conformance, /ledger, /blog/, /blog/provenance-is-not-legitimacy, /404 |
clean |
/rekor |
Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' 'sha256-+z3guWA9…'" |
Root cause
script-src carries exactly one hash, and by construction it can never carry /rekor's.
emit-artifacts.mjs walks dist/ and hashes every inline script it finds. deploy.yml runs the stamped pipeline (line 87), which includes emit-artifacts.mjs, and only then runs gen-provenance.mjs (line 93). That script writes dist/rekor/index.html with an inline redirect (vendor/conformance-kit/integrity/gen-provenance.mjs:125):
<script>location.replace(${JSON.stringify(rekorUrl)})</script>
The ordering is deliberate and correct on its own terms: the page cannot exist before signing, because it carries the log index the signature produces. But it means the CSP hash set is computed over a dist/ that does not yet contain the last page written into it.
User-visible impact today
Small, and I want to be accurate about it. /rekor also carries <meta http-equiv="refresh">, which script-src does not govern, so the redirect still happens under enforcement. The script only makes it faster. Nothing is broken for a reader right now.
What is broken is the mechanism. The page that would have failed is the one-click path to the transparency log, which "Trust lives outside the page" points at as the thing you do not have to trust the page for. Nobody learned this in the weeks the header has been shipping, because the mode that would have told them had no endpoint.
Suggested shape
- Hash
/rekor at the point it is written. Either have gen-provenance.mjs recompute the _headers CSP after it writes the sidecar, or drop the inline script and rely on the meta refresh alone, which needs no hash and no exception.
- Add a build check that every inline
<script> in the final dist/ has a matching hash in the emitted CSP, so an ordering change cannot silently reintroduce this.
- Then flip to enforcing. On the evidence above, every other page is already clean under the exact policy.
- If report-only is kept for a transition window, give it a
report-to endpoint. Otherwise the mode is decorative.
Filed from a cold read, unclaimed and unassigned.
Cold read of https://bounded.tools, 2026-09-03, against the live deploy (commit
3a4ac1d).Two defects that hide each other
scripts/emit-artifacts.mjs:92explains the report-only choice:That plan needs reports. The emitted policy has ten directives and none of them is
report-uriorreport-to:A report-only policy with nowhere to report is inert. It does not block, and it tells no one. The header has been shipping in the one mode whose entire purpose is telemetry, with the telemetry off.
And it was hiding a real violation.
Measured
I served the live pages with that exact policy as an enforcing
Content-Security-Policyand loaded each in Chromium:/,/map,/contracts,/conformance,/ledger,/blog/,/blog/provenance-is-not-legitimacy,/404/rekorRefused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' 'sha256-+z3guWA9…'"Root cause
script-srccarries exactly one hash, and by construction it can never carry/rekor's.emit-artifacts.mjswalksdist/and hashes every inline script it finds.deploy.ymlruns thestampedpipeline (line 87), which includesemit-artifacts.mjs, and only then runsgen-provenance.mjs(line 93). That script writesdist/rekor/index.htmlwith an inline redirect (vendor/conformance-kit/integrity/gen-provenance.mjs:125):The ordering is deliberate and correct on its own terms: the page cannot exist before signing, because it carries the log index the signature produces. But it means the CSP hash set is computed over a
dist/that does not yet contain the last page written into it.User-visible impact today
Small, and I want to be accurate about it.
/rekoralso carries<meta http-equiv="refresh">, whichscript-srcdoes not govern, so the redirect still happens under enforcement. The script only makes it faster. Nothing is broken for a reader right now.What is broken is the mechanism. The page that would have failed is the one-click path to the transparency log, which "Trust lives outside the page" points at as the thing you do not have to trust the page for. Nobody learned this in the weeks the header has been shipping, because the mode that would have told them had no endpoint.
Suggested shape
/rekorat the point it is written. Either havegen-provenance.mjsrecompute the_headersCSP after it writes the sidecar, or drop the inline script and rely on the meta refresh alone, which needs no hash and no exception.<script>in the finaldist/has a matching hash in the emitted CSP, so an ordering change cannot silently reintroduce this.report-toendpoint. Otherwise the mode is decorative.Filed from a cold read, unclaimed and unassigned.