From a2243f0c569d7eb6657988e82e1c4b7dbc94ab27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 1 Sep 2026 16:32:11 +0200 Subject: [PATCH] :whale: Cover the inline scripts of the served pages with CSP hashes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The frontend build now emits the sha256 hashes of the inline scripts of every page it writes into resources/public, the image moves them out of the document root, and the entrypoint splices them into the default script-src. This removes one of the two reasons why enforcing mode was not usable. The hashes are computed on the rendered output rather than on the mustache templates, since the digest covers the exact bytes served between the script tags. All four served pages contribute, not just index.html: challenge.html handles the redirect and render.html and rasterizer.html are loaded by the exporter, so leaving them out would have broken export under enforcing mode. The storybook previews are excluded because that container does not serve them. A bundle predating this change yields no hashes and the policy stays as it was, so older bundles keep building. Signed-off-by: David Barragán Merino --- docker/images/Dockerfile.frontend | 9 +++++++ docker/images/files/nginx-entrypoint.sh | 23 +++++++++++----- docs/technical-guide/configuration.md | 24 ++++++++++------- frontend/scripts/_helpers.js | 35 +++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 17 deletions(-) diff --git a/docker/images/Dockerfile.frontend b/docker/images/Dockerfile.frontend index 6565e519c1..753039b615 100644 --- a/docker/images/Dockerfile.frontend +++ b/docker/images/Dockerfile.frontend @@ -32,6 +32,15 @@ COPY ./files/nginx-mime.types /etc/nginx/mime.types COPY ./files/nginx-external-locations.conf /etc/nginx/overrides/location.d/external-locations.conf COPY ./files/nginx-entrypoint.sh /entrypoint.sh +# The CSP hashes of the inline scripts of index.html are emitted by the +# frontend build. Move them out of the document root: nginx must read them, +# the browser has no reason to. +RUN if [ -f /var/www/app/csp-script-hashes.txt ]; then \ + mv /var/www/app/csp-script-hashes.txt /etc/nginx/csp-script-hashes.txt; \ + else \ + echo "WARNING: the frontend bundle does not provide csp-script-hashes.txt" >&2; \ + fi + RUN chown -R 1001:0 /var/cache/nginx; \ chmod -R g+w /var/cache/nginx; \ chown -R 1001:0 /etc/nginx; \ diff --git a/docker/images/files/nginx-entrypoint.sh b/docker/images/files/nginx-entrypoint.sh index 06ccd65f3e..8950dc7e26 100644 --- a/docker/images/files/nginx-entrypoint.sh +++ b/docker/images/files/nginx-entrypoint.sh @@ -95,13 +95,22 @@ envsubst "\$PENPOT_INTERNAL_RESOLVER" \ # because the Google Fonts and GitHub templates endpoints are reverse # proxied by this very server. # -# It ships in report-only mode: the inline