fix(document): HTML-escape NextScript.getInlineScriptSource output - #2727
Open
NathanDrake2406 wants to merge 1 commit into
Open
fix(document): HTML-escape NextScript.getInlineScriptSource output#2727NathanDrake2406 wants to merge 1 commit into
NathanDrake2406 wants to merge 1 commit into
Conversation
The shim returned raw JSON.stringify(context.__NEXT_DATA__). JSON.stringify does not escape characters the HTML parser treats as significant, so a string value containing "</script>" terminates the inline script it is embedded in and the remainder is parsed as HTML. Next.js returns htmlEscapeJsonString(data) from every path of this method (pages/_document.tsx). A custom _document that calls the helper and inlines the result — the pattern the method exists for — therefore loses escaping it would keep on Next.js, turning page props or query-derived data into script injection. Use safeJsonStringify, the escaper this repo already applies to the framework-generated __NEXT_DATA__ tag in pages-page-response.ts, so both paths produce the same output. The default renderer was unaffected.
commit: |
Contributor
Performance benchmarksCompared 1 improved · 0 regressed · 5 within ±1.5%
View detailed results and traces 🟢 improvement · 🔴 regression · ⚫ change below 1.5% · paired base/head |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
NextScript.getInlineScriptSource()in thenext/documentshim returned rawJSON.stringify(context.__NEXT_DATA__):JSON.stringifydoes not escape characters the HTML parser treats as significant. A string value containing</script>terminates the inline script the result is embedded in, and everything after it is parsed as HTML rather than as script content. The same applies to<,>,&, U+2028 and U+2029.Why it matters
This method exists so a custom
_documentcan place__NEXT_DATA__into its own inline<script>. Next.js returnshtmlEscapeJsonString(data)from every return path of the equivalent method (packages/next/src/pages/_document.tsx), so an app that relied on that escaping silently lost it here — page props or query-derived values become script injection on the app's origin.The default renderer was never affected:
pages-page-response.tsalready builds its__NEXT_DATA__tag withsafeJsonStringify. Only the custom-_documentpath diverged.Fix
Call
safeJsonStringify— the escaper already used for the framework-generated tag — so both paths emit identical output.server/html.tshas no imports, so pulling it into the shim adds no dependency weight.Testing
tests/document.test.tsasserts the output contains no literal</script>, contains the escaped form, and still round-trips throughJSON.parseto the original value.tests/document.test.ts+tests/safe-json.test.ts: 45 passing. Repo pre-commit full check, unit/integration suites and knip all passed.No behavior change for apps using the default document.