fix(website): allow Next static inline scripts in CSP - #41
Conversation
📝 WalkthroughWalkthroughThe static web app’s global Content Security Policy now permits inline scripts by adding ChangesContent Security Policy Update
Estimated code review effort: 1 (Trivial) | ~2 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@website/public/staticwebapp.config.json`:
- Line 21: Update the CSP configuration around the content-security-policy entry
to document why the static-export Next.js build requires script-src
'unsafe-inline' and why nonce/hash-based protection is not feasible. Add a
focused CSP-violation scan or test and monitoring for inline-script violations;
if the build does not require the exception, remove it and narrow script-src
instead.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 32d8a03c-0c39-4524-acbe-586445ece673
📒 Files selected for processing (1)
website/public/staticwebapp.config.json
| }, | ||
| "globalHeaders": { | ||
| "content-security-policy": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data: https:; connect-src 'self' https:; frame-ancestors 'none'; base-uri 'self'; form-action 'self'", | ||
| "content-security-policy": "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data: https:; connect-src 'self' https:; frame-ancestors 'none'; base-uri 'self'; form-action 'self'", |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== candidate files =="
git ls-files | rg '(^|/)(staticwebapp\.config\.json|package\.json|next\.config\.(js|mjs)|README|docs?|SECURITY|Dockerfile|github/workflows|package-lock\.json|pnpm-lock\.yaml|yarn\.lock)$' || true
echo
echo "== staticwebapp config =="
if [ -f website/public/staticwebapp.config.json ]; then
cat -n website/public/staticwebapp.config.json
fi
echo
echo "== next config files outline/contents =="
for f in $(git ls-files | rg 'next\.config\.(js|mjs)$' || true); do
echo "--- $f ($(wc -l < "$f") lines) ---"
sed -n '1,220p' "$f"
done
echo
echo "== package scripts/deps snippets =="
if [ -f package.json ]; then
node - <<'JS'
const fs=require('fs');
const p=JSON.parse(fs.readFileSync('package.json','utf8'));
console.log(JSON.stringify({scripts:p.scripts, dependencies:p.dependencies, devDependencies:p.devDependencies, packageManager:p.packageManager}, null, 2));
JS
fi
echo
echo "== security/csp mentions =="
rg -n -i "unsafe-inline|content-security-policy|csp|nonce|hash|static.*export|next export|output\s*[:=]|trailingSlash|security|xss|vulnerab" README package.json website docs .github 2>/dev/null || true
echo
echo "== staticwebapp occurrences =="
rg -n "content-security-policy|staticwebapp|staticWebApp|headers|securityHeaders" .Repository: phoenixvc/codeflow-engine
Length of output: 50382
🌐 Web query:
Next.js static export script-src unsafe-inline CSP script runtime environment client side rendering
💡 Result:
When using Next.js with static exports, enforcing a Content Security Policy (CSP) that excludes 'unsafe-inline' is challenging because Next.js injects mandatory inline scripts (such as self.next_f.push for state management and hydration) into the generated HTML [1][2]. Since static pages are generated at build time, they cannot utilize request-specific nonces, which are the standard mechanism for allowing legitimate inline scripts in dynamic Next.js applications [3][4]. Key Technical Constraints: 1. Static Limitation: CSP nonces require dynamic server-side rendering because every request must generate a fresh, unique nonce [3][5]. Static exports lack this request-response cycle, making nonce-based CSPs incompatible with standard static deployment [3][4]. 2. Inline Script Injection: Next.js relies on inline scripts for core functionality, including client-side hydration and dynamic component loading [1][2]. Without 'unsafe-inline', these scripts are blocked, typically resulting in a broken, non-functional application [2]. Workarounds and Approaches: - Allowing 'unsafe-inline': The most straightforward, albeit less secure, approach is to include 'unsafe-inline' in your CSP script-src directive [6][7]. - Post-Build HTML Rewriting: Some developers bypass this by implementing a post-build script that extracts inline scripts into external files, updates the HTML to reference these files, and then serves the application with a stricter CSP that only allows 'self' [1]. - CSP Hashes: Theoretically, you could use hash-based CSPs for static inline scripts [8]. However, Next.js does not provide built-in support for automatically generating and applying these hashes during static export, making it a complex manual task to maintain as build outputs change [1][8]. - Moving to Dynamic Rendering: If a strict CSP (without 'unsafe-inline') is a hard requirement, you must move away from static export and use dynamic rendering (e.g., SSR or Server Components), which enables the injection of per-request nonces [3][9][5]. For applications requiring high security, prioritize evaluating if the trade-off of disabling static optimization in favor of dynamic rendering is acceptable to enable a strict, nonce-based CSP [4]. If you must stick with static exports, be aware that you are trading off strict CSP compliance for the performance benefits of static site generation [4].
Citations:
- 1: [Static export] Generated inline scripts, `self.__next_f.push` raises CSP errors vercel/next.js#54152
- 2: NextJS13 dynamic components injects <script> tag in the DOM causing weird markup and CSP errors even when using next export vercel/next.js#45099
- 3: https://nextjs.org/docs/15/app/guides/content-security-policy
- 4: https://johnkavanagh.co.uk/articles/content-security-policy-in-nextjs/
- 5: https://stackoverflow.com/questions/78268191/csp-with-nextjs-refused-to-execute-inline-script-in-production
- 6: https://nextjs.org/docs/app/guides/content-security-policy
- 7: https://nextjs.org/docs/pages/guides/content-security-policy
- 8: Inline scripts generated as a result of
appDirpreventing use of strict CSP vercel/next.js#43743 - 9: https://csp-guide.com/posts/csp-and-server-side-rendering-with-next-js-app-router/
Document the script-src 'unsafe-inline' exception.
The CSP now allows inline script execution globally, which can let an HTML-injection flaw run arbitrary JavaScript. Since this static-export Next.js build needs inline scripts, add a focused CSP-violation scan/test and monitoring, and document why nonce/hash-based CSP is not feasible; otherwise narrow the policy.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@website/public/staticwebapp.config.json` at line 21, Update the CSP
configuration around the content-security-policy entry to document why the
static-export Next.js build requires script-src 'unsafe-inline' and why
nonce/hash-based protection is not feasible. Add a focused CSP-violation scan or
test and monitoring for inline-script violations; if the build does not require
the exception, remove it and narrow script-src instead.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8afbd07364
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| }, | ||
| "globalHeaders": { | ||
| "content-security-policy": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data: https:; connect-src 'self' https:; frame-ancestors 'none'; base-uri 'self'; form-action 'self'", | ||
| "content-security-policy": "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data: https:; connect-src 'self' https:; frame-ancestors 'none'; base-uri 'self'; form-action 'self'", |
There was a problem hiding this comment.
Avoid allowing arbitrary inline scripts
Because this header is applied globally in Azure Static Web Apps, adding script-src 'unsafe-inline' does more than permit the Next-generated static snippets: it also authorizes any injected inline <script> or inline event handler on every route. That removes the XSS mitigation the previous script-src 'self' provided; for a static export, prefer build-time CSP hashes for the deterministic Next inline scripts, or another page-specific CSP mechanism, instead of globally allowing all inline script execution.
Useful? React with 👍 / 👎.
Summary
Validation
Summary by CodeRabbit