fix(router): limit UrlParser recursion depth to prevent stack overflow · angular/angular@d6268c0
@@ -612,7 +612,13 @@ class UrlParser {
612612return 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+}
616622if (this.remaining === '') {
617623return {};
618624}
@@ -632,12 +638,12 @@ class UrlParser {
632638let children: {[outlet: string]: UrlSegmentGroup} = {};
633639if (this.peekStartsWith('/(')) {
634640this.capture('/');
635-children = this.parseParens(true);
641+children = this.parseParens(true, depth);
636642}
637643638644let res: {[outlet: string]: UrlSegmentGroup} = {};
639645if (this.peekStartsWith('(')) {
640-res = this.parseParens(false);
646+res = this.parseParens(false, depth);
641647}
642648643649if (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} {
727733const segments: {[key: string]: UrlSegmentGroup} = {};
728734this.capture('(');
729735@@ -750,7 +756,7 @@ class UrlParser {
750756outletName = PRIMARY_OUTLET;
751757}
752758753-const children = this.parseChildren();
759+const children = this.parseChildren(depth + 1);
754760segments[outletName ?? PRIMARY_OUTLET] =
755761Object.keys(children).length === 1 && children[PRIMARY_OUTLET]
756762 ? children[PRIMARY_OUTLET]