|
7 | 7 | # location, re-include this file there. |
8 | 8 | # |
9 | 9 | # CSP notes (must not break the SPA — see app/index.html and app/src): |
10 | | -# - script-src 'unsafe-inline': index.html ships inline scripts (theme |
11 | | -# resolver, Eruda loader, Plausible stub); no nonce infra for a static file. |
| 10 | +# - script-src 'unsafe-inline': index.html ships three executable inline |
| 11 | +# scripts (theme resolver, Eruda loader, Plausible stub), and a FOURTH one |
| 12 | +# arrives that this repository does not write — see the block below. |
12 | 13 | # - script-src cdn.jsdelivr.net: on-device debug console (Eruda) behind ?debug=1. |
| 14 | +# |
| 15 | +# Why script-src still says 'unsafe-inline' (measured 2026-09-03) |
| 16 | +# --------------------------------------------------------------- |
| 17 | +# Replacing 'unsafe-inline' with the sha256 of each inline script is the |
| 18 | +# obvious hardening — index.html is static, so its scripts are fixed at build |
| 19 | +# time, and `yarn build` was verified to copy them through byte-for-byte. The |
| 20 | +# three hashes are recorded below and pinned by tests/unit/api/test_csp_policy.py |
| 21 | +# so they never go stale. |
| 22 | +# |
| 23 | +# They cannot be ENFORCED yet. Cloudflare JavaScript Detections injects an |
| 24 | +# inline script into every HTML response at the edge, after nginx, and its body |
| 25 | +# carries a per-response ray id and timestamp — so its hash differs on every |
| 26 | +# request and cannot be listed here. The whole policy was mounted over the LIVE |
| 27 | +# production bundle through a local proxy and loaded twice, once with each |
| 28 | +# script-src: |
| 29 | +# |
| 30 | +# 'unsafe-inline' → Cloudflare's script runs (its hidden iframe appears) |
| 31 | +# hashes only → "Executing inline script violates … The action has |
| 32 | +# been blocked", no iframe, no JS-detection signal |
| 33 | +# |
| 34 | +# Exactly one script is blocked, and it is the edge's. Shipping the hash policy |
| 35 | +# would silently degrade bot detection on a site whose origin gate leans on the |
| 36 | +# edge — so it is not shipped, and 'unsafe-inline' is NOT joined by hashes |
| 37 | +# either: a browser ignores 'unsafe-inline' as soon as a hash is present, so the |
| 38 | +# two together are the same breakage wearing a stricter-looking policy. |
| 39 | +# |
| 40 | +# The way out is a NONCE, not a hash. Cloudflare parses this response header |
| 41 | +# and stamps its own injected script with the nonce it finds there (their |
| 42 | +# JavaScript Detections docs say so explicitly, and recommend it over |
| 43 | +# 'unsafe-inline'). That needs nginx to mint one per request and rewrite |
| 44 | +# index.html's `<script>` tags with it — `sub_filter` plus `gzip_static off` |
| 45 | +# for the shell — which is a delivery change no test in this repo can prove and |
| 46 | +# no local nginx here can run. It is the open item; the alternative is turning |
| 47 | +# JavaScript Detections off in the zone, which is a security trade, not a fix. |
| 48 | +# |
| 49 | +# The hashes of app/index.html's three executable inline scripts, ready for the |
| 50 | +# day one of those two happens (JSON-LD blocks need none — a browser never |
| 51 | +# executes `type="application/ld+json"`, so CSP never asks): |
| 52 | +# 'sha256-4VdX7wfQgL9PnVFBkrDWBbPpiST1xriKljA5URM8DcM=' theme resolver |
| 53 | +# 'sha256-HfNBzShy4Q4W9GmmnPkcx36GlrZI5mkU15xjpyh1pmk=' Eruda loader |
| 54 | +# 'sha256-BiWO1y5gYRbSlOrPh1rJPXYnES50FX9PwJpfXfWJy0A=' Plausible stub |
| 55 | +# A hash covers the exact bytes between `<script>` and `</script>`, so a single |
| 56 | +# re-indent invalidates one. The test recomputes them from index.html on every |
| 57 | +# run, which is what keeps this block honest while it waits. |
13 | 58 | # - style-src 'unsafe-inline': MUI/emotion inject inline styles. |
14 | 59 | # - img/font/connect storage.googleapis.com: plot previews + MonoLisa fonts on GCS. |
15 | 60 | # - img/connect/frame api.anyplot.ai: API calls, og images, interactive-preview |
|
0 commit comments