Skip to content

Commit 05ab3b5

Browse files
committed
fix: properly handle invalid URLs in router
Resolves #668
1 parent adbaaed commit 05ab3b5

File tree

3 files changed

+40
-2
lines changed

3 files changed

+40
-2
lines changed

router.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ import {
6767
type TokensToRegexpOptions,
6868
} from "./deps.ts";
6969
import { compose, type Middleware } from "./middleware.ts";
70+
import { decode } from "./utils/decode.ts";
7071
import { decodeComponent } from "./utils/decode_component.ts";
7172

7273
interface Matches<R extends string> {
@@ -1299,8 +1300,7 @@ export class Router<
12991300
} catch (e) {
13001301
return Promise.reject(e);
13011302
}
1302-
const path = this.#opts.routerPath ?? ctx.routerPath ??
1303-
decodeURI(pathname);
1303+
const path = this.#opts.routerPath ?? ctx.routerPath ?? decode(pathname);
13041304
const matches = this.#match(path, method);
13051305

13061306
if (ctx.matched) {

utils/decode.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2018-2024 the oak authors. All rights reserved. MIT license.
2+
3+
import { assert, isHttpError } from "../deps.ts";
4+
import { assertEquals } from "../deps_test.ts";
5+
import { decode } from "./decode.ts";
6+
7+
Deno.test({
8+
name: "decodeComponent - throws HTTP error",
9+
fn() {
10+
try {
11+
decode("%");
12+
} catch (err) {
13+
assert(isHttpError(err));
14+
assertEquals(err.status, 400);
15+
assertEquals(err.expose, false);
16+
return;
17+
}
18+
throw Error("unaccessible code");
19+
},
20+
});

utils/decode.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2018-2024 the oak authors. All rights reserved. MIT license.
2+
3+
import { createHttpError } from "../deps.ts";
4+
5+
/**
6+
* Safely decode a URI component, where if it fails, instead of throwing,
7+
* just returns the original string
8+
*/
9+
export function decode(pathname: string): string {
10+
try {
11+
return decodeURI(pathname);
12+
} catch (err) {
13+
if (err instanceof URIError) {
14+
throw createHttpError(400, "Failed to decode URI", { expose: false });
15+
}
16+
throw err;
17+
}
18+
}

0 commit comments

Comments
 (0)