mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
Revert "fix(router): limit protocol-relative URL handling to serialization"
This reverts commit435f8b2b8b. (cherry picked from commit292991e2df)
This commit is contained in:
committed by
Jessica Janiuk
parent
f61f2d0500
commit
223f25ff37
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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)}` : '';
|
||||
|
||||
@@ -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)');
|
||||
|
||||
@@ -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: `<a [routerLink]="commands" queryParamsHandling="preserve">commands</a>`,
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user