Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
6 changes: 3 additions & 3 deletions packages/@ember/routing/history-location.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import EmberObject from '@ember/object';
import { assert } from '@ember/debug';
import type { default as EmberLocation, UpdateCallback } from '@ember/routing/location';
import { getHash } from './lib/location-utils';
import { escapeRegExp, getHash } from './lib/location-utils';

/**
@module @ember/routing/history-location
Expand Down Expand Up @@ -134,8 +134,8 @@ export default class HistoryLocation extends EmberObject implements EmberLocatio

// remove baseURL and rootURL from start of path
let url = path
.replace(new RegExp(`^${baseURL}(?=/|$)`), '')
.replace(new RegExp(`^${rootURL}(?=/|$)`), '')
.replace(new RegExp(`^${escapeRegExp(baseURL)}(?=/|$)`), '')
.replace(new RegExp(`^${escapeRegExp(rootURL)}(?=/|$)`), '')
.replace(/\/\//g, '/'); // remove extra slashes

let search = location.search || '';
Expand Down
10 changes: 10 additions & 0 deletions packages/@ember/routing/lib/location-utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
/**
@private

Escapes any regular-expression metacharacters in `str` so it can be safely
interpolated into a `RegExp` and matched as a literal.
*/
export function escapeRegExp(str: string): string {

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.

Image

https://caniuse.com/mdn-javascript_builtins_regexp_escape

we can't use this yet, but should document to delete this function once our minimum browser support all includes RegExp.escape

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good call. Added a TODO on the helper pointing at the caniuse link so we drop it for RegExp.escape once our minimum browsers cover it.

return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}

/**
@private

Expand Down
3 changes: 2 additions & 1 deletion packages/@ember/routing/none-location.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import EmberObject from '@ember/object';
import { assert } from '@ember/debug';
import type { default as EmberLocation, UpdateCallback } from '@ember/routing/location';
import { escapeRegExp } from './lib/location-utils';

/**
@module @ember/routing/none-location
Expand Down Expand Up @@ -63,7 +64,7 @@ export default class NoneLocation extends EmberObject implements EmberLocation {
rootURL = rootURL.replace(/\/$/, '');

// remove rootURL from url
return path.replace(new RegExp(`^${rootURL}(?=/|$)`), '');
return path.replace(new RegExp(`^${escapeRegExp(rootURL)}(?=/|$)`), '');
}

/**
Expand Down
17 changes: 17 additions & 0 deletions packages/@ember/routing/tests/location/history_location_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,23 @@ moduleFor(
assert.equal(location.getURL(), '/admin/profile/');
}

['@test HistoryLocation.getURL() treats regex metacharacters in rootURL and baseURL literally'](
assert
) {
HistoryTestLocation.reopen({
init() {
this._super(...arguments);
set(this, 'location', mockBrowserLocation('/axc/secret'));
set(this, 'rootURL', '/a.c/');
set(this, 'baseURL', '/a.c/');
},
});

createLocation();

assert.equal(location.getURL(), '/axc/secret');
}

['@test Existing state is preserved on init'](assert) {
let existingState = {
path: '/route/path',
Expand Down
14 changes: 14 additions & 0 deletions packages/@ember/routing/tests/location/none_location_test.js
Comment thread
NullVoxPopuli marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -84,5 +84,19 @@ moduleFor(

assert.equal(location.getURL(), '/bars/baz');
}

['@test NoneLocation.getURL() treats regex metacharacters in rootURL literally'](assert) {
NoneTestLocation.reopen({
init() {
this._super(...arguments);
set(this, 'rootURL', '/a.c/');
set(this, 'path', '/axc/secret');
},
});

createLocation();

assert.equal(location.getURL(), '/axc/secret');
}
}
);