Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add defensiveless for malformed hash urls without a leading slash #10753

Merged
merged 1 commit into from
Aug 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/hash-leading-slash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@remix-run/router": patch
---

Ensure hash history always includes a leading slash on hash pathnames
16 changes: 16 additions & 0 deletions packages/router/__tests__/hash-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,22 @@ describe("a hash history", () => {
expect(unencodedHref).toEqual("#/#abc");
});

it("prefixes raw hash values with /", () => {
dom.window.history.replaceState(null, "", "#hello");
history = createHashHistory({ window: dom.window as unknown as Window });
expect(history.location.pathname).toBe("/hello");

history.push("world");
expect(history.location.pathname).toBe("/world");

// Not supported but ensure we don't prefix here
history.push("./relative");
expect(history.location.pathname).toBe("./relative");

history.push("../relative");
expect(history.location.pathname).toBe("../relative");
});

describe("listen", () => {
it("does not immediately call listeners", () => {
Listen(history);
Expand Down
11 changes: 11 additions & 0 deletions packages/router/history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,17 @@ export function createHashHistory(
search = "",
hash = "",
} = parsePath(window.location.hash.substr(1));

// Hash URL should always have a leading / just like window.location.pathname
// does, so if an app ends up at a route like /#something then we add a
// leading slash so all of our path-matching behaves the same as if it would
// in a browser router. This is particularly important when there exists a
// root splat route (<Route path="*">) since that matches internally against
// "/*" and we'd expect /#something to 404 in a hash router app.
if (!pathname.startsWith("/") && !pathname.startsWith(".")) {
pathname = "/" + pathname;
}

return createLocation(
"",
{ pathname, search, hash },
Expand Down