forked from validatorjs/validator.js
/
isURL.js
166 lines (139 loc) · 4.02 KB
/
isURL.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
import assertString from './util/assertString';
import isFQDN from './isFQDN';
import isIP from './isIP';
import merge from './util/merge';
/*
options for isURL method
require_protocol - if set as true isURL will return false if protocol is not present in the URL
require_valid_protocol - isURL will check if the URL's protocol is present in the protocols option
protocols - valid protocols can be modified with this option
require_host - if set as false isURL will not check if host is present in the URL
require_port - if set as true isURL will check if port is present in the URL
allow_protocol_relative_urls - if set as true protocol relative URLs will be allowed
validate_length - if set as false isURL will skip string length validation (IE maximum is 2083)
*/
const default_url_options = {
protocols: ['http', 'https', 'ftp'],
require_tld: true,
require_protocol: false,
require_host: true,
require_port: false,
require_valid_protocol: true,
allow_underscores: false,
allow_trailing_dot: false,
allow_protocol_relative_urls: false,
allow_fragments: true,
allow_query_components: true,
validate_length: true,
};
const wrapped_ipv6 = /^\[([^\]]+)\](?::([0-9]+))?$/;
function isRegExp(obj) {
return Object.prototype.toString.call(obj) === '[object RegExp]';
}
function checkHost(host, matches) {
for (let i = 0; i < matches.length; i++) {
let match = matches[i];
if (host === match || (isRegExp(match) && match.test(host))) {
return true;
}
}
return false;
}
export default function isURL(url, options) {
assertString(url);
if (!url || /[\s<>]/.test(url)) {
return false;
}
if (url.indexOf('mailto:') === 0) {
return false;
}
options = merge(options, default_url_options);
if (options.validate_length && url.length >= 2083) {
return false;
}
if (!options.allow_fragments && url.includes('#')) {
return false;
}
if (!options.allow_query_components && (url.includes('?') || url.includes('&'))) {
return false;
}
let protocol, auth, host, hostname, port, port_str, split, ipv6;
split = url.split('#');
url = split.shift();
split = url.split('?');
url = split.shift();
split = url.split('://');
if (split.length > 1) {
protocol = split.shift().toLowerCase();
if (options.require_valid_protocol && options.protocols.indexOf(protocol) === -1) {
return false;
}
} else if (options.require_protocol) {
return false;
} else if (url.substr(0, 2) === '//') {
if (!options.allow_protocol_relative_urls) {
return false;
}
split[0] = url.substr(2);
}
url = split.join('://');
if (url === '') {
return false;
}
split = url.split('/');
url = split.shift();
if (url === '' && !options.require_host) {
return true;
}
split = url.split('@');
if (split.length > 1) {
if (options.disallow_auth) {
return false;
}
if (split[0] === '') {
return false;
}
const user_password = split[0].split(':');
if (user_password[0] === '' && user_password[1] === '') {
return false;
}
auth = split.shift();
if (auth.indexOf(':') >= 0 && auth.split(':').length > 2) {
return false;
}
}
hostname = split.join('@');
port_str = null;
ipv6 = null;
const ipv6_match = hostname.match(wrapped_ipv6);
if (ipv6_match) {
host = '';
ipv6 = ipv6_match[1];
port_str = ipv6_match[2] || null;
} else {
split = hostname.split(':');
host = split.shift();
if (split.length) {
port_str = split.join(':');
}
}
if (port_str !== null) {
port = parseInt(port_str, 10);
if (!/^[0-9]+$/.test(port_str) || port <= 0 || port > 65535) {
return false;
}
} else if (options.require_port) {
return false;
}
if (options.host_whitelist) {
return checkHost(host, options.host_whitelist);
}
if (!isIP(host) && !isFQDN(host, options) && (!ipv6 || !isIP(ipv6, 6))) {
return false;
}
host = host || ipv6;
if (options.host_blacklist && checkHost(host, options.host_blacklist)) {
return false;
}
return true;
}