Skip to content

Commit

Permalink
[fix] prevent double decoding of params
Browse files Browse the repository at this point in the history
  • Loading branch information
Anton Channov committed Nov 7, 2022
1 parent 0a63441 commit 53a01a8
Show file tree
Hide file tree
Showing 6 changed files with 34 additions and 16 deletions.
5 changes: 5 additions & 0 deletions .changeset/curly-ants-guess.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

[fix] prevent double decoding of params
10 changes: 8 additions & 2 deletions packages/kit/src/runtime/client/client.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { onMount, tick } from 'svelte';
import { make_trackable, decode_params, normalize_path, add_data_suffix } from '../../utils/url.js';
import {
make_trackable,
decode_pathname,
decode_params,
normalize_path,
add_data_suffix
} from '../../utils/url.js';
import { find_anchor, get_base_uri, scroll_state } from './utils.js';
import {
lock_fetch,
Expand Down Expand Up @@ -992,7 +998,7 @@ export function create_client({ target, base, trailing_slash }) {
function get_navigation_intent(url, invalidating) {
if (is_external_url(url)) return;

const path = decodeURI(url.pathname.slice(base.length) || '/');
const path = decode_pathname(url.pathname.slice(base.length) || '/');

for (const route of routes) {
const params = route.exec(path);
Expand Down
3 changes: 2 additions & 1 deletion packages/kit/src/runtime/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { coalesce_to_error } from '../../utils/error.js';
import { is_form_content_type } from '../../utils/http.js';
import { GENERIC_ERROR, handle_fatal_error } from './utils.js';
import {
decode_pathname,
decode_params,
disable_search,
has_data_suffix,
Expand Down Expand Up @@ -44,7 +45,7 @@ export async function respond(request, options, state) {

let decoded;
try {
decoded = decodeURI(url.pathname);
decoded = decode_pathname(url.pathname);
} catch {
return new Response('Malformed URI', { status: 400 });
}
Expand Down
23 changes: 10 additions & 13 deletions packages/kit/src/utils/url.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,23 +54,20 @@ export function normalize_path(path, trailing_slash) {
return path;
}

/**
* Decode pathname excluding %25 to prevent further double decoding of params
* @param {string} pathname
*/
export function decode_pathname(pathname) {
return pathname.split('%25').map(decodeURI).join('%25');
}

/** @param {Record<string, string>} params */
export function decode_params(params) {
for (const key in params) {
// input has already been decoded by decodeURI
// now handle the rest that decodeURIComponent would do
params[key] = params[key]
.replace(/%23/g, '#')
.replace(/%3[Bb]/g, ';')
.replace(/%2[Cc]/g, ',')
.replace(/%2[Ff]/g, '/')
.replace(/%3[Ff]/g, '?')
.replace(/%3[Aa]/g, ':')
.replace(/%40/g, '@')
.replace(/%26/g, '&')
.replace(/%3[Dd]/g, '=')
.replace(/%2[Bb]/g, '+')
.replace(/%24/g, '$');
// now handle the rest
params[key] = decodeURIComponent(params[key]);
}

return params;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
<a href="/encoded/@svelte">@svelte</a>
<a href="/encoded/$SVLT">$SVLT</a>
<a href="/encoded/test%2520me">test%20me</a>
<a href="/encoded/test%252fme">test%2fme</a>
<a href="/encoded/AC%2fDC">AC/DC</a>
<a href="/encoded/%5b">[</a>
8 changes: 8 additions & 0 deletions packages/kit/test/apps/basics/test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,14 @@ test.describe('Encoded paths', () => {
expect(await page.innerHTML('h3')).toBe('/encoded/test%2520me: test%20me');
});

test('visits a route with a doubly encoded slash', async ({ page, clicknav }) => {
await page.goto('/encoded');
await clicknav('[href="/encoded/test%252fme"]');
expect(await page.innerHTML('h1')).toBe('dynamic');
expect(await page.innerHTML('h2')).toBe('/encoded/test%252fme: test%2fme');
expect(await page.innerHTML('h3')).toBe('/encoded/test%252fme: test%2fme');
});

test('visits a route with an encoded slash', async ({ page, clicknav }) => {
await page.goto('/encoded');
await clicknav('[href="/encoded/AC%2fDC"]');
Expand Down

0 comments on commit 53a01a8

Please sign in to comment.