fix(router): limit UrlParser recursion depth to prevent stack overflow · angular/angular@d6268c0

@@ -612,7 +612,13 @@ class UrlParser {

612612

return this.consumeOptional('#') ? decodeURIComponent(this.remaining) : null;

613613

}

614614615-

private parseChildren(): {[outlet: string]: UrlSegmentGroup} {

615+

private parseChildren(depth = 0): {[outlet: string]: UrlSegmentGroup} {

616+

if (depth > 50) {

617+

throw new RuntimeError(

618+

RuntimeErrorCode.UNPARSABLE_URL,

619+

(typeof ngDevMode === 'undefined' || ngDevMode) && 'URL is too deep',

620+

);

621+

}

616622

if (this.remaining === '') {

617623

return {};

618624

}

@@ -632,12 +638,12 @@ class UrlParser {

632638

let children: {[outlet: string]: UrlSegmentGroup} = {};

633639

if (this.peekStartsWith('/(')) {

634640

this.capture('/');

635-

children = this.parseParens(true);

641+

children = this.parseParens(true, depth);

636642

}

637643638644

let res: {[outlet: string]: UrlSegmentGroup} = {};

639645

if (this.peekStartsWith('(')) {

640-

res = this.parseParens(false);

646+

res = this.parseParens(false, depth);

641647

}

642648643649

if (segments.length > 0 || Object.keys(children).length > 0) {

@@ -723,7 +729,7 @@ class UrlParser {

723729

}

724730725731

// parse `(a/b//outlet_name:c/d)`

726-

private parseParens(allowPrimary: boolean): {[outlet: string]: UrlSegmentGroup} {

732+

private parseParens(allowPrimary: boolean, depth: number): {[outlet: string]: UrlSegmentGroup} {

727733

const segments: {[key: string]: UrlSegmentGroup} = {};

728734

this.capture('(');

729735

@@ -750,7 +756,7 @@ class UrlParser {

750756

outletName = PRIMARY_OUTLET;

751757

}

752758753-

const children = this.parseChildren();

759+

const children = this.parseChildren(depth + 1);

754760

segments[outletName ?? PRIMARY_OUTLET] =

755761

Object.keys(children).length === 1 && children[PRIMARY_OUTLET]

756762

? children[PRIMARY_OUTLET]