VYPR
Medium severity5.4OSV Advisory· Published Apr 15, 2025· Updated Apr 15, 2026

CVE-2025-32388

CVE-2025-32388

Description

SvelteKit is a framework for rapidly developing robust, performant web applications using Svelte. Prior to 2.20.6 , unsanitized search param names cause XSS vulnerability. You are affected if you iterate over all entries of event.url.searchParams inside a server load function. Attackers can exploit it by crafting a malicious URL and getting a user to click a link with said URL. This vulnerability is fixed in 2.20.6.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Unsanitized search param names in SvelteKit's server load functions cause XSS; fixed in version 2.20.6.

Vulnerability

SvelteKit, a web framework based on Svelte, is affected by a stored XSS vulnerability in server load functions (CVE-2025-32388). The issue arises when the application iterates over all entries of event.url.searchParams inside a load function in +page.server.js or +layout.server.js [1][4]. The uses.search_params array, included in the boot script of server-rendered HTML, contains search param names in unsanitized form, allowing injection of malicious scripts [1].

Exploitation

An attacker can exploit this by crafting a malicious URL with a search parameter name containing HTML/script payloads (e.g., ?alert(1)). When a user clicks the link, the unsanitized parameter name is directly embedded into the server-rendered HTML without escaping, leading to arbitrary script execution in the user's browser [4]. The vulnerability is triggered regardless of whether the parameter values are returned or rendered; simply reading the keys during load is sufficient [4].

Impact

Successful exploitation allows an attacker to execute arbitrary JavaScript in the context of the victim's session. This can lead to data theft, session hijacking, or other malicious actions typically associated with XSS attacks [4].

Mitigation

The vulnerability has been patched in SvelteKit version 2.20.6 [3]. Users should upgrade immediately. The fix properly escapes search parameter names, preventing injection [1]. No workarounds are available for earlier versions.

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
@sveltejs/kitnpm
>= 2.0.0, < 2.20.62.20.6

Affected products

2
  • Range: @sveltejs/adapter-auto@2.0.1, @sveltejs/adapter-auto@2.1.0, @sveltejs/adapter-auto@2.1.1, …
  • ghsa-coords
    Range: >= 2.0.0, < 2.20.6

Patches

2
d3300c6a6790

Merge commit from fork

https://github.com/sveltejs/kitRich HarrisApr 14, 2025via ghsa
7 files changed · +43 16
  • .changeset/rotten-colts-arrive.md+5 0 added
    @@ -0,0 +1,5 @@
    +---
    +'@sveltejs/kit': patch
    +---
    +
    +fix: escape names of tracked search parameters
    
  • packages/kit/src/runtime/server/data/index.js+3 3 modified
    @@ -2,7 +2,7 @@ import { HttpError, SvelteKitError, Redirect } from '../../control.js';
     import { normalize_error } from '../../../utils/error.js';
     import { once } from '../../../utils/functions.js';
     import { load_server_data } from '../page/load_data.js';
    -import { clarify_devalue_error, handle_error_and_jsonify, stringify_uses } from '../utils.js';
    +import { clarify_devalue_error, handle_error_and_jsonify, serialize_uses } from '../utils.js';
     import { normalize_path } from '../../../utils/url.js';
     import { text } from '../../../exports/index.js';
     import * as devalue from 'devalue';
    @@ -253,8 +253,8 @@ export function get_data_json(event, options, nodes) {
     				return JSON.stringify(node);
     			}
     
    -			return `{"type":"data","data":${devalue.stringify(node.data, reducers)},${stringify_uses(
    -				node
    +			return `{"type":"data","data":${devalue.stringify(node.data, reducers)},${JSON.stringify(
    +				serialize_uses(node)
     			)}${node.slash ? `,"slash":${JSON.stringify(node.slash)}` : ''}}`;
     		});
     
    
  • packages/kit/src/runtime/server/page/render.js+6 4 modified
    @@ -7,7 +7,7 @@ import { serialize_data } from './serialize_data.js';
     import { s } from '../../../utils/misc.js';
     import { Csp } from './csp.js';
     import { uneval_action_response } from './actions.js';
    -import { clarify_devalue_error, stringify_uses, handle_error_and_jsonify } from '../utils.js';
    +import { clarify_devalue_error, handle_error_and_jsonify, serialize_uses } from '../utils.js';
     import { public_env, safe_public_env } from '../../shared-server.js';
     import { text } from '../../../exports/index.js';
     import { create_async_iterator } from '../../../utils/streaming.js';
    @@ -646,9 +646,11 @@ function get_data(event, options, nodes, csp, global) {
     		const strings = nodes.map((node) => {
     			if (!node) return 'null';
     
    -			return `{"type":"data","data":${devalue.uneval(node.data, replacer)},${stringify_uses(node)}${
    -				node.slash ? `,"slash":${JSON.stringify(node.slash)}` : ''
    -			}}`;
    +			/** @type {any} */
    +			const payload = { type: 'data', data: node.data, uses: serialize_uses(node) };
    +			if (node.slash) payload.slash = node.slash;
    +
    +			return devalue.uneval(payload, replacer);
     		});
     
     		return {
    
  • packages/kit/src/runtime/server/utils.js+9 9 modified
    @@ -147,26 +147,26 @@ export function clarify_devalue_error(event, error) {
     /**
      * @param {import('types').ServerDataNode} node
      */
    -export function stringify_uses(node) {
    -	const uses = [];
    +export function serialize_uses(node) {
    +	const uses = {};
     
     	if (node.uses && node.uses.dependencies.size > 0) {
    -		uses.push(`"dependencies":${JSON.stringify(Array.from(node.uses.dependencies))}`);
    +		uses.dependencies = Array.from(node.uses.dependencies);
     	}
     
     	if (node.uses && node.uses.search_params.size > 0) {
    -		uses.push(`"search_params":${JSON.stringify(Array.from(node.uses.search_params))}`);
    +		uses.search_params = Array.from(node.uses.search_params);
     	}
     
     	if (node.uses && node.uses.params.size > 0) {
    -		uses.push(`"params":${JSON.stringify(Array.from(node.uses.params))}`);
    +		uses.params = Array.from(node.uses.params);
     	}
     
    -	if (node.uses?.parent) uses.push('"parent":1');
    -	if (node.uses?.route) uses.push('"route":1');
    -	if (node.uses?.url) uses.push('"url":1');
    +	if (node.uses?.parent) uses.parent = 1;
    +	if (node.uses?.route) uses.route = 1;
    +	if (node.uses?.url) uses.url = 1;
     
    -	return `"uses":{${uses.join(',')}}`;
    +	return uses;
     }
     
     /**
    
  • packages/kit/test/apps/basics/src/routes/xss/query-tracking/+page.server.js+11 0 added
    @@ -0,0 +1,11 @@
    +export function load({ url }) {
    +	const values = {};
    +
    +	for (const key of url.searchParams.keys()) {
    +		values[key] = url.searchParams.get(key);
    +	}
    +
    +	return {
    +		values
    +	};
    +}
    
  • packages/kit/test/apps/basics/src/routes/xss/query-tracking/+page.svelte+1 0 added
    @@ -0,0 +1 @@
    +<p>check window.pwned</p>
    
  • packages/kit/test/apps/basics/test/cross-platform/test.js+8 0 modified
    @@ -1049,6 +1049,14 @@ test.describe('XSS', () => {
     			'user.name is </script><script>window.pwned = 1</script>'
     		);
     	});
    +
    +	test('no xss via tracked search parameters', async ({ page }) => {
    +		// https://github.com/sveltejs/kit/security/advisories/GHSA-6q87-84jw-cjhp
    +		await page.goto('/xss/query-tracking?</script/><script>window.pwned%3D1</script/>');
    +
    +		// @ts-expect-error - check global injected variable
    +		expect(await page.evaluate(() => window.pwned)).toBeUndefined();
    +	});
     });
     
     test.describe('$app/server', () => {
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.