CVE-2024-23841 (published 2024-01-30T18:15Z)

apollo-client-nextjs is the Apollo Client support for the Next.js App Router. The @apollo/experimental-apollo-client-nextjs NPM package is vulnerable to a cross-site scripting vulnerability. To exploit this vulnerability, an attacker would need to either inject malicious input (e.g. by redirecting a user to a specifically-crafted link) or arrange to have malicious input be returned by a GraphQL server (e.g. by persisting it in a database). To fix this issue, please update to version 0.7.0 or later.

CVSS v3 base score: 6.1, MEDIUM (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N)

Weakness information:
cwe_id: CWE-80
cwe_name: Improper Neutralization of Script-Related HTML Tags in a Web Page (Basic XSS)
description: The product receives input from an upstream component, but it does not neutralize or incorrectly neutralizes special characters such as "<", ">", and "&" that could be interpreted as web-scripting elements when they are sent to a downstream component that processes web pages.
extended_description: This may allow such characters to be treated as control characters, which are executed client-side in the context of the user's session. Although this can be classified as an injection problem, the more pertinent issue is the improper conversion of such special characters to respective context-appropriate entities before displaying them to the user.
         

Fix information:
repo_url: https://github.com/apollographql/apollo-client-nextjs
commit_details: repo_url: https://github.com/apollographql/apollo-client-nextjs
author: Lenz Weber-Tronic
author_date: 2024-01-30 18:00:32+01:00
msg: Merge pull request from GHSA-rv8p-rr2h-fgpg

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

* increment package version
repository: repo_url: https://github.com/apollographql/apollo-client-nextjs
repo_name: apollographql/apollo-client-nextjs
description: Apollo Client support for the Next.js App Router
repo_language: TypeScript
owner: apollographql
file_changes: filename: package.json
change_type: ModificationType.MODIFY
diff: @@ -1,6 +1,6 @@
 {
   "name": "@apollo/experimental-nextjs-app-support",
-  "version": "0.6.0",
+  "version": "0.7.0",
   "repository": {
     "url": "git+https://github.com/apollographql/apollo-client-nextjs"
   },

programming_language: JavaScript
filename: dataTransport.ts
change_type: ModificationType.MODIFY
diff: @@ -9,6 +9,7 @@ import type { RehydrationCache } from "./types";
 import { registerLateInitializingQueue } from "./lateInitializingQueue";
 import type { Cache, WatchQueryOptions } from "@apollo/client";
 import invariant from "ts-invariant";
+import { htmlEscapeJsonString } from "../util/htmlescape";
 
 export type DataTransport<T> = Array<T> | { push(...args: T[]): void };
 
@@ -23,8 +24,8 @@ type DataToTransport = {
  */
 export function transportDataToJS(data: DataToTransport) {
   const key = Symbol.keyFor(ApolloSSRDataTransport);
-  return `(window[Symbol.for("${key}")] ??= []).push(${SuperJSON.stringify(
-    data
+  return `(window[Symbol.for("${key}")] ??= []).push(${htmlEscapeJsonString(
+    SuperJSON.stringify(data)
   )})`;
 }
 

programming_language: TypeScript
filename: htmlescape.ts
change_type: ModificationType.ADD
diff: @@ -0,0 +1,24 @@
+// --------------------------------------------------------------------------------
+//
+// 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]);
+}

programming_language: JavaScript