Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit c238bd2

Browse filesBrowse files
arshsmith1leonsenft
authored andcommitted
fix(router): handle outlet named __proto__ in segment group maps
Outlet maps are keyed by names read verbatim from the url, so a name like `__proto__` (e.g. `/one(__proto__:two)`) is assigned through the inherited `__proto__` setter instead of creating an outlet. This drops the outlet and mutates the map's prototype, and throws under Node's `--disable-proto=throw`. Build these outlet maps with `Object.create(null)` so `__proto__` is treated as an ordinary key. Covers `parseParens` and `squashSegmentGroup` in url_tree.ts, `createSegmentGroup` in apply_redirects.ts, and `replaceSegment` and `updateSegmentGroupChildren` in create_url_tree.ts. (cherry picked from commit cbbb1d8)
1 parent 2b3277f commit c238bd2
Copy full SHA for c238bd2

4 files changed

+24-5Lines changed: 24 additions & 5 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎packages/router/src/apply_redirects.ts‎

Copy file name to clipboardExpand all lines: packages/router/src/apply_redirects.ts
+2-1Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,8 @@ export class ApplyRedirects {
142142
): UrlSegmentGroup {
143143
const updatedSegments = this.createSegments(redirectTo, group.segments, segments, posParams);
144144

145-
let children: {[n: string]: UrlSegmentGroup} = {};
145+
// Keyed by outlet name, which can be `__proto__`, so use a null-prototype map.
146+
let children: {[n: string]: UrlSegmentGroup} = Object.create(null);
146147
Object.entries(group.children).forEach(([name, child]) => {
147148
children[name] = this.createSegmentGroup(redirectTo, child, segments, posParams);
148149
});
Collapse file

‎packages/router/src/create_url_tree.ts‎

Copy file name to clipboardExpand all lines: packages/router/src/create_url_tree.ts
+4-2Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,8 @@ function replaceSegment(
225225
oldSegment: UrlSegmentGroup,
226226
newSegment: UrlSegmentGroup,
227227
): UrlSegmentGroup {
228-
const children: {[key: string]: UrlSegmentGroup} = {};
228+
// Keyed by outlet name, which can be `__proto__`, so use a null-prototype map.
229+
const children: {[key: string]: UrlSegmentGroup} = Object.create(null);
229230
Object.entries(current.children).forEach(([outletName, c]) => {
230231
if (c === oldSegment) {
231232
children[outletName] = newSegment;
@@ -419,7 +420,8 @@ function updateSegmentGroupChildren(
419420
return new UrlSegmentGroup(segmentGroup.segments, {});
420421
} else {
421422
const outlets = getOutlets(commands);
422-
const children: {[key: string]: UrlSegmentGroup} = {};
423+
// Keyed by outlet name, which can be `__proto__`, so use a null-prototype map.
424+
const children: {[key: string]: UrlSegmentGroup} = Object.create(null);
423425
// If the set of commands applies to anything other than the primary outlet and the child
424426
// segment is an empty path primary segment on its own, we want to apply the commands to the
425427
// empty child path rather than here. The outcome is that the empty primary child is effectively
Collapse file

‎packages/router/src/url_tree.ts‎

Copy file name to clipboardExpand all lines: packages/router/src/url_tree.ts
+7-2Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -761,7 +761,11 @@ class UrlParser {
761761

762762
// parse `(a/b//outlet_name:c/d)`
763763
private parseParens(allowPrimary: boolean, depth: number): {[outlet: string]: UrlSegmentGroup} {
764-
const segments: {[key: string]: UrlSegmentGroup} = {};
764+
// The outlet name is taken verbatim from the URL, so it can be `__proto__`. Indexing a plain
765+
// object with that key assigns through the inherited `__proto__` setter instead of creating an
766+
// outlet, which drops the outlet and mutates the map's prototype (and throws under Node's
767+
// `--disable-proto=throw`). A null-prototype map makes `__proto__` an ordinary key.
768+
const segments: {[key: string]: UrlSegmentGroup} = Object.create(null);
765769
this.capture('(');
766770

767771
while (!this.consumeOptional(')') && this.remaining.length > 0) {
@@ -838,7 +842,8 @@ export function createRoot(rootCandidate: UrlSegmentGroup): UrlSegmentGroup {
838842
* root but the `a` route lives under an empty path primary route.
839843
*/
840844
export function squashSegmentGroup(segmentGroup: UrlSegmentGroup): UrlSegmentGroup {
841-
const newChildren: Record<string, UrlSegmentGroup> = {};
845+
// Keyed by outlet name, which can be `__proto__`, so use a null-prototype map (see `parseParens`).
846+
const newChildren: Record<string, UrlSegmentGroup> = Object.create(null);
842847
for (const [childOutlet, child] of Object.entries(segmentGroup.children)) {
843848
const childCandidate = squashSegmentGroup(child);
844849
// moves named children in an empty path primary child into this group
Collapse file

‎packages/router/test/url_serializer.spec.ts‎

Copy file name to clipboardExpand all lines: packages/router/test/url_serializer.spec.ts
+11Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,17 @@ describe('url serializer', () => {
132132
expect(url.serialize(tree)).toEqual('/one(left:two/three)');
133133
});
134134

135+
it('should parse a secondary segment named "__proto__"', () => {
136+
const tree = url.parse('/one(__proto__:two)');
137+
138+
expectSegment(tree.root.children[PRIMARY_OUTLET], 'one');
139+
expectSegment(tree.root.children['__proto__'], 'two');
140+
expect(tree.root.numberOfChildren).toEqual(2);
141+
expect(Object.getPrototypeOf(tree.root.children)).toBeNull();
142+
143+
expect(url.serialize(tree)).toEqual('/one(__proto__:two)');
144+
});
145+
135146
it('should parse an empty secondary segment group', () => {
136147
const tree = url.parse('/one()');
137148

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.