diff --git a/goldens/public-api/router/errors.api.md b/goldens/public-api/router/errors.api.md index 95a36f8abcc..0ed6714f7a1 100644 --- a/goldens/public-api/router/errors.api.md +++ b/goldens/public-api/router/errors.api.md @@ -37,8 +37,6 @@ export const enum RuntimeErrorCode { // (undocumented) OUTLET_NOT_ACTIVATED = 4012, // (undocumented) - PROTOCOL_RELATIVE_URL_NOT_ALLOWED = 4019, - // (undocumented) ROOT_SEGMENT_MATRIX_PARAMS = 4003, // (undocumented) TWO_SEGMENTS_WITH_SAME_OUTLET = 4006, diff --git a/packages/router/src/errors.ts b/packages/router/src/errors.ts index a8cc313a683..734d56e1c80 100644 --- a/packages/router/src/errors.ts +++ b/packages/router/src/errors.ts @@ -29,5 +29,4 @@ export const enum RuntimeErrorCode { INFINITE_REDIRECT = 4016, INVALID_ROUTER_LINK_INPUTS = 4017, ERROR_PARSING_URL = 4018, - PROTOCOL_RELATIVE_URL_NOT_ALLOWED = 4019, } diff --git a/packages/router/src/url_tree.ts b/packages/router/src/url_tree.ts index c729b9a3ddf..ee6853cc087 100644 --- a/packages/router/src/url_tree.ts +++ b/packages/router/src/url_tree.ts @@ -454,13 +454,6 @@ export class DefaultUrlSerializer implements UrlSerializer { /** Converts a `UrlTree` into a url */ serialize(tree: UrlTree): string { const segment = `/${serializeSegment(tree.root, true)}`; - if (segment.startsWith('//')) { - throw new RuntimeError( - RuntimeErrorCode.PROTOCOL_RELATIVE_URL_NOT_ALLOWED, - (typeof ngDevMode === 'undefined' || ngDevMode) && - 'Cannot serialize a UrlTree that would produce a protocol-relative URL.', - ); - } const query = serializeQueryParams(tree.queryParams); const fragment = typeof tree.fragment === `string` ? `#${encodeUriFragment(tree.fragment)}` : ''; diff --git a/packages/router/test/create_url_tree.spec.ts b/packages/router/test/create_url_tree.spec.ts index 764a26f963a..8132136d1ed 100644 --- a/packages/router/test/create_url_tree.spec.ts +++ b/packages/router/test/create_url_tree.spec.ts @@ -22,8 +22,6 @@ import {timeout} from '@angular/private/testing'; describe('createUrlTree', () => { const serializer = new DefaultUrlSerializer(); - const protocolRelativeUrlError = - /NG04019: Cannot serialize a UrlTree that would produce a protocol-relative URL/; let router: Router; beforeEach(() => { router = TestBed.inject(Router); @@ -136,62 +134,6 @@ describe('createUrlTree', () => { expect(serializer.serialize(t)).toEqual('/%2Fone/two%2Fthree'); }); - describe('leading empty path commands', () => { - it('should reject absolute navigations that would serialize as protocol-relative', () => { - const t = router.createUrlTree(['/', '', '', 'attacker.example', 'collect']); - - expect(() => serializer.serialize(t)).toThrowError(protocolRelativeUrlError); - }); - - it('should reject an unsafe primary outlet string', async () => { - await router.navigateByUrl('/safe'); - const t = router.createUrlTree([{outlets: {primary: '/attacker.example/collect'}}]); - - expect(() => serializer.serialize(t)).toThrowError(protocolRelativeUrlError); - }); - - it('should reject an unsafe primary outlet array', () => { - const t = router.createUrlTree([{outlets: {primary: ['', 'attacker.example', 'collect']}}]); - - expect(() => serializer.serialize(t)).toThrowError(protocolRelativeUrlError); - }); - - it('should reject unsafe parent-relative commands', async () => { - router.resetConfig([{path: 'source', component: class {}}]); - await router.navigateByUrl('/source'); - const t = create(router.routerState.root.firstChild!, [ - '../', - '', - 'attacker.example', - 'collect', - ]); - - expect(() => serializer.serialize(t)).toThrowError(protocolRelativeUrlError); - }); - - it('should reject an escaped slash after an empty path command', () => { - const t = router.createUrlTree(['/', '', {segmentPath: '/'}]); - - expect(() => serializer.serialize(t)).toThrowError(protocolRelativeUrlError); - }); - - it('should reject final empty path commands', () => { - const t = router.createUrlTree(['/', '', '']); - - expect(() => serializer.serialize(t)).toThrowError(protocolRelativeUrlError); - }); - - it('should not normalize a leading empty path command in a secondary outlet', () => { - const t = router.createUrlTree(['/', {outlets: {right: ['', 'child']}}]); - - expect(t.root.children['right'].segments.map((segment) => segment.path)).toEqual([ - '', - 'child', - ]); - expect(serializer.serialize(t)).toEqual('/(right:/child)'); - }); - }); - describe('named outlets', () => { it('should preserve secondary segments', async () => { const p = serializer.parse('/a/11/b(right:c)'); diff --git a/packages/router/test/router_link_spec.ts b/packages/router/test/router_link_spec.ts index 8d880c2bfb2..c675a03a561 100644 --- a/packages/router/test/router_link_spec.ts +++ b/packages/router/test/router_link_spec.ts @@ -329,24 +329,4 @@ describe('RouterLink', () => { await harness.navigateByUrl('/different'); expect(anchor.getAttribute('href')).toBe('/different/child'); }); - - it('rejects a link that would generate a protocol-relative href', async () => { - @Component({ - template: `commands`, - imports: [RouterLink], - }) - class WithLink { - readonly commands = ['/', '', 'attacker.example', 'collect']; - } - - TestBed.configureTestingModule({ - providers: [provideRouter([{path: '', component: WithLink}])], - }); - const fixture = TestBed.createComponent(WithLink); - - await expectAsync(fixture.whenStable()).toBeRejectedWithError( - /NG04019: Cannot serialize a UrlTree that would produce a protocol-relative URL/, - ); - expect(fixture.nativeElement.querySelector('a').getAttribute('href')).toBeNull(); - }); }); diff --git a/packages/router/test/url_serializer.spec.ts b/packages/router/test/url_serializer.spec.ts index cdb1a5444c2..70329510ab0 100644 --- a/packages/router/test/url_serializer.spec.ts +++ b/packages/router/test/url_serializer.spec.ts @@ -13,14 +13,11 @@ import { encodeUriQuery, encodeUriSegment, serializePath, - UrlSegment, UrlSegmentGroup, } from '../src/url_tree'; describe('url serializer', () => { const url = new DefaultUrlSerializer(); - const protocolRelativeUrlError = - /NG04019: Cannot serialize a UrlTree that would produce a protocol-relative URL/; it('should parse the root url', () => { const tree = url.parse('/'); @@ -450,33 +447,6 @@ describe('url serializer', () => { }); }); - describe('leading empty path segments', () => { - it('should reject a parsed primary outlet that would serialize as protocol-relative', () => { - const tree = url.parse('/(primary://attacker.example/collect)?token=RESET_TOKEN'); - - expect(() => url.serialize(tree)).toThrowError(protocolRelativeUrlError); - }); - - it('should reject multiple leading empty primary segments', () => { - const tree = url.parse('/attacker.example/collect'); - tree.root.children[PRIMARY_OUTLET].segments.unshift( - new UrlSegment('', {}), - new UrlSegment('', {}), - ); - - expect(() => url.serialize(tree)).toThrowError(protocolRelativeUrlError); - }); - - it('should reject unsafe trees with secondary outlets, query params, and fragments', () => { - const tree = url.parse( - '/attacker.example/collect(popup:compose)?token=RESET_TOKEN#OAUTH_TOKEN', - ); - tree.root.children[PRIMARY_OUTLET].segments.unshift(new UrlSegment('', {})); - - expect(() => url.serialize(tree)).toThrowError(protocolRelativeUrlError); - }); - }); - describe('error handling', () => { it('should throw when invalid characters inside children', () => { expect(() => url.parse('/one/(left#one)')).toThrowError();