diff --git a/.changeset/curvy-owls-brake.md b/.changeset/curvy-owls-brake.md
new file mode 100644
index 0000000000..4795b1dba3
--- /dev/null
+++ b/.changeset/curvy-owls-brake.md
@@ -0,0 +1,5 @@
+---
+'@shopify/react-html': patch
+---
+
+Less aggressive escaping of script tag content
diff --git a/packages/react-html/package.json b/packages/react-html/package.json
index ea0be19d0f..f9d00afe46 100644
--- a/packages/react-html/package.json
+++ b/packages/react-html/package.json
@@ -33,8 +33,8 @@
"@shopify/react-effect": "^5.0.2",
"@shopify/react-hydrate": "^3.0.6",
"@types/multistream": "^2.1.1",
- "multistream": "^2.1.1",
- "serialize-javascript": "^3.0.0"
+ "jsesc": "^3.0.2",
+ "multistream": "^2.1.1"
},
"peerDependencies": {
"react": ">=16.8.0 <19.0.0",
diff --git a/packages/react-html/src/server/components/Serialize.tsx b/packages/react-html/src/server/components/Serialize.tsx
index 2854b01424..3ccbbe0a8b 100644
--- a/packages/react-html/src/server/components/Serialize.tsx
+++ b/packages/react-html/src/server/components/Serialize.tsx
@@ -1,5 +1,5 @@
import React from 'react';
-import serialize from 'serialize-javascript';
+import jsesc from 'jsesc';
import {SERIALIZE_ATTRIBUTE} from '../../utilities';
@@ -9,10 +9,15 @@ interface Props {
}
export default function Serialize({id, data}: Props) {
+ const serialized = jsesc(data, {
+ isScriptContext: true,
+ json: true,
+ });
+
return (
);
diff --git a/packages/react-html/src/server/components/tests/Serialize.test.tsx b/packages/react-html/src/server/components/tests/Serialize.test.tsx
index 38f88691e2..24888e1501 100644
--- a/packages/react-html/src/server/components/tests/Serialize.test.tsx
+++ b/packages/react-html/src/server/components/tests/Serialize.test.tsx
@@ -1,5 +1,4 @@
import React from 'react';
-import serializeJavaScript from 'serialize-javascript';
import {mount} from '@shopify/react-testing';
import Serialize from '../Serialize';
@@ -23,14 +22,20 @@ describe('', () => {
describe('data', () => {
it('serializes the content as the child contents of the script tag', () => {
const data = {
- foo: {bar: {baz: 'window.location = "http://dangerous.com"'}},
+ foo: {
+ bar: {
+ baz: 'window.location = "http://example.com"',
+ },
+ },
+ xss: '',
};
const serialize = mount();
expect(serialize).toContainReactComponent('script', {
dangerouslySetInnerHTML: {
- __html: serializeJavaScript(data, {isJSON: true}),
+ __html:
+ '{"foo":{"bar":{"baz":"window.location = \\"http://example.com\\""}},"xss":"<\\/script>