Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.

Merge pull request from GHSA-qhjf-hm5j-335w

* escape html characters in JSON string before injecting it into stream

* Add changeset

---------

Co-authored-by: Lenz Weber-Tronic <lorenz.weber-tronic@apollographql.com>
Co-authored-by: Phil Pluckthun <phil@kitten.sh>

Changed files
+31 -1
.changeset
packages
+5
.changeset/brave-buses-thank.md
···
+
---
+
'@urql/next': patch
+
---
+
+
Fix `CVE-2024-24556`, addressing an XSS vulnerability, where `@urql/next` failed to escape HTML characters in JSON payloads injected into RSC hydration bodies. When an attacker is able to manipulate strings in the JSON response in RSC payloads, this could cause HTML to be evaluated via a typical XSS vulnerability (See [`GHSA-qhjf-hm5j-335w`](https://github.com/urql-graphql/urql/security/advisories/GHSA-qhjf-hm5j-335w) for details.)
+2 -1
packages/next-urql/src/DataHydrationContext.ts
···
import * as React from 'react';
import { ServerInsertedHTMLContext } from 'next/navigation';
import type { UrqlResult } from './useUrqlValue';
+
import { htmlEscapeJsonString } from './htmlescape';
interface DataHydrationValue {
isInjecting: boolean;
···
function transportDataToJS(data: any) {
const key = 'urql_transport';
-
return `(window[Symbol.for("${key}")] ??= []).push(${JSON.stringify(data)})`;
+
return `(window[Symbol.for("${key}")] ??= []).push(${htmlEscapeJsonString(JSON.stringify(data))})`;
}
export const DataHydrationContextProvider = ({
+24
packages/next-urql/src/htmlescape.ts
···
+
// --------------------------------------------------------------------------------
+
//
+
// copied from
+
// https://github.com/vercel/next.js/blob/6bc07792a4462a4bf921a72ab30dc4ab2c4e1bda/packages/next/src/server/htmlescape.ts
+
// License: https://github.com/vercel/next.js/blob/6bc07792a4462a4bf921a72ab30dc4ab2c4e1bda/packages/next/license.md
+
//
+
// --------------------------------------------------------------------------------
+
+
// This utility is based on https://github.com/zertosh/htmlescape
+
// License: https://github.com/zertosh/htmlescape/blob/0527ca7156a524d256101bb310a9f970f63078ad/LICENSE
+
+
const ESCAPE_LOOKUP: { [match: string]: string } = {
+
"&": "\\u0026",
+
">": "\\u003e",
+
"<": "\\u003c",
+
"\u2028": "\\u2028",
+
"\u2029": "\\u2029",
+
};
+
+
export const ESCAPE_REGEX = /[&><\u2028\u2029]/g;
+
+
export function htmlEscapeJsonString(str: string): string {
+
return str.replace(ESCAPE_REGEX, (match) => ESCAPE_LOOKUP[match]);
+
}