Skip to content

Commit 0ebea40

Browse files
committed
Revert "fix(router): fall back when serializing protocol-relative URLs"
This reverts commit 41d6a8b.
1 parent 82a4289 commit 0ebea40

6 files changed

Lines changed: 1 addition & 144 deletions

File tree

‎goldens/public-api/router/errors.api.md‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ export const enum RuntimeErrorCode {
3737
// (undocumented)
3838
OUTLET_NOT_ACTIVATED = 4012,
3939
// (undocumented)
40-
PROTOCOL_RELATIVE_URL_NOT_ALLOWED = 4019,
41-
// (undocumented)
4240
ROOT_SEGMENT_MATRIX_PARAMS = 4003,
4341
// (undocumented)
4442
TWO_SEGMENTS_WITH_SAME_OUTLET = 4006,

‎packages/router/src/errors.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,4 @@ export const enum RuntimeErrorCode {
2929
INFINITE_REDIRECT = 4016,
3030
INVALID_ROUTER_LINK_INPUTS = 4017,
3131
ERROR_PARSING_URL = 4018,
32-
PROTOCOL_RELATIVE_URL_NOT_ALLOWED = 4019,
3332
}

‎packages/router/src/url_tree.ts‎

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,7 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9-
import {
10-
computed,
11-
ɵformatRuntimeError as formatRuntimeError,
12-
ɵRuntimeError as RuntimeError,
13-
Service,
14-
Signal,
15-
} from '@angular/core';
9+
import {computed, ɵRuntimeError as RuntimeError, Service, Signal} from '@angular/core';
1610

1711
import {RuntimeErrorCode} from './errors';
1812
import type {Router} from './router';
@@ -479,17 +473,6 @@ export class DefaultUrlSerializer implements UrlSerializer {
479473
/** Converts a `UrlTree` into a url */
480474
serialize(tree: UrlTree): string {
481475
const segment = `/${serializeSegment(tree.root, true)}`;
482-
if (segment.startsWith('//')) {
483-
if (typeof ngDevMode === 'undefined' || ngDevMode) {
484-
console.warn(
485-
formatRuntimeError(
486-
RuntimeErrorCode.PROTOCOL_RELATIVE_URL_NOT_ALLOWED,
487-
`Cannot serialize a UrlTree that would produce a protocol-relative URL. Falling back to '/' instead.`,
488-
),
489-
);
490-
}
491-
return '/';
492-
}
493476
const query = serializeQueryParams(tree.queryParams);
494477
const fragment =
495478
typeof tree.fragment === `string` ? `#${encodeUriFragment(tree.fragment)}` : '';

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

Lines changed: 0 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,6 @@ import {timeout} from '@angular/private/testing';
2222

2323
describe('createUrlTree', () => {
2424
const serializer = new DefaultUrlSerializer();
25-
const protocolRelativeUrlWarning = `NG04019: Cannot serialize a UrlTree that would produce a protocol-relative URL. Falling back to '/' instead.`;
26-
27-
function expectProtocolRelativeUrlFallback(tree: UrlTree): void {
28-
const warn = spyOn(console, 'warn');
29-
30-
expect(serializer.serialize(tree)).toEqual('/');
31-
expect(warn).toHaveBeenCalledOnceWith(protocolRelativeUrlWarning);
32-
}
33-
3425
let router: Router;
3526
beforeEach(() => {
3627
router = TestBed.inject(Router);
@@ -143,62 +134,6 @@ describe('createUrlTree', () => {
143134
expect(serializer.serialize(t)).toEqual('/%2Fone/two%2Fthree');
144135
});
145136

146-
describe('leading empty path commands', () => {
147-
it('should fall back for absolute navigations that would serialize as protocol-relative', () => {
148-
const t = router.createUrlTree(['/', '', '', 'attacker.example', 'collect']);
149-
150-
expectProtocolRelativeUrlFallback(t);
151-
});
152-
153-
it('should fall back for an unsafe primary outlet string', async () => {
154-
await router.navigateByUrl('/safe');
155-
const t = router.createUrlTree([{outlets: {primary: '/attacker.example/collect'}}]);
156-
157-
expectProtocolRelativeUrlFallback(t);
158-
});
159-
160-
it('should fall back for an unsafe primary outlet array', () => {
161-
const t = router.createUrlTree([{outlets: {primary: ['', 'attacker.example', 'collect']}}]);
162-
163-
expectProtocolRelativeUrlFallback(t);
164-
});
165-
166-
it('should fall back for unsafe parent-relative commands', async () => {
167-
router.resetConfig([{path: 'source', component: class {}}]);
168-
await router.navigateByUrl('/source');
169-
const t = create(router.routerState.root.firstChild!, [
170-
'../',
171-
'',
172-
'attacker.example',
173-
'collect',
174-
]);
175-
176-
expectProtocolRelativeUrlFallback(t);
177-
});
178-
179-
it('should fall back for an escaped slash after an empty path command', () => {
180-
const t = router.createUrlTree(['/', '', {segmentPath: '/'}]);
181-
182-
expectProtocolRelativeUrlFallback(t);
183-
});
184-
185-
it('should fall back for final empty path commands', () => {
186-
const t = router.createUrlTree(['/', '', '']);
187-
188-
expectProtocolRelativeUrlFallback(t);
189-
});
190-
191-
it('should not normalize a leading empty path command in a secondary outlet', () => {
192-
const t = router.createUrlTree(['/', {outlets: {right: ['', 'child']}}]);
193-
194-
expect(t.root.children['right'].segments.map((segment) => segment.path)).toEqual([
195-
'',
196-
'child',
197-
]);
198-
expect(serializer.serialize(t)).toEqual('/(right:/child)');
199-
});
200-
});
201-
202137
describe('named outlets', () => {
203138
it('should preserve secondary segments', async () => {
204139
const p = serializer.parse('/a/11/b(right:c)');

‎packages/router/test/router_link_spec.ts‎

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -329,27 +329,4 @@ describe('RouterLink', () => {
329329
await harness.navigateByUrl('/different');
330330
expect(anchor.getAttribute('href')).toBe('/different/child');
331331
});
332-
333-
it('falls back to the root for a link that would generate a protocol-relative href', async () => {
334-
@Component({
335-
template: `<a [routerLink]="commands" queryParamsHandling="preserve">commands</a>`,
336-
imports: [RouterLink],
337-
})
338-
class WithLink {
339-
readonly commands = ['/', '', 'attacker.example', 'collect'];
340-
}
341-
342-
TestBed.configureTestingModule({
343-
providers: [provideRouter([{path: '', component: WithLink}])],
344-
});
345-
const warn = spyOn(console, 'warn');
346-
const fixture = TestBed.createComponent(WithLink);
347-
348-
await fixture.whenStable();
349-
350-
expect(fixture.nativeElement.querySelector('a').getAttribute('href')).toBe('/');
351-
expect(warn).toHaveBeenCalledWith(
352-
`NG04019: Cannot serialize a UrlTree that would produce a protocol-relative URL. Falling back to '/' instead.`,
353-
);
354-
});
355332
});

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

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,11 @@ import {
1313
encodeUriQuery,
1414
encodeUriSegment,
1515
serializePath,
16-
UrlSegment,
1716
UrlSegmentGroup,
1817
} from '../src/url_tree';
1918

2019
describe('url serializer', () => {
2120
const url = new DefaultUrlSerializer();
22-
const protocolRelativeUrlWarning = `NG04019: Cannot serialize a UrlTree that would produce a protocol-relative URL. Falling back to '/' instead.`;
2321

2422
it('should parse the root url', () => {
2523
const tree = url.parse('/');
@@ -449,39 +447,6 @@ describe('url serializer', () => {
449447
});
450448
});
451449

452-
describe('leading empty path segments', () => {
453-
it('should fall back for a parsed primary outlet that would serialize as protocol-relative', () => {
454-
const warn = spyOn(console, 'warn');
455-
const tree = url.parse('/(primary://attacker.example/collect)?token=RESET_TOKEN');
456-
457-
expect(url.serialize(tree)).toBe('/');
458-
expect(warn).toHaveBeenCalledOnceWith(protocolRelativeUrlWarning);
459-
});
460-
461-
it('should fall back for multiple leading empty primary segments', () => {
462-
const warn = spyOn(console, 'warn');
463-
const tree = url.parse('/attacker.example/collect');
464-
tree.root.children[PRIMARY_OUTLET].segments.unshift(
465-
new UrlSegment('', {}),
466-
new UrlSegment('', {}),
467-
);
468-
469-
expect(url.serialize(tree)).toBe('/');
470-
expect(warn).toHaveBeenCalledOnceWith(protocolRelativeUrlWarning);
471-
});
472-
473-
it('should fall back for unsafe trees with secondary outlets, query params, and fragments', () => {
474-
const warn = spyOn(console, 'warn');
475-
const tree = url.parse(
476-
'/attacker.example/collect(popup:compose)?token=RESET_TOKEN#OAUTH_TOKEN',
477-
);
478-
tree.root.children[PRIMARY_OUTLET].segments.unshift(new UrlSegment('', {}));
479-
480-
expect(url.serialize(tree)).toBe('/');
481-
expect(warn).toHaveBeenCalledOnceWith(protocolRelativeUrlWarning);
482-
});
483-
});
484-
485450
describe('error handling', () => {
486451
it('should throw when invalid characters inside children', () => {
487452
expect(() => url.parse('/one/(left#one)')).toThrowError();

0 commit comments

Comments
 (0)