Skip to content

Commit c89c32c

Browse files
authored
Escape HTML in scroll restoration keys (#14705)
1 parent 041c7b1 commit c89c32c

File tree

3 files changed

+11
-20
lines changed

3 files changed

+11
-20
lines changed

.changeset/fuzzy-worms-decide.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"react-router": patch
3+
---
4+
5+
Escape HTML in scroll restoration keys

packages/react-router/lib/dom/lib.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ import {
9797
} from "../hooks";
9898
import type { SerializeFrom } from "../types/route-data";
9999
import type { unstable_ClientInstrumentation } from "../router/instrumentation";
100+
import { escapeHtml } from "./ssr/markup";
100101

101102
////////////////////////////////////////////////////////////////////////////////
102103
//#region Global Stuff
@@ -2033,9 +2034,9 @@ export function ScrollRestoration({
20332034
{...props}
20342035
suppressHydrationWarning
20352036
dangerouslySetInnerHTML={{
2036-
__html: `(${restoreScroll})(${JSON.stringify(
2037-
storageKey || SCROLL_RESTORATION_STORAGE_KEY,
2038-
)}, ${JSON.stringify(ssrKey)})`,
2037+
__html: `(${restoreScroll})(${escapeHtml(
2038+
JSON.stringify(storageKey || SCROLL_RESTORATION_STORAGE_KEY),
2039+
)}, ${escapeHtml(JSON.stringify(ssrKey))})`,
20392040
}}
20402041
/>
20412042
);

packages/react-router/lib/dom/server.tsx

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import {
3535
ViewTransitionContext,
3636
} from "../context";
3737
import { useRoutesImpl } from "../hooks";
38+
import { escapeHtml } from "./ssr/markup";
3839

3940
/**
4041
* @category Types
@@ -187,7 +188,7 @@ export function StaticRouterProvider({
187188
// up parsing on the client. Dual-stringify is needed to ensure all quotes
188189
// are properly escaped in the resulting string. See:
189190
// https://v8.dev/blog/cost-of-javascript-2019#json
190-
let json = htmlEscape(JSON.stringify(JSON.stringify(data)));
191+
let json = escapeHtml(JSON.stringify(JSON.stringify(data)));
191192
hydrateScript = `window.__staticRouterHydrationData = JSON.parse(${json});`;
192193
}
193194

@@ -520,19 +521,3 @@ function encodeLocation(to: To): Path {
520521
}
521522

522523
const ABSOLUTE_URL_REGEX = /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i;
523-
524-
// This utility is based on https://github.com/zertosh/htmlescape
525-
// License: https://github.com/zertosh/htmlescape/blob/0527ca7156a524d256101bb310a9f970f63078ad/LICENSE
526-
const ESCAPE_LOOKUP: { [match: string]: string } = {
527-
"&": "\\u0026",
528-
">": "\\u003e",
529-
"<": "\\u003c",
530-
"\u2028": "\\u2028",
531-
"\u2029": "\\u2029",
532-
};
533-
534-
const ESCAPE_REGEX = /[&><\u2028\u2029]/g;
535-
536-
function htmlEscape(str: string): string {
537-
return str.replace(ESCAPE_REGEX, (match) => ESCAPE_LOOKUP[match]);
538-
}

0 commit comments

Comments
 (0)