Skip to content

Commit c6a6e5d

Browse files
committed
Normalize double-slashes in resolvePath
1 parent 13abd80 commit c6a6e5d

File tree

3 files changed

+70
-5
lines changed

3 files changed

+70
-5
lines changed

.changeset/nice-goats-shout.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"react-router": patch
3+
---
4+
5+
Normalize double-slashes in `resolvePath`

packages/react-router/__tests__/resolvePath-test.tsx

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,38 @@
11
import { resolvePath } from "react-router";
22

33
describe("resolvePath", () => {
4+
it("does not touch with protocol-less absolute paths", () => {
5+
expect(resolvePath("//google.com")).toMatchObject({
6+
pathname: "//google.com",
7+
});
8+
9+
expect(resolvePath("//google.com/../../path")).toMatchObject({
10+
pathname: "//google.com/../../path",
11+
});
12+
13+
expect(resolvePath("//google.com?q=query#hash")).toMatchObject({
14+
pathname: "//google.com",
15+
search: "?q=query",
16+
hash: "#hash",
17+
});
18+
});
19+
420
it('resolves absolute paths irrespective of the "from" pathname', () => {
521
expect(resolvePath("/search", "/inbox")).toMatchObject({
622
pathname: "/search",
723
});
24+
25+
expect(resolvePath("/search/../123", "/inbox")).toMatchObject({
26+
pathname: "/123",
27+
});
28+
29+
expect(resolvePath("/search/../../123", "/inbox")).toMatchObject({
30+
pathname: "/123",
31+
});
32+
33+
expect(resolvePath("/search/user/../../123", "/inbox")).toMatchObject({
34+
pathname: "/123",
35+
});
836
});
937

1038
it("resolves relative paths", () => {
@@ -23,6 +51,27 @@ describe("resolvePath", () => {
2351
expect(resolvePath("search", "/inbox")).toMatchObject({
2452
pathname: "/inbox/search",
2553
});
54+
55+
expect(resolvePath("search/../123", "/inbox")).toMatchObject({
56+
pathname: "/inbox/123",
57+
});
58+
59+
expect(resolvePath("search/../../123", "/inbox")).toMatchObject({
60+
pathname: "/123",
61+
});
62+
63+
expect(resolvePath("search/../../../123", "/inbox")).toMatchObject({
64+
pathname: "/123",
65+
});
66+
});
67+
68+
it("normalizes any mid-path double-slashes", () => {
69+
expect(resolvePath("/search/../..//foo")).toMatchObject({
70+
pathname: "/foo",
71+
});
72+
expect(resolvePath("search/../..//foo", "/inbox")).toMatchObject({
73+
pathname: "/foo",
74+
});
2675
});
2776

2877
it('ignores trailing slashes on the "from" pathname when resolving relative paths', () => {

packages/react-router/lib/router/utils.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1588,11 +1588,22 @@ export function resolvePath(to: To, fromPathname = "/"): Path {
15881588
hash = "",
15891589
} = typeof to === "string" ? parsePath(to) : to;
15901590

1591-
let pathname = toPathname
1592-
? toPathname.startsWith("/")
1593-
? toPathname
1594-
: resolvePathname(toPathname, fromPathname)
1595-
: fromPathname;
1591+
let pathname: string;
1592+
if (toPathname) {
1593+
if (toPathname.startsWith("//")) {
1594+
pathname = toPathname;
1595+
} else {
1596+
// Normalize double-slashes
1597+
toPathname = toPathname.replace(/\/\/+/g, "/");
1598+
if (toPathname.startsWith("/")) {
1599+
pathname = resolvePathname(toPathname.substring(1), "/");
1600+
} else {
1601+
pathname = resolvePathname(toPathname, fromPathname);
1602+
}
1603+
}
1604+
} else {
1605+
pathname = fromPathname;
1606+
}
15961607

15971608
return {
15981609
pathname,

0 commit comments

Comments
 (0)