From d40dc73cf903e8a12fd29e900df2406e7f611c5c Mon Sep 17 00:00:00 2001 From: Josh Story Date: Sat, 16 Apr 2022 10:47:46 -0700 Subject: [PATCH] Escape bootstrapScriptContent for javascript embedding into HTML (#24385) The previous escape was for Text into HTML and breaks script contents. The new escaping ensures that the script contents cannot prematurely close the host script tag by escaping script open and close string sequences using a unicode escape substitution. --- .../src/__tests__/ReactDOMFizzServer-test.js | 61 +++++++++++++++++++ .../src/server/ReactDOMServerFormatConfig.js | 13 +++- 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js index 295e99b26a691..da9097c51d5e3 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js @@ -2811,4 +2811,65 @@ describe('ReactDOMFizzServer', () => { , ); }); + + describe('bootstrapScriptContent escaping', () => { + // @gate experimental + it('the "S" in " { + window.__test_outlet = ''; + const stringWithScriptsInIt = + 'prescription pre'); +const scriptRegex = /(<\/|<)(s)(cript)/gi; +const scriptReplacer = (match, prefix, s, suffix) => + `${prefix}${s === 's' ? '\\u0073' : '\\u0053'}${suffix}`; + +function escapeBootstrapScriptContent(scriptText) { + if (__DEV__) { + checkHtmlStringCoercion(scriptText); + } + return ('' + scriptText).replace(scriptRegex, scriptReplacer); +} + // Allows us to keep track of what we've already written so we can refer back to it. export function createResponseState( identifierPrefix: string | void, @@ -102,7 +113,7 @@ export function createResponseState( if (bootstrapScriptContent !== undefined) { bootstrapChunks.push( inlineScriptWithNonce, - stringToChunk(escapeTextForBrowser(bootstrapScriptContent)), + stringToChunk(escapeBootstrapScriptContent(bootstrapScriptContent)), endInlineScript, ); }