Skip to content

Commit 0bf6fa5

Browse files
jasonadenkara
authored andcommitted
fix(router): correct over-encoding of URL fragment (#22687)
Relates to: #10280 #22337 PR Close #22687
1 parent 40315be commit 0bf6fa5

File tree

2 files changed

+22
-17
lines changed

2 files changed

+22
-17
lines changed

packages/router/src/url_tree.ts

+15-10
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,8 @@ export class DefaultUrlSerializer implements UrlSerializer {
280280
serialize(tree: UrlTree): string {
281281
const segment = `/${serializeSegment(tree.root, true)}`;
282282
const query = serializeQueryParams(tree.queryParams);
283-
const fragment = typeof tree.fragment === `string` ? `#${encodeUriQuery(tree.fragment !)}` : '';
283+
const fragment =
284+
typeof tree.fragment === `string` ? `#${encodeUriFragment(tree.fragment !)}` : '';
284285

285286
return `${segment}${query}${fragment}`;
286287
}
@@ -329,13 +330,7 @@ function serializeSegment(segment: UrlSegmentGroup, root: boolean): string {
329330
* Encodes a URI string with the default encoding. This function will only ever be called from
330331
* `encodeUriQuery` or `encodeUriSegment` as it's the base set of encodings to be used. We need
331332
* a custom encoding because encodeURIComponent is too aggressive and encodes stuff that doesn't
332-
* have to be encoded per http://tools.ietf.org/html/rfc3986:
333-
* query = *( pchar / "/" / "?" )
334-
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
335-
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
336-
* pct-encoded = "%" HEXDIG HEXDIG
337-
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
338-
* / "*" / "+" / "," / ";" / "="
333+
* have to be encoded per https://url.spec.whatwg.org.
339334
*/
340335
function encodeUriString(s: string): string {
341336
return encodeURIComponent(s)
@@ -346,15 +341,25 @@ function encodeUriString(s: string): string {
346341
}
347342

348343
/**
349-
* This function should be used to encode both keys and values in a query string key/value or the
350-
* URL fragment. In the following URL, you need to call encodeUriQuery on "k", "v" and "f":
344+
* This function should be used to encode both keys and values in a query string key/value. In
345+
* the following URL, you need to call encodeUriQuery on "k" and "v":
351346
*
352347
* http://www.site.org/html;mk=mv?k=v#f
353348
*/
354349
export function encodeUriQuery(s: string): string {
355350
return encodeUriString(s).replace(/%3B/gi, ';');
356351
}
357352

353+
/**
354+
* This function should be used to encode a URL fragment. In the following URL, you need to call
355+
* encodeUriFragment on "f":
356+
*
357+
* http://www.site.org/html;mk=mv?k=v#f
358+
*/
359+
export function encodeUriFragment(s: string): string {
360+
return encodeURI(s);
361+
}
362+
358363
/**
359364
* This function should be run on any URI segment as well as the key and value in a key/value
360365
* pair for matrix params. In the following URL, you need to call encodeUriSegment on "html",

packages/router/test/url_serializer.spec.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88

99
import {PRIMARY_OUTLET} from '../src/shared';
10-
import {DefaultUrlSerializer, UrlSegmentGroup, encodeUriQuery, encodeUriSegment, serializePath} from '../src/url_tree';
10+
import {DefaultUrlSerializer, UrlSegmentGroup, encodeUriFragment, encodeUriQuery, encodeUriSegment, serializePath} from '../src/url_tree';
1111

1212
describe('url serializer', () => {
1313
const url = new DefaultUrlSerializer();
@@ -254,11 +254,11 @@ describe('url serializer', () => {
254254
});
255255

256256
it('should encode/decode fragment', () => {
257-
const u = `/one#${encodeUriQuery('one two=three four')}`;
257+
const u = `/one#${encodeUriFragment('one two=three four')}`;
258258
const tree = url.parse(u);
259259

260260
expect(tree.fragment).toEqual('one two=three four');
261-
expect(url.serialize(tree)).toEqual('/one#one%20two%3Dthree%20four');
261+
expect(url.serialize(tree)).toEqual('/one#one%20two=three%20four');
262262
});
263263
});
264264

@@ -311,7 +311,7 @@ describe('url serializer', () => {
311311
// From http://www.ietf.org/rfc/rfc3986.txt
312312
const unreserved = `abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~`;
313313

314-
it('should encode a minimal set of special characters in queryParams and fragment', () => {
314+
it('should encode a minimal set of special characters in queryParams', () => {
315315
const notEncoded = unreserved + `:@!$'*,();`;
316316
const encode = ` +%&=#[]/?`;
317317
const encoded = `%20%2B%25%26%3D%23%5B%5D%2F%3F`;
@@ -324,9 +324,9 @@ describe('url serializer', () => {
324324
});
325325

326326
it('should encode a minimal set of special characters in fragment', () => {
327-
const notEncoded = unreserved + `:@!$'*,();`;
328-
const encode = ` +%&=#[]/?`;
329-
const encoded = `%20%2B%25%26%3D%23%5B%5D%2F%3F`;
327+
const notEncoded = unreserved + `:@!$'*,();+&=#/?`;
328+
const encode = ' %<>`"[]';
329+
const encoded = `%20%25%3C%3E%60%22%5B%5D`;
330330

331331
const parsed = url.parse('/foo');
332332

0 commit comments

Comments
 (0)