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

ignore URLs that the app does not own #1487

Merged
merged 1 commit into from
May 18, 2021
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/wise-bees-juggle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

Ignore URLs that the app does not own
40 changes: 28 additions & 12 deletions packages/kit/src/runtime/client/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ export class Router {
// Don't handle hash changes
if (url.pathname === location.pathname && url.search === location.search) return;

if (!this.owns(url)) return;

const noscroll = a.hasAttribute('sveltekit:noscroll');
history.pushState({}, '', url.href);
this._navigate(url, noscroll ? scroll_state() : null, [], url.hash);
Expand All @@ -155,22 +157,26 @@ export class Router {
history.replaceState(history.state || {}, '', location.href);
}

/** @param {URL} url */
owns(url) {
return url.origin === location.origin && url.pathname.startsWith(this.base);
}

/**
* @param {URL} url
* @returns {import('./types').NavigationInfo}
*/
parse(url) {
if (url.origin !== location.origin) return null;
if (!url.pathname.startsWith(this.base)) return null;
if (this.owns(url)) {
const path = decodeURIComponent(url.pathname.slice(this.base.length) || '/');

const path = decodeURIComponent(url.pathname.slice(this.base.length) || '/');
const routes = this.routes.filter(([pattern]) => pattern.test(path));

const routes = this.routes.filter(([pattern]) => pattern.test(path));
const query = new URLSearchParams(url.search);
const id = `${path}?${query}`;

const query = new URLSearchParams(url.search);
const id = `${path}?${query}`;

return { id, routes, path, query };
return { id, routes, path, query };
}
}

/**
Expand All @@ -179,14 +185,14 @@ export class Router {
* @param {string[]} chain
*/
async goto(href, { noscroll = false, replaceState = false } = {}, chain) {
if (this.enabled) {
const url = new URL(href, get_base_uri(document));
const url = new URL(href, get_base_uri(document));

if (this.enabled && this.owns(url)) {
history[replaceState ? 'replaceState' : 'pushState']({}, '', href);
return this._navigate(url, noscroll ? scroll_state() : null, chain, url.hash);
}

location.href = href;
location.href = url.href;
return new Promise(() => {
/* never resolves */
});
Expand All @@ -205,7 +211,13 @@ export class Router {
* @returns {Promise<import('./types').NavigationResult>}
*/
async prefetch(url) {
return this.renderer.load(this.parse(url));
const info = this.parse(url);

if (!info) {
throw new Error('Attempted to prefetch a URL that does not belong to this app');
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if it's worth trying to shorten this message the one below to save bytes shipped to the client such as: 'Attempted to prefetch external URL'

Copy link
Member Author

Choose a reason for hiding this comment

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

i don't think 'external' is correct — nytimes.com isn't external to nytimes.com/interactive/2021/us/covid-cases.html, in the normal sense of that word in a URL context

}

return this.renderer.load(info);
}

/**
Expand All @@ -217,6 +229,10 @@ export class Router {
async _navigate(url, scroll, chain, hash) {
const info = this.parse(url);

if (!info) {
throw new Error('Attempted to navigate to a URL that does not belong to this app');
}

// remove trailing slashes
if (info.path !== '/') {
const has_trailing_slash = info.path.endsWith('/');
Expand Down
18 changes: 18 additions & 0 deletions packages/kit/test/apps/basics/src/routes/routing/_tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,15 @@ export default function (test) {

const requests2 = await capture_requests(() => app.goto('/routing/prefetched'));
assert.equal(requests2, []);

try {
await app.prefetch('https://example.com');
throw new Error('Error was not thrown');
} catch (e) {
assert.ok(
e.message.includes('Attempted to prefetch a URL that does not belong to this app')
);
}
}
});

Expand Down Expand Up @@ -200,4 +209,13 @@ export default function (test) {
assert.equal(await page.textContent('h2'), 'y-z');
}
);

test(
'ignores navigation to URLs the app does not own',
'/routing',
async ({ page, clicknav }) => {
await clicknav('[href="https://www.google.com"]');
Copy link
Member

Choose a reason for hiding this comment

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

there's some chance that this will cause tests to fail on the CI. I have no idea what Google will do if it thinks it's being hit by an automated bot. Maybe we should use svelte.dev?

Copy link
Member

Choose a reason for hiding this comment

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

The extra safe version would probably be to spin up another server on localhost on a different port, but that might be more hassle than it's worth, especially if this is a fix you're trying to get out quickly.

Copy link
Member Author

Choose a reason for hiding this comment

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

it works fine locally, so presumably google has no problem with it. agree that it's a potential source of flakiness, but realistically if google.com is inaccessible the internet probably has bigger problems that day

assert.equal(await page.url(), 'https://www.google.com/');
}
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@

<a href="/routing/a">a</a>
<a href="/routing/ambiguous/ok.json">ok</a>
<a href="https://www.google.com">elsewhere</a>

<div class='hydrate-test'></div>
<div class='hydrate-test'></div>