fix(build): compute CSP hash on LF-normalized index.html
When the build runs on a Windows checkout, fs.readFileSync returns the file with CRLF intact, and the hash of the inline bootstrap script's body reflects those CRLFs. The release tarball / git transport / Linux file system strip CRLF on the publishing host, so the browser sees the LF-only version and computes a different sha256. CSP then blocks the script — disabling the version widget, theme switcher, and any other DOM bindings set up in that inline block. Normalize CRLF -> LF before computing the hash (the on-disk file keeps its native line endings; only the hash input is normalized). The CSP allowlist now matches whatever Caddy actually serves.
This commit is contained in:
+9
-1
@@ -78,7 +78,13 @@ const bundles = {
|
|||||||
|
|
||||||
function updateInlineScriptCspHash() {
|
function updateInlineScriptCspHash() {
|
||||||
const html = fs.readFileSync(INDEX_HTML, 'utf8');
|
const html = fs.readFileSync(INDEX_HTML, 'utf8');
|
||||||
const scripts = [...html.matchAll(/<script>([\s\S]*?)<\/script>/g)];
|
// The hash MUST match what the browser computes from the served bytes.
|
||||||
|
// git's text normalization + tar transport strip CRLF on the Linux side,
|
||||||
|
// so the deployed file is always LF-only — even when the dev copy is CRLF
|
||||||
|
// (e.g. cloned on Windows). Normalize before hashing so a Windows-built
|
||||||
|
// index.html produces a CSP allowlist that matches the served LF version.
|
||||||
|
const normalized = html.replace(/\r\n/g, '\n');
|
||||||
|
const scripts = [...normalized.matchAll(/<script>([\s\S]*?)<\/script>/g)];
|
||||||
const target = scripts.find(match => {
|
const target = scripts.find(match => {
|
||||||
const block = match[1] || '';
|
const block = match[1] || '';
|
||||||
return block.includes("license-topbar-version") || block.includes("openVersionInfo") || block.includes("widget-");
|
return block.includes("license-topbar-version") || block.includes("openVersionInfo") || block.includes("widget-");
|
||||||
@@ -90,6 +96,8 @@ function updateInlineScriptCspHash() {
|
|||||||
|
|
||||||
const scriptContent = target[1];
|
const scriptContent = target[1];
|
||||||
const hash = crypto.createHash('sha256').update(scriptContent).digest('base64');
|
const hash = crypto.createHash('sha256').update(scriptContent).digest('base64');
|
||||||
|
// Write the CSP update back into the original (possibly CRLF) file so we
|
||||||
|
// don't churn the working copy's line endings just because we read it.
|
||||||
const updatedHtml = html.replace(
|
const updatedHtml = html.replace(
|
||||||
/script-src 'self' 'sha256-[^']+';/,
|
/script-src 'self' 'sha256-[^']+';/,
|
||||||
`script-src 'self' 'sha256-${hash}';`
|
`script-src 'self' 'sha256-${hash}';`
|
||||||
|
|||||||
+1
-1
@@ -8,7 +8,7 @@
|
|||||||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
|
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
|
||||||
<meta http-equiv="Pragma" content="no-cache" />
|
<meta http-equiv="Pragma" content="no-cache" />
|
||||||
<meta http-equiv="Expires" content="0" />
|
<meta http-equiv="Expires" content="0" />
|
||||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'sha256-kwm9nLWm/jfIuT8y4i62Xq6mDqe4mRlMZn1tDg+5Zek='; style-src 'self' 'unsafe-inline'; img-src 'self' https://cdn.jsdelivr.net data:; connect-src 'self' https://api.open-meteo.com https://geocoding-api.open-meteo.com; font-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'">
|
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'sha256-Nv8xzCSztfdYOL663VgPKWQn6v0lnM0ACWxkxpFfcfY='; style-src 'self' 'unsafe-inline'; img-src 'self' https://cdn.jsdelivr.net data:; connect-src 'self' https://api.open-meteo.com https://geocoding-api.open-meteo.com; font-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'">
|
||||||
|
|
||||||
<link rel="icon" href="/assets/dashcaddy-favicon.ico" sizes="any">
|
<link rel="icon" href="/assets/dashcaddy-favicon.ico" sizes="any">
|
||||||
<link rel="icon" type="image/png" sizes="192x192" href="/assets/icon-192.png">
|
<link rel="icon" type="image/png" sizes="192x192" href="/assets/icon-192.png">
|
||||||
|
|||||||
Reference in New Issue
Block a user