diff --git a/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js b/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js index 73295820485e7..9f8b7b1a265b5 100644 --- a/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js +++ b/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js @@ -2669,6 +2669,26 @@ function pushStyle( } } +/** + * This escaping function is designed to work with style tag textContent only. + * + * While untrusted style content should be made safe before using this api it will + * ensure that the style cannot be early terminated or never terminated state + */ +function escapeStyleTextContent(styleText: string) { + if (__DEV__) { + checkHtmlStringCoercion(styleText); + } + return ('' + styleText).replace(styleRegex, styleReplacer); +} +const styleRegex = /(<\/|<)(s)(tyle)/gi; +const styleReplacer = ( + match: string, + prefix: string, + s: string, + suffix: string, +) => `${prefix}${s === 's' ? '\\73 ' : '\\53 '}${suffix}`; + function pushStyleImpl( target: Array, props: Object, @@ -2710,7 +2730,7 @@ function pushStyleImpl( child !== undefined ) { // eslint-disable-next-line react-internal/safe-string-coercion - target.push(stringToChunk(escapeTextForBrowser('' + child))); + target.push(stringToChunk(escapeStyleTextContent(child))); } pushInnerHTML(target, innerHTML, children); target.push(endChunkForTag('style')); @@ -2752,7 +2772,7 @@ function pushStyleContents( child !== undefined ) { // eslint-disable-next-line react-internal/safe-string-coercion - target.push(stringToChunk(escapeTextForBrowser('' + child))); + target.push(stringToChunk(escapeStyleTextContent(child))); } pushInnerHTML(target, innerHTML, children); return; diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js index 50afd73d1f937..c91165a1ea77c 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js @@ -4256,6 +4256,56 @@ describe('ReactDOMFizzServer', () => { }); }); + describe(', + ); + pipe(writable); + }); + expect(window.getComputedStyle(document.body).backgroundColor).toMatch( + 'blue', + ); + }); + + it('the "S" in " { + await act(() => { + const {pipe} = renderToPipeableStream( + <> + + + , + ); + pipe(writable); + }); + expect(window.getComputedStyle(document.body).backgroundColor).toMatch( + 'red', + ); + }); + }); + // @gate enableFizzExternalRuntime it('supports option to load runtime as an external script', async () => { await act(() => {