Skip to content

fix(history): use absolute path on http(s) to keep userinfo#2717

Open
gluebi wants to merge 2 commits into
vuejs:mainfrom
gluebi:fix/history-preserve-userinfo
Open

fix(history): use absolute path on http(s) to keep userinfo#2717
gluebi wants to merge 2 commits into
vuejs:mainfrom
gluebi:fix/history-preserve-userinfo

Conversation

@gluebi

@gluebi gluebi commented May 20, 2026

Copy link
Copy Markdown

Refs #2714

Problem

On Chromium, when the document URL contains userinfo (e.g.
http://user:pass@host/ from an HTTP Basic-Auth challenge),
createWebHistory()'s initial history.replaceState throws
SecurityError. Chromium enforces userinfo equality on
pushState/replaceState, not just origin equality.

changeLocation builds the URL via
createBaseLocation() + base + to, where
createBaseLocation = () => location.protocol + '//' + location.host.
location.host is hostname[:port] by spec, so userinfo is dropped.
The resulting replaceState URL http://host/ mismatches the
document URL http://user:pass@host/SecurityError → the
surrounding try/catch falls back to location.replace(url), which
silently reloads the page to the userinfo-less URL on the user's
first basic-auth visit, losing client-side state.

Live repro (Chromium, docker compose + nginx basic-auth):
https://github.com/gluebi/vue-router-5-basic-auth-repro

Fix

Per your hint in #2714, skip createBaseLocation() for non-file:
protocols. Pass an absolute path (base + to) and let the browser
resolve it against the document URL — that preserves scheme +
userinfo + host + port automatically.

For the remaining branches (file:// and protocol-relative //
paths), createBaseLocation() is still used. To keep userinfo there
as well, createBaseLocation() now reads it from location.href
via the URL constructor (since location.host strips it). file://
is unaffected in practice (no userinfo), but push('//foo') on a
credentialed document URL no longer hits the same SecurityError.

The protocol-relative // branch is preserved so the defense from
#261 isn't regressed.

Tests

  • New passes an absolute path when document URL contains userinfo
    regression for createWebHistory() initial replaceState throws SecurityError when document URL contains userinfo (HTTP basic-auth in URL) #2714 (happy-dom setURL simulates userinfo).
  • New keeps userinfo when prepending the host for // urls covers
    the createBaseLocation() userinfo path (push('//foo') on a
    basic-auth URL retains test:test@).
  • New prepends the file:// origin on file documents without a hash base
    covers the location.protocol === 'file:' branch (previously no
    file:// test ran without a hash base).
  • Existing prepends the host to support // urls updated — /foo
    now asserts an absolute path; the //foo defense path is unchanged.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed history URL handling so origins and protocol-relative paths are constructed correctly for file:// documents, protocol-relative (//) URLs, and URLs containing userinfo, preventing navigation errors.
  • Tests

    • Expanded unit coverage for history behavior, including documents with userinfo, protocol-relative URLs, and file:// documents without a hash base; adjusted expectations for relative push/replace behavior.

Review Change Stack

@netlify

netlify Bot commented May 20, 2026

Copy link
Copy Markdown

Deploy Preview for vue-router canceled.

Name Link
🔨 Latest commit 2d0dabe
🔍 Latest deploy log https://app.netlify.com/projects/vue-router/deploys/6a0d5a54182493000825c6db

@coderabbitai

coderabbitai Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a2d2837c-fece-4b1c-b141-0ba7302d528f

📥 Commits

Reviewing files that changed from the base of the PR and between 4037491 and 2d0dabe.

📒 Files selected for processing (2)
  • packages/router/__tests__/history/html5.spec.ts
  • packages/router/src/history/html5.ts

📝 Walkthrough

Walkthrough

This PR changes HTML5 history URL construction: changeLocation now conditionally prefixes origins only for file: or protocol-relative // paths and createBaseLocation preserves userinfo; tests updated and added for userinfo-containing document URLs and file:// documents.

Changes

HTML5 History URL Construction Fix

Layer / File(s) Summary
URL construction logic fix in changeLocation
packages/router/src/history/html5.ts
createBaseLocation() now parses location.href with URL to include protocol, host, and userinfo. changeLocation prefixes base + to with the explicit origin only when location.protocol === 'file:' or when (base + to) starts with //; otherwise it uses base + to directly.
Tests validating URL handling
packages/router/__tests__/history/html5.spec.ts
Updated expectation for history.push('/foo') to expect a relative '/foo'. Added Happy DOM tests: when document URL contains userinfo, replaceState is called with "/"; when prepending host for //... and document has userinfo, pushState includes the userinfo; for file:// documents without a hash base, replaceState receives the file:// origin URL.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Poem

🐰 A rabbit hops through history's maze,
Where URLs must find their ways—
Userinfo no longer spooks the stack,
File origins kept where they lack.
A tidy hop, then back on track.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly addresses the main fix: preserving userinfo by using absolute paths on HTTP(S) protocols, which is the core problem and solution described in the PR objectives.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gluebi gluebi force-pushed the fix/history-preserve-userinfo branch from 4f78e11 to 4037491 Compare May 20, 2026 06:41
@gluebi gluebi changed the title fix(history): use relative URL on http(s) to keep userinfo fix(history): use absolute path on http(s) to keep userinfo May 20, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/router/src/history/html5.ts`:
- Around line 235-236: The protocol-relative ("//") path handling in html5.ts
currently rebuilds the URL using location.host which strips userinfo; update the
branch that handles (base + to).startsWith('//') so it constructs the
protocol-relative prefix from the current document URL in a way that preserves
userinfo (e.g., derive the "scheme + // + authority" portion from location.href
or createBaseLocation() that includes userinfo instead of using location.host),
adjust the logic inside the same function that handles history.push to use this
preserved-authority prefix for protocol-relative targets, and add a regression
test exercising history.push('//foo') on a credentialed document URL to ensure
userinfo is retained and the same SecurityError behavior is reproduced.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: bbb9df8d-bfc1-46f9-9eda-8d771617fbf7

📥 Commits

Reviewing files that changed from the base of the PR and between 4f78e11 and 4037491.

📒 Files selected for processing (2)
  • packages/router/__tests__/history/html5.spec.ts
  • packages/router/src/history/html5.ts

Comment thread packages/router/src/history/html5.ts
gluebi added 2 commits May 20, 2026 08:52
Chromium enforces userinfo equality on pushState/replaceState.
Building the URL via `location.protocol + '//' + location.host`
strips userinfo and trips SecurityError on basic-auth documents
like http://user:pass@host/. Pass an absolute path on http(s)
so the browser resolves it against the document URL. For
file:// (no host) and `//`-prefixed paths (vuejs#261), still use
createBaseLocation() but include userinfo so those branches
don't strip it either.

Refs vuejs#2714
@gluebi gluebi force-pushed the fix/history-preserve-userinfo branch from 4037491 to 2d0dabe Compare May 20, 2026 06:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant