fix(web/security): tighten CSP and harden /go bootstrap error path

- Drop 'unsafe-eval' from script-src CSP — Next.js prod build doesn't
  need it; closes an XSS escalation vector for any future inline payload.
- Cap error messages embedded in the generated OOBE PowerShell script at
  200 chars to prevent a multi-MB upstream message from being inlined
  verbatim into a script that runs as SYSTEM during Windows OOBE.
- Add X-Content-Type-Options: nosniff to the /go error response for
  consistency with the success response.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Oliver Kieselbach
2026-05-08 13:18:09 +02:00
parent 0c24719e16
commit f620b884c6
2 changed files with 14 additions and 2 deletions
@@ -65,15 +65,27 @@ export async function GET(
}
}
// Hard cap on error messages embedded in the generated PS script.
// Defense against an upstream/backend bug or compromise emitting a multi-MB
// `message` that would otherwise be inlined verbatim into the script body.
const MAX_ERROR_MESSAGE_LENGTH = 200;
function errorScript(message: string): Response {
// Cap length BEFORE quote-escaping so the cap is on raw input, not the
// post-escape size (which can double for ' -heavy strings).
const truncated =
message.length > MAX_ERROR_MESSAGE_LENGTH
? message.slice(0, MAX_ERROR_MESSAGE_LENGTH) + "..."
: message;
// Escape single quotes for PowerShell string
const safeMsg = message.replace(/'/g, "''");
const safeMsg = truncated.replace(/'/g, "''");
const script = `Write-Host ''; Write-Host 'ERROR: ${safeMsg}' -ForegroundColor Red; Write-Host ''`;
return new Response(script, {
status: 200, // Return 200 so irm | iex works; the Write-Host will show the error
headers: {
"Content-Type": "text/plain; charset=utf-8",
"Cache-Control": "no-store, no-cache",
"X-Content-Type-Options": "nosniff",
},
});
}
+1 -1
View File
@@ -23,7 +23,7 @@ const nextConfig: NextConfig = {
key: "Content-Security-Policy",
value: [
"default-src 'self'",
"script-src 'self' 'unsafe-inline' 'unsafe-eval'",
"script-src 'self' 'unsafe-inline'",
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data: blob: https://*.tile.openstreetmap.org",
"font-src 'self'",