Skip to content

Commit 882cb84

Browse files
authored
Ignore URLs with custom protocols (#178)
1 parent 92cfc6b commit 882cb84

File tree

4 files changed

+33
-21
lines changed

4 files changed

+33
-21
lines changed

index.d.ts

+2
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,8 @@ export interface Options {
285285
/**
286286
[Normalize](https://en.wikipedia.org/wiki/URL_normalization) a URL.
287287
288+
URLs with custom protocols are not normalized and just passed through by default. Supported protocols are: `https`, `http`, `file`, and `data`.
289+
288290
@param url - URL to normalize, including [data URL](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs).
289291
290292
@example

index.js

+17-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,21 @@ const DATA_URL_DEFAULT_CHARSET = 'us-ascii';
44

55
const testParameter = (name, filters) => filters.some(filter => filter instanceof RegExp ? filter.test(name) : filter === name);
66

7+
const supportedProtocols = new Set([
8+
'https:',
9+
'http:',
10+
'file:',
11+
]);
12+
13+
const hasCustomProtocol = urlString => {
14+
try {
15+
const {protocol} = new URL(urlString);
16+
return protocol.endsWith(':') && !supportedProtocols.has(protocol);
17+
} catch {
18+
return false;
19+
}
20+
};
21+
722
const normalizeDataURL = (urlString, {stripHash}) => {
823
const match = /^data:(?<type>[^,]*?),(?<data>[^#]*?)(?:#(?<hash>.*))?$/.exec(urlString);
924

@@ -81,8 +96,8 @@ export default function normalizeUrl(urlString, options) {
8196
return normalizeDataURL(urlString, options);
8297
}
8398

84-
if (/^view-source:/i.test(urlString)) {
85-
throw new Error('`view-source:` is not supported as it is a non-standard protocol');
99+
if (hasCustomProtocol(urlString)) {
100+
return urlString;
86101
}
87102

88103
const hasRelativeProtocol = urlString.startsWith('//');

readme.md

+2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ normalizeUrl('//www.sindresorhus.com:80/../baz?b=bar&a=foo');
3030

3131
### normalizeUrl(url, options?)
3232

33+
URLs with custom protocols are not normalized and just passed through by default. Supported protocols are: `https`, `http`, `file`, and `data`.
34+
3335
#### url
3436

3537
Type: `string`

test.js

+12-19
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ test('main', t => {
1212
t.is(normalizeUrl('http://sindresorhus.com'), 'http://sindresorhus.com');
1313
t.is(normalizeUrl('http://sindresorhus.com:80'), 'http://sindresorhus.com');
1414
t.is(normalizeUrl('https://sindresorhus.com:443'), 'https://sindresorhus.com');
15-
t.is(normalizeUrl('ftp://sindresorhus.com:21'), 'ftp://sindresorhus.com');
1615
t.is(normalizeUrl('http://www.sindresorhus.com'), 'http://sindresorhus.com');
1716
t.is(normalizeUrl('www.com'), 'http://www.com');
1817
t.is(normalizeUrl('http://www.www.sindresorhus.com'), 'http://www.www.sindresorhus.com');
@@ -37,27 +36,25 @@ test('main', t => {
3736
t.is(normalizeUrl('http://sindresorhus.com/foo#bar:~:text=hello%20world', {stripHash: true}), 'http://sindresorhus.com/foo');
3837
t.is(normalizeUrl('http://sindresorhus.com/foo/bar/../baz'), 'http://sindresorhus.com/foo/baz');
3938
t.is(normalizeUrl('http://sindresorhus.com/foo/bar/./baz'), 'http://sindresorhus.com/foo/bar/baz');
40-
t.is(normalizeUrl('sindre://www.sorhus.com'), 'sindre://sorhus.com');
41-
t.is(normalizeUrl('sindre://www.sorhus.com/'), 'sindre://sorhus.com');
42-
t.is(normalizeUrl('sindre://www.sorhus.com/foo/bar'), 'sindre://sorhus.com/foo/bar');
39+
// t.is(normalizeUrl('sindre://www.sorhus.com'), 'sindre://sorhus.com');
40+
// t.is(normalizeUrl('sindre://www.sorhus.com/'), 'sindre://sorhus.com');
41+
// t.is(normalizeUrl('sindre://www.sorhus.com/foo/bar'), 'sindre://sorhus.com/foo/bar');
4342
t.is(normalizeUrl('https://i.vimeocdn.com/filter/overlay?src0=https://i.vimeocdn.com/video/598160082_1280x720.jpg&src1=https://f.vimeocdn.com/images_v6/share/play_icon_overlay.png'), 'https://i.vimeocdn.com/filter/overlay?src0=https://i.vimeocdn.com/video/598160082_1280x720.jpg&src1=https://f.vimeocdn.com/images_v6/share/play_icon_overlay.png');
4443
});
4544

4645
test('stripAuthentication option', t => {
4746
t.is(normalizeUrl('http://user:password@www.sindresorhus.com'), 'http://sindresorhus.com');
4847
t.is(normalizeUrl('https://user:password@www.sindresorhus.com'), 'https://sindresorhus.com');
4948
t.is(normalizeUrl('https://user:password@www.sindresorhus.com/@user'), 'https://sindresorhus.com/@user');
50-
t.is(normalizeUrl('user:password@sindresorhus.com'), 'http://sindresorhus.com');
5149
t.is(normalizeUrl('http://user:password@www.êxample.com'), 'http://xn--xample-hva.com');
52-
t.is(normalizeUrl('sindre://user:password@www.sorhus.com'), 'sindre://sorhus.com');
50+
// t.is(normalizeUrl('sindre://user:password@www.sorhus.com'), 'sindre://sorhus.com');
5351

5452
const options = {stripAuthentication: false};
5553
t.is(normalizeUrl('http://user:password@www.sindresorhus.com', options), 'http://user:password@sindresorhus.com');
5654
t.is(normalizeUrl('https://user:password@www.sindresorhus.com', options), 'https://user:password@sindresorhus.com');
5755
t.is(normalizeUrl('https://user:password@www.sindresorhus.com/@user', options), 'https://user:password@sindresorhus.com/@user');
58-
t.is(normalizeUrl('user:password@sindresorhus.com', options), 'http://user:password@sindresorhus.com');
5956
t.is(normalizeUrl('http://user:password@www.êxample.com', options), 'http://user:password@xn--xample-hva.com');
60-
t.is(normalizeUrl('sindre://user:password@www.sorhus.com', options), 'sindre://user:password@sorhus.com');
57+
// t.is(normalizeUrl('sindre://user:password@www.sorhus.com', options), 'sindre://user:password@sorhus.com');
6158
});
6259

6360
test('stripProtocol option', t => {
@@ -66,8 +63,6 @@ test('stripProtocol option', t => {
6663
t.is(normalizeUrl('http://sindresorhus.com', options), 'sindresorhus.com');
6764
t.is(normalizeUrl('https://www.sindresorhus.com', options), 'sindresorhus.com');
6865
t.is(normalizeUrl('//www.sindresorhus.com', options), 'sindresorhus.com');
69-
t.is(normalizeUrl('sindre://user:password@www.sorhus.com', options), 'sindre://sorhus.com');
70-
t.is(normalizeUrl('sindre://www.sorhus.com', options), 'sindre://sorhus.com');
7166
});
7267

7368
test('stripTextFragment option', t => {
@@ -98,7 +93,7 @@ test('stripWWW option', t => {
9893
t.is(normalizeUrl('http://www.sindresorhus.com', options), 'http://www.sindresorhus.com');
9994
t.is(normalizeUrl('www.sindresorhus.com', options), 'http://www.sindresorhus.com');
10095
t.is(normalizeUrl('http://www.êxample.com', options), 'http://www.xn--xample-hva.com');
101-
t.is(normalizeUrl('sindre://www.sorhus.com', options), 'sindre://www.sorhus.com');
96+
// t.is(normalizeUrl('sindre://www.sorhus.com', options), 'sindre://www.sorhus.com');
10297

10398
const options2 = {stripWWW: true};
10499
t.is(normalizeUrl('http://www.vue.amsterdam', options2), 'http://vue.amsterdam');
@@ -393,14 +388,6 @@ test('prevents homograph attack', t => {
393388
t.is(normalizeUrl('https://ebаy.com'), 'https://xn--eby-7cd.com');
394389
});
395390

396-
test('view-source URL', t => {
397-
t.throws(() => {
398-
normalizeUrl('view-source:https://www.sindresorhus.com');
399-
}, {
400-
message: '`view-source:` is not supported as it is a non-standard protocol',
401-
});
402-
});
403-
404391
test('does not have exponential performance for data URLs', t => {
405392
for (let index = 0; index < 1000; index += 50) {
406393
const url = 'data:' + Array.from({length: index}).fill(',#').join('') + '\ra';
@@ -414,3 +401,9 @@ test('does not have exponential performance for data URLs', t => {
414401
t.true(difference < 100, `Execution time: ${difference}`);
415402
}
416403
});
404+
405+
test('ignore custom schemes', t => {
406+
t.is(normalizeUrl('tel:004346382763'), 'tel:004346382763');
407+
t.is(normalizeUrl('mailto:office@foo.com'), 'mailto:office@foo.com');
408+
t.is(normalizeUrl('sindre://www.sindresorhus.com'), 'sindre://www.sindresorhus.com');
409+
});

0 commit comments

Comments
 (0)