OLD | NEW |
1 /* | 1 /* |
2 * This file is part of Adblock Plus <https://adblockplus.org/>, | 2 * This file is part of Adblock Plus <https://adblockplus.org/>, |
3 * Copyright (C) 2006-present eyeo GmbH | 3 * Copyright (C) 2006-present eyeo GmbH |
4 * | 4 * |
5 * Adblock Plus is free software: you can redistribute it and/or modify | 5 * Adblock Plus is free software: you can redistribute it and/or modify |
6 * it under the terms of the GNU General Public License version 3 as | 6 * it under the terms of the GNU General Public License version 3 as |
7 * published by the Free Software Foundation. | 7 * published by the Free Software Foundation. |
8 * | 8 * |
9 * Adblock Plus is distributed in the hope that it will be useful, | 9 * Adblock Plus is distributed in the hope that it will be useful, |
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 * GNU General Public License for more details. | 12 * GNU General Public License for more details. |
13 * | 13 * |
14 * You should have received a copy of the GNU General Public License | 14 * You should have received a copy of the GNU General Public License |
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. | 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. |
16 */ | 16 */ |
17 | 17 |
18 "use strict"; | 18 "use strict"; |
19 | 19 |
| 20 const assert = require("assert"); |
20 // Only starting NodeJS 10 that URL is in the global space. | 21 // Only starting NodeJS 10 that URL is in the global space. |
21 const {URL} = require("url"); | 22 const {URL} = require("url"); |
22 const {createSandbox} = require("./_common"); | 23 const {createSandbox} = require("./_common"); |
23 | |
24 const publicSuffixes = require("../data/publicSuffixList.json"); | 24 const publicSuffixes = require("../data/publicSuffixList.json"); |
25 | 25 |
26 let parseURL = null; | 26 let parseURL = null; |
27 let normalizeHostname = null; | 27 let normalizeHostname = null; |
28 let domainSuffixes = null; | 28 let domainSuffixes = null; |
29 let isThirdParty = null; | 29 let isThirdParty = null; |
30 let getBaseDomain = null; | 30 let getBaseDomain = null; |
31 | 31 |
32 exports.setUp = function(callback) | 32 describe("URL", () => |
33 { | 33 { |
34 let sandboxedRequire = createSandbox({ | 34 beforeEach(() => |
35 extraExports: { | 35 { |
36 domain: ["getBaseDomain"] | 36 let sandboxedRequire = createSandbox({ |
| 37 extraExports: { |
| 38 domain: ["getBaseDomain"] |
| 39 } |
| 40 }); |
| 41 ( |
| 42 {parseURL, normalizeHostname, domainSuffixes, isThirdParty, |
| 43 getBaseDomain} = sandboxedRequire("../lib/url") |
| 44 ); |
| 45 }); |
| 46 |
| 47 it("Normalize Hostname", () => |
| 48 { |
| 49 assert.equal(normalizeHostname("example.com"), "example.com"); |
| 50 assert.equal(normalizeHostname("example.com."), "example.com"); |
| 51 assert.equal(normalizeHostname("example.com.."), "example.com"); |
| 52 assert.equal(normalizeHostname("example.com..."), "example.com"); |
| 53 |
| 54 assert.equal(normalizeHostname("Example.com"), "example.com"); |
| 55 assert.equal(normalizeHostname("ExaMple.Com"), "example.com"); |
| 56 assert.equal(normalizeHostname("ExaMple.Com.."), "example.com"); |
| 57 |
| 58 assert.equal(normalizeHostname("192.168.1.1"), "192.168.1.1"); |
| 59 assert.equal(normalizeHostname("192.168.1.1."), "192.168.1.1"); |
| 60 |
| 61 assert.equal(normalizeHostname("2001:0db8:85a3:0000:0000:8a2e:0370:7334"), |
| 62 "2001:0db8:85a3:0000:0000:8a2e:0370:7334"); |
| 63 assert.equal(normalizeHostname("2001:0db8:85a3:0000:0000:8a2e:0370:7334."), |
| 64 "2001:0db8:85a3:0000:0000:8a2e:0370:7334"); |
| 65 assert.equal(normalizeHostname("2001:0DB8:85A3:0000:0000:8A2E:0370:7334"), |
| 66 "2001:0db8:85a3:0000:0000:8a2e:0370:7334"); |
| 67 }); |
| 68 |
| 69 it("Domain Suffixes", () => |
| 70 { |
| 71 assert.deepEqual([...domainSuffixes("localhost")], ["localhost"]); |
| 72 assert.deepEqual([...domainSuffixes("example.com")], ["example.com", "com"])
; |
| 73 assert.deepEqual([...domainSuffixes("www.example.com")], |
| 74 ["www.example.com", "example.com", "com"]); |
| 75 assert.deepEqual([...domainSuffixes("www.example.co.in")], |
| 76 ["www.example.co.in", "example.co.in", "co.in", "in"]); |
| 77 |
| 78 // With blank. |
| 79 assert.deepEqual([...domainSuffixes("localhost", true)], ["localhost", ""]); |
| 80 assert.deepEqual([...domainSuffixes("example.com", true)], |
| 81 ["example.com", "com", ""]); |
| 82 assert.deepEqual([...domainSuffixes("www.example.com", true)], |
| 83 ["www.example.com", "example.com", "com", ""]); |
| 84 assert.deepEqual([...domainSuffixes("www.example.co.in", true)], |
| 85 ["www.example.co.in", "example.co.in", "co.in", "in", ""]); |
| 86 |
| 87 // Quirks and edge cases. |
| 88 assert.deepEqual([...domainSuffixes("")], []); |
| 89 assert.deepEqual([...domainSuffixes(".")], ["."]); |
| 90 assert.deepEqual([...domainSuffixes(".localhost")], |
| 91 [".localhost", "localhost"]); |
| 92 assert.deepEqual([...domainSuffixes(".example.com")], |
| 93 [".example.com", "example.com", "com"]); |
| 94 assert.deepEqual([...domainSuffixes("localhost.")], |
| 95 ["localhost."]); |
| 96 assert.deepEqual([...domainSuffixes("example.com.")], |
| 97 ["example.com.", "com."]); |
| 98 assert.deepEqual([...domainSuffixes("..localhost")], |
| 99 ["..localhost", ".localhost", "localhost"]); |
| 100 assert.deepEqual( |
| 101 [...domainSuffixes("..example..com")], |
| 102 ["..example..com", ".example..com", "example..com", ".com", "com"] |
| 103 ); |
| 104 assert.deepEqual([...domainSuffixes("localhost..")], ["localhost..", "."]); |
| 105 assert.deepEqual([...domainSuffixes("example..com..")], |
| 106 ["example..com..", ".com..", "com..", "."]); |
| 107 }); |
| 108 |
| 109 it("URL parsing", () => |
| 110 { |
| 111 function testURLParsing(url) |
| 112 { |
| 113 // Note: The function expects a normalized URL. |
| 114 // e.g. "http:example.com:80?foo" should already be normalized to |
| 115 // "http://example.com/?foo". If not, the tests will fail. |
| 116 let urlInfo = parseURL(url); |
| 117 |
| 118 // We need to ensure only that our implementation matches that of the URL |
| 119 // object. |
| 120 let urlObject = new URL(url); |
| 121 |
| 122 assert.equal(urlInfo.href, urlObject.href); |
| 123 assert.equal(urlInfo.protocol, urlObject.protocol); |
| 124 assert.equal(urlInfo.hostname, urlObject.hostname); |
| 125 |
| 126 assert.equal(urlInfo.toString(), urlObject.toString()); |
| 127 assert.equal(String(urlInfo), String(urlObject)); |
| 128 assert.equal(urlInfo + "", urlObject + ""); |
37 } | 129 } |
38 }); | 130 |
39 ( | 131 testURLParsing("https://example.com/"); |
40 {parseURL, normalizeHostname, domainSuffixes, isThirdParty, | 132 testURLParsing("https://example.com/foo"); |
41 getBaseDomain} = sandboxedRequire("../lib/url") | 133 testURLParsing("https://example.com/foo/bar"); |
42 ); | 134 testURLParsing( |
43 | 135 "https://example.com/foo/bar?https://random/foo/bar" |
44 callback(); | 136 ); |
45 }; | 137 |
46 | 138 testURLParsing("https://example.com:8080/"); |
47 function hostnameToURL(hostname) | 139 testURLParsing("https://example.com:8080/foo"); |
48 { | 140 testURLParsing("https://example.com:8080/foo/bar"); |
49 return new URL("http://" + hostname); | 141 testURLParsing( |
50 } | 142 "https://example.com:8080/foo/bar?https://random/foo/bar" |
51 | 143 ); |
52 function testURLParsing(test, url) | 144 |
53 { | 145 testURLParsing("http://localhost/"); |
54 // Note: The function expects a normalized URL. | 146 testURLParsing("http://localhost/foo"); |
55 // e.g. "http:example.com:80?foo" should already be normalized to | 147 testURLParsing("http://localhost/foo/bar"); |
56 // "http://example.com/?foo". If not, the tests will fail. | 148 testURLParsing( |
57 let urlInfo = parseURL(url); | 149 "http://localhost/foo/bar?https://random/foo/bar" |
58 | 150 ); |
59 // We need to ensure only that our implementation matches that of the URL | 151 |
60 // object. | 152 testURLParsing("https://user@example.com/"); |
61 let urlObject = new URL(url); | 153 testURLParsing("https://user@example.com/foo"); |
62 | 154 testURLParsing("https://user@example.com/foo/bar"); |
63 test.equal(urlInfo.href, urlObject.href); | 155 testURLParsing( |
64 test.equal(urlInfo.protocol, urlObject.protocol); | 156 "https://user@example.com/foo/bar?https://random/foo/bar" |
65 test.equal(urlInfo.hostname, urlObject.hostname); | 157 ); |
66 | 158 |
67 test.equal(urlInfo.toString(), urlObject.toString()); | 159 testURLParsing("https://user@example.com:8080/"); |
68 test.equal(String(urlInfo), String(urlObject)); | 160 testURLParsing("https://user@example.com:8080/foo"); |
69 test.equal(urlInfo + "", urlObject + ""); | 161 testURLParsing("https://user@example.com:8080/foo/bar"); |
70 } | 162 testURLParsing( |
71 | 163 "https://user@example.com:8080/foo/bar?https://random/foo/bar" |
72 function testThirdParty(test, requestHostname, documentHostname, expected, | 164 ); |
73 message) | 165 |
74 { | 166 testURLParsing("https://user:pass@example.com/"); |
75 test.equal( | 167 testURLParsing("https://user:pass@example.com/foo"); |
76 isThirdParty( | 168 testURLParsing("https://user:pass@example.com/foo/bar"); |
77 hostnameToURL(requestHostname).hostname, | 169 testURLParsing( |
78 | 170 "https://user:pass@example.com/foo/bar?https://random/foo/bar" |
79 // Chrome's URL object normalizes IP addresses. So some test | 171 ); |
80 // will fail if we don't normalize the document host as well. | 172 |
81 hostnameToURL(documentHostname).hostname | 173 testURLParsing("https://user:pass@example.com:8080/"); |
82 ), | 174 testURLParsing("https://user:pass@example.com:8080/foo"); |
83 expected, | 175 testURLParsing("https://user:pass@example.com:8080/foo/bar"); |
84 message | 176 testURLParsing( |
85 ); | 177 "https://user:pass@example.com:8080/foo/bar?https://random/foo/bar" |
86 } | 178 ); |
87 | 179 |
88 exports.testParseURL = function(test) | 180 testURLParsing("https://us%40er:pa%40ss@example.com/"); |
89 { | 181 testURLParsing("https://us%40er:pa%40ss@example.com/foo"); |
90 testURLParsing(test, "https://example.com/"); | 182 testURLParsing("https://us%40er:pa%40ss@example.com/foo/bar"); |
91 testURLParsing(test, "https://example.com/foo"); | 183 testURLParsing( |
92 testURLParsing(test, "https://example.com/foo/bar"); | 184 "https://us%40er:pa%40ss@example.com/foo/bar?https://random/foo/bar" |
93 testURLParsing( | 185 ); |
94 test, | 186 |
95 "https://example.com/foo/bar?https://random/foo/bar" | 187 testURLParsing("https://us%40er:pa%40ss@example.com:8080/"); |
96 ); | 188 testURLParsing("https://us%40er:pa%40ss@example.com:8080/foo"); |
97 | 189 testURLParsing("https://us%40er:pa%40ss@example.com:8080/foo/bar"); |
98 testURLParsing(test, "https://example.com:8080/"); | 190 testURLParsing( |
99 testURLParsing(test, "https://example.com:8080/foo"); | 191 "https://us%40er:pa%40ss@example.com:8080/foo/bar?https://random/foo/bar" |
100 testURLParsing(test, "https://example.com:8080/foo/bar"); | 192 ); |
101 testURLParsing( | 193 |
102 test, | 194 testURLParsing("http://192.168.1.1/"); |
103 "https://example.com:8080/foo/bar?https://random/foo/bar" | 195 testURLParsing("http://192.168.1.1/foo"); |
104 ); | 196 testURLParsing("http://192.168.1.1/foo/bar"); |
105 | 197 testURLParsing( |
106 testURLParsing(test, "http://localhost/"); | 198 "http://192.168.1.1/foo/bar?https://random/foo/bar" |
107 testURLParsing(test, "http://localhost/foo"); | 199 ); |
108 testURLParsing(test, "http://localhost/foo/bar"); | 200 testURLParsing( |
109 testURLParsing( | 201 "http://192.168.1.1:8080/foo/bar?https://random/foo/bar" |
110 test, | 202 ); |
111 "http://localhost/foo/bar?https://random/foo/bar" | 203 testURLParsing( |
112 ); | 204 "http://user@192.168.1.1:8080/foo/bar?https://random/foo/bar" |
113 | 205 ); |
114 testURLParsing(test, "https://user@example.com/"); | 206 testURLParsing( |
115 testURLParsing(test, "https://user@example.com/foo"); | 207 "http://user:pass@192.168.1.1:8080/foo/bar?https://random/foo/bar" |
116 testURLParsing(test, "https://user@example.com/foo/bar"); | 208 ); |
117 testURLParsing( | 209 |
118 test, | 210 testURLParsing("http://[2001:db8:0:42:0:8a2e:370:7334]/"); |
119 "https://user@example.com/foo/bar?https://random/foo/bar" | 211 testURLParsing("http://[2001:db8:0:42:0:8a2e:370:7334]/foo"); |
120 ); | 212 testURLParsing( |
121 | 213 "http://[2001:db8:0:42:0:8a2e:370:7334]/foo/bar" |
122 testURLParsing(test, "https://user@example.com:8080/"); | 214 ); |
123 testURLParsing(test, "https://user@example.com:8080/foo"); | 215 testURLParsing( |
124 testURLParsing(test, "https://user@example.com:8080/foo/bar"); | 216 "http://[2001:db8:0:42:0:8a2e:370:7334]/foo/bar?https://random/foo/bar" |
125 testURLParsing( | 217 ); |
126 test, | 218 testURLParsing( |
127 "https://user@example.com:8080/foo/bar?https://random/foo/bar" | 219 "http://[2001:db8:0:42:0:8a2e:370:7334]:8080/foo/bar?https://random/foo/ba
r" |
128 ); | 220 ); |
129 | 221 testURLParsing( |
130 testURLParsing(test, "https://user:pass@example.com/"); | 222 "http://user@[2001:db8:0:42:0:8a2e:370:7334]:8080/foo/bar?https://random/f
oo/bar" |
131 testURLParsing(test, "https://user:pass@example.com/foo"); | 223 ); |
132 testURLParsing(test, "https://user:pass@example.com/foo/bar"); | 224 testURLParsing( |
133 testURLParsing( | 225 "http://user:pass@[2001:db8:0:42:0:8a2e:370:7334]:8080/foo/bar?https://ran
dom/foo/bar" |
134 test, | 226 ); |
135 "https://user:pass@example.com/foo/bar?https://random/foo/bar" | 227 |
136 ); | 228 testURLParsing("ftp://user:pass@example.com:8021/"); |
137 | 229 testURLParsing("ftp://user:pass@example.com:8021/foo"); |
138 testURLParsing(test, "https://user:pass@example.com:8080/"); | 230 testURLParsing("ftp://user:pass@example.com:8021/foo/bar"); |
139 testURLParsing(test, "https://user:pass@example.com:8080/foo"); | 231 |
140 testURLParsing(test, "https://user:pass@example.com:8080/foo/bar"); | 232 testURLParsing("about:blank"); |
141 testURLParsing( | 233 testURLParsing("chrome://extensions"); |
142 test, | 234 testURLParsing( |
143 "https://user:pass@example.com:8080/foo/bar?https://random/foo/bar" | 235 "chrome-extension://bhignfpcigccnlfapldlodmhlidjaion/options.html" |
144 ); | 236 ); |
145 | 237 testURLParsing("mailto:john.doe@mail.example.com"); |
146 testURLParsing(test, "https://us%40er:pa%40ss@example.com/"); | 238 |
147 testURLParsing(test, "https://us%40er:pa%40ss@example.com/foo"); | 239 testURLParsing("news:newsgroup"); |
148 testURLParsing(test, "https://us%40er:pa%40ss@example.com/foo/bar"); | 240 testURLParsing("news:message-id"); |
149 testURLParsing( | 241 testURLParsing("nntp://example.com:8119/newsgroup"); |
150 test, | 242 testURLParsing("nntp://example.com:8119/message-id"); |
151 "https://us%40er:pa%40ss@example.com/foo/bar?https://random/foo/bar" | 243 |
152 ); | 244 testURLParsing("data:,"); |
153 | 245 testURLParsing( |
154 testURLParsing(test, "https://us%40er:pa%40ss@example.com:8080/"); | 246 "data:text/vnd-example+xyz;foo=bar;base64,R0lGODdh" |
155 testURLParsing(test, "https://us%40er:pa%40ss@example.com:8080/foo"); | 247 ); |
156 testURLParsing(test, "https://us%40er:pa%40ss@example.com:8080/foo/bar"); | 248 testURLParsing( |
157 testURLParsing( | 249 "data:text/plain;charset=UTF-8;page=21,the%20data:1234,5678" |
158 test, | 250 ); |
159 "https://us%40er:pa%40ss@example.com:8080/foo/bar?https://random/foo/bar" | 251 |
160 ); | 252 testURLParsing("javascript:"); |
161 | 253 testURLParsing("javascript:alert();"); |
162 testURLParsing(test, "http://192.168.1.1/"); | 254 testURLParsing("javascript:foo/bar/"); |
163 testURLParsing(test, "http://192.168.1.1/foo"); | 255 testURLParsing("javascript://foo/bar/"); |
164 testURLParsing(test, "http://192.168.1.1/foo/bar"); | 256 |
165 testURLParsing( | 257 testURLParsing("file:///dev/random"); |
166 test, | 258 |
167 "http://192.168.1.1/foo/bar?https://random/foo/bar" | 259 testURLParsing("wss://example.com/"); |
168 ); | 260 testURLParsing("wss://example.com:8080/"); |
169 testURLParsing( | 261 testURLParsing("wss://user@example.com:8080/"); |
170 test, | 262 testURLParsing("wss://user:pass@example.com:8080/"); |
171 "http://192.168.1.1:8080/foo/bar?https://random/foo/bar" | 263 |
172 ); | 264 testURLParsing("stuns:stuns.example.com/"); |
173 testURLParsing( | 265 testURLParsing("stuns:stuns.example.com:8080/"); |
174 test, | 266 testURLParsing("stuns:user@stuns.example.com:8080/"); |
175 "http://user@192.168.1.1:8080/foo/bar?https://random/foo/bar" | 267 testURLParsing("stuns:user:pass@stuns.example.com:8080/"); |
176 ); | 268 |
177 testURLParsing( | 269 // The following tests are based on |
178 test, | 270 // https://cs.chromium.org/chromium/src/url/gurl_unittest.cc?rcl=9ec7bc85e0f
6a0bf28eff6b2eca678067da547e9 |
179 "http://user:pass@192.168.1.1:8080/foo/bar?https://random/foo/bar" | 271 // Note: We do not check for "canonicalization" (normalization). parseURL() |
180 ); | 272 // should be used with normalized URLs only. |
181 | 273 |
182 testURLParsing(test, "http://[2001:db8:0:42:0:8a2e:370:7334]/"); | 274 testURLParsing("something:///example.com/"); |
183 testURLParsing(test, "http://[2001:db8:0:42:0:8a2e:370:7334]/foo"); | 275 testURLParsing("something://example.com/"); |
184 testURLParsing( | 276 |
185 test, | 277 testURLParsing("file:///C:/foo.txt"); |
186 "http://[2001:db8:0:42:0:8a2e:370:7334]/foo/bar" | 278 testURLParsing("file://server/foo.txt"); |
187 ); | 279 |
188 testURLParsing( | 280 testURLParsing("http://user:pass@example.com:99/foo;bar?q=a#ref"); |
189 test, | 281 |
190 "http://[2001:db8:0:42:0:8a2e:370:7334]/foo/bar?https://random/foo/bar" | 282 testURLParsing("http://user:%40!$&'()*+,%3B%3D%3A@example.com:12345/"); |
191 ); | 283 |
192 testURLParsing( | 284 testURLParsing("filesystem:http://example.com/temporary/"); |
193 test, | 285 testURLParsing( |
194 "http://[2001:db8:0:42:0:8a2e:370:7334]:8080/foo/bar?https://random/foo/bar" | 286 "filesystem:http://user:%40!$&'()*+,%3B%3D%3A@example.com:12345/" |
195 ); | 287 ); |
196 testURLParsing( | 288 |
197 test, | 289 testURLParsing("javascript:window.alert('hello, world');"); |
198 "http://user@[2001:db8:0:42:0:8a2e:370:7334]:8080/foo/bar?https://random/foo
/bar" | 290 testURLParsing("javascript:#"); |
199 ); | 291 |
200 testURLParsing( | 292 testURLParsing( |
201 test, | 293 "blob:https://example.com/7ce70a1e-9681-4148-87a8-43cb9171b994" |
202 "http://user:pass@[2001:db8:0:42:0:8a2e:370:7334]:8080/foo/bar?https://rando
m/foo/bar" | 294 ); |
203 ); | 295 |
204 | 296 testURLParsing("http://[2001:db8::1]/"); |
205 testURLParsing(test, "ftp://user:pass@example.com:8021/"); | 297 testURLParsing("http://[2001:db8::1]:8080/"); |
206 testURLParsing(test, "ftp://user:pass@example.com:8021/foo"); | 298 testURLParsing("http://[::]:8080/"); |
207 testURLParsing(test, "ftp://user:pass@example.com:8021/foo/bar"); | 299 |
208 | 300 testURLParsing("not-a-standard-scheme:this is arbitrary content"); |
209 testURLParsing(test, "about:blank"); | 301 testURLParsing("view-source:http://example.com/path"); |
210 testURLParsing(test, "chrome://extensions"); | 302 |
211 testURLParsing( | 303 testURLParsing( |
212 test, | 304 "data:text/html,Question?%3Cdiv%20style=%22color:%20#bad%22%3Eidea%3C/div%
3E" |
213 "chrome-extension://bhignfpcigccnlfapldlodmhlidjaion/options.html" | 305 ); |
214 ); | 306 }); |
215 testURLParsing(test, "mailto:john.doe@mail.example.com"); | 307 |
216 | 308 it("Is Third Party", () => |
217 testURLParsing(test, "news:newsgroup"); | 309 { |
218 testURLParsing(test, "news:message-id"); | 310 function hostnameToURL(hostname) |
219 testURLParsing(test, "nntp://example.com:8119/newsgroup"); | |
220 testURLParsing(test, "nntp://example.com:8119/message-id"); | |
221 | |
222 testURLParsing(test, "data:,"); | |
223 testURLParsing( | |
224 test, | |
225 "data:text/vnd-example+xyz;foo=bar;base64,R0lGODdh" | |
226 ); | |
227 testURLParsing( | |
228 test, | |
229 "data:text/plain;charset=UTF-8;page=21,the%20data:1234,5678" | |
230 ); | |
231 | |
232 testURLParsing(test, "javascript:"); | |
233 testURLParsing(test, "javascript:alert();"); | |
234 testURLParsing(test, "javascript:foo/bar/"); | |
235 testURLParsing(test, "javascript://foo/bar/"); | |
236 | |
237 testURLParsing(test, "file:///dev/random"); | |
238 | |
239 testURLParsing(test, "wss://example.com/"); | |
240 testURLParsing(test, "wss://example.com:8080/"); | |
241 testURLParsing(test, "wss://user@example.com:8080/"); | |
242 testURLParsing(test, "wss://user:pass@example.com:8080/"); | |
243 | |
244 testURLParsing(test, "stuns:stuns.example.com/"); | |
245 testURLParsing(test, "stuns:stuns.example.com:8080/"); | |
246 testURLParsing(test, "stuns:user@stuns.example.com:8080/"); | |
247 testURLParsing(test, "stuns:user:pass@stuns.example.com:8080/"); | |
248 | |
249 // The following tests are based on | |
250 // https://cs.chromium.org/chromium/src/url/gurl_unittest.cc?rcl=9ec7bc85e0f6a
0bf28eff6b2eca678067da547e9 | |
251 // Note: We do not check for "canonicalization" (normalization). parseURL() | |
252 // should be used with normalized URLs only. | |
253 | |
254 testURLParsing(test, "something:///example.com/"); | |
255 testURLParsing(test, "something://example.com/"); | |
256 | |
257 testURLParsing(test, "file:///C:/foo.txt"); | |
258 testURLParsing(test, "file://server/foo.txt"); | |
259 | |
260 testURLParsing(test, "http://user:pass@example.com:99/foo;bar?q=a#ref"); | |
261 | |
262 testURLParsing(test, "http://user:%40!$&'()*+,%3B%3D%3A@example.com:12345/"); | |
263 | |
264 testURLParsing(test, "filesystem:http://example.com/temporary/"); | |
265 testURLParsing( | |
266 test, | |
267 "filesystem:http://user:%40!$&'()*+,%3B%3D%3A@example.com:12345/" | |
268 ); | |
269 | |
270 testURLParsing(test, "javascript:window.alert('hello, world');"); | |
271 testURLParsing(test, "javascript:#"); | |
272 | |
273 testURLParsing( | |
274 test, | |
275 "blob:https://example.com/7ce70a1e-9681-4148-87a8-43cb9171b994" | |
276 ); | |
277 | |
278 testURLParsing(test, "http://[2001:db8::1]/"); | |
279 testURLParsing(test, "http://[2001:db8::1]:8080/"); | |
280 testURLParsing(test, "http://[::]:8080/"); | |
281 | |
282 testURLParsing(test, "not-a-standard-scheme:this is arbitrary content"); | |
283 testURLParsing(test, "view-source:http://example.com/path"); | |
284 | |
285 testURLParsing( | |
286 test, | |
287 "data:text/html,Question?%3Cdiv%20style=%22color:%20#bad%22%3Eidea%3C/div%3E
" | |
288 ); | |
289 | |
290 test.done(); | |
291 }; | |
292 | |
293 exports.testNormalizeHostname = function(test) | |
294 { | |
295 test.equal(normalizeHostname("example.com"), "example.com"); | |
296 test.equal(normalizeHostname("example.com."), "example.com"); | |
297 test.equal(normalizeHostname("example.com.."), "example.com"); | |
298 test.equal(normalizeHostname("example.com..."), "example.com"); | |
299 | |
300 test.equal(normalizeHostname("Example.com"), "example.com"); | |
301 test.equal(normalizeHostname("ExaMple.Com"), "example.com"); | |
302 test.equal(normalizeHostname("ExaMple.Com.."), "example.com"); | |
303 | |
304 test.equal(normalizeHostname("192.168.1.1"), "192.168.1.1"); | |
305 test.equal(normalizeHostname("192.168.1.1."), "192.168.1.1"); | |
306 | |
307 test.equal(normalizeHostname("2001:0db8:85a3:0000:0000:8a2e:0370:7334"), | |
308 "2001:0db8:85a3:0000:0000:8a2e:0370:7334"); | |
309 test.equal(normalizeHostname("2001:0db8:85a3:0000:0000:8a2e:0370:7334."), | |
310 "2001:0db8:85a3:0000:0000:8a2e:0370:7334"); | |
311 test.equal(normalizeHostname("2001:0DB8:85A3:0000:0000:8A2E:0370:7334"), | |
312 "2001:0db8:85a3:0000:0000:8a2e:0370:7334"); | |
313 | |
314 test.done(); | |
315 }; | |
316 | |
317 exports.testDomainSuffixes = function(test) | |
318 { | |
319 test.deepEqual([...domainSuffixes("localhost")], ["localhost"]); | |
320 test.deepEqual([...domainSuffixes("example.com")], ["example.com", "com"]); | |
321 test.deepEqual([...domainSuffixes("www.example.com")], | |
322 ["www.example.com", "example.com", "com"]); | |
323 test.deepEqual([...domainSuffixes("www.example.co.in")], | |
324 ["www.example.co.in", "example.co.in", "co.in", "in"]); | |
325 | |
326 // With blank. | |
327 test.deepEqual([...domainSuffixes("localhost", true)], ["localhost", ""]); | |
328 test.deepEqual([...domainSuffixes("example.com", true)], | |
329 ["example.com", "com", ""]); | |
330 test.deepEqual([...domainSuffixes("www.example.com", true)], | |
331 ["www.example.com", "example.com", "com", ""]); | |
332 test.deepEqual([...domainSuffixes("www.example.co.in", true)], | |
333 ["www.example.co.in", "example.co.in", "co.in", "in", ""]); | |
334 | |
335 // Quirks and edge cases. | |
336 test.deepEqual([...domainSuffixes("")], []); | |
337 test.deepEqual([...domainSuffixes(".")], ["."]); | |
338 test.deepEqual([...domainSuffixes(".localhost")], | |
339 [".localhost", "localhost"]); | |
340 test.deepEqual([...domainSuffixes(".example.com")], | |
341 [".example.com", "example.com", "com"]); | |
342 test.deepEqual([...domainSuffixes("localhost.")], | |
343 ["localhost."]); | |
344 test.deepEqual([...domainSuffixes("example.com.")], | |
345 ["example.com.", "com."]); | |
346 test.deepEqual([...domainSuffixes("..localhost")], | |
347 ["..localhost", ".localhost", "localhost"]); | |
348 test.deepEqual( | |
349 [...domainSuffixes("..example..com")], | |
350 ["..example..com", ".example..com", "example..com", ".com", "com"] | |
351 ); | |
352 test.deepEqual([...domainSuffixes("localhost..")], ["localhost..", "."]); | |
353 test.deepEqual([...domainSuffixes("example..com..")], | |
354 ["example..com..", ".com..", "com..", "."]); | |
355 | |
356 test.done(); | |
357 }; | |
358 | |
359 exports.testIsThirdParty = function(test) | |
360 { | |
361 testThirdParty(test, "foo", "foo", false, "same domain isn't third-party"); | |
362 testThirdParty(test, "foo", "bar", true, "different domain is third-party"); | |
363 testThirdParty(test, "foo.com", "foo.com", false, | |
364 "same domain with TLD (.com) isn't third-party"); | |
365 testThirdParty(test, "foo.com", "bar.com", true, | |
366 "same TLD (.com) but different domain is third-party"); | |
367 testThirdParty(test, "foo.com", "www.foo.com", false, | |
368 "same domain but differend subdomain isn't third-party"); | |
369 testThirdParty(test, "foo.example.com", "bar.example.com", false, | |
370 "same basedomain (example.com) isn't third-party"); | |
371 testThirdParty(test, "foo.uk", "bar.uk", true, | |
372 "same TLD (.uk) but different domain is third-party"); | |
373 testThirdParty(test, "foo.co.uk", "bar.co.uk", true, | |
374 "same TLD (.co.uk) but different domain is third-party"); | |
375 testThirdParty(test, "foo.example.co.uk", "bar.example.co.uk", false, | |
376 "same basedomain (example.co.uk) isn't third-party"); | |
377 testThirdParty(test, "1.2.3.4", "1.2.3.4", false, | |
378 "same IPv4 address isn't third-party"); | |
379 testThirdParty(test, "1.1.1.1", "2.1.1.1", true, | |
380 "different IPv4 address is third-party"); | |
381 testThirdParty(test, "0x01ff0101", "0x01ff0101", false, | |
382 "same IPv4 hexadecimal address isn't third-party"); | |
383 testThirdParty(test, "0x01ff0101", "0x01ff0102", true, | |
384 "different IPv4 hexadecimal address is third-party"); | |
385 testThirdParty( | |
386 test, | |
387 "1.0xff.3.4", "1.0xff.3.4", false, | |
388 "same IPv4 address with hexadecimal octet isn't third-party" | |
389 ); | |
390 testThirdParty( | |
391 test, | |
392 "1.0xff.1.1", "2.0xff.1.1", true, | |
393 "different IPv4 address with hexadecimal octet is third-party" | |
394 ); | |
395 testThirdParty( | |
396 test, | |
397 "0xff.example.com", "example.com", false, | |
398 "domain starts like a hexadecimal IPv4 address but isn't one" | |
399 ); | |
400 testThirdParty( | |
401 test, | |
402 "[2001:db8:85a3::8a2e:370:7334]", "[2001:db8:85a3::8a2e:370:7334]", false, | |
403 "same IPv6 address isn't third-party" | |
404 ); | |
405 testThirdParty( | |
406 test, | |
407 "[2001:db8:85a3::8a2e:370:7334]", "[5001:db8:85a3::8a2e:370:7334]", true, | |
408 "different IPv6 address is third-party" | |
409 ); | |
410 testThirdParty( | |
411 test, | |
412 "[::ffff:192.0.2.128]", "[::ffff:192.0.2.128]", false, | |
413 "same IPv4-mapped IPv6 address isn't third-party" | |
414 ); | |
415 testThirdParty( | |
416 test, | |
417 "[::ffff:192.0.2.128]", "[::ffff:192.1.2.128]", true, | |
418 "different IPv4-mapped IPv6 address is third-party" | |
419 ); | |
420 testThirdParty(test, "xn--f-1gaa.com", "f\u00f6\u00f6.com", false, | |
421 "same IDN isn't third-party"); | |
422 testThirdParty(test, "example.com..", "example.com....", false, | |
423 "traling dots are ignored"); | |
424 | |
425 test.done(); | |
426 }; | |
427 | |
428 exports.testGetBaseDomain = function(test) | |
429 { | |
430 let parts = ["aaa", "bbb", "ccc", "ddd", "eee"]; | |
431 let levels = 3; | |
432 | |
433 for (let suffix in publicSuffixes) | |
434 { | |
435 let offset = publicSuffixes[suffix]; | |
436 | |
437 // If this fails, add more parts. | |
438 test.ok(offset <= parts.length - levels, | |
439 "Not enough domain parts for testing"); | |
440 | |
441 for (let i = 0; i < offset + levels; i++) | |
442 { | 311 { |
443 let hostname = parts.slice(0, i).join("."); | 312 return new URL("http://" + hostname); |
444 hostname += (hostname ? "." : "") + suffix; | |
445 | |
446 let expected = parts.slice(Math.max(0, i - offset), i).join("."); | |
447 expected += (expected ? "." : "") + suffix; | |
448 | |
449 test.equal(getBaseDomain(hostname), expected, | |
450 `getBaseDomain("${hostname}") == "${expected}"` + | |
451 ` with {suffix: "${suffix}", offset: ${offset}}`); | |
452 } | 313 } |
453 } | 314 |
454 | 315 function testThirdParty(requestHostname, documentHostname, expected, |
455 // Unknown suffixes. | 316 message) |
456 test.equal(typeof publicSuffixes["localhost"], "undefined"); | 317 { |
457 test.equal(typeof publicSuffixes["localhost.localdomain"], "undefined"); | 318 assert.equal( |
458 | 319 isThirdParty( |
459 test.equal(getBaseDomain("localhost"), "localhost"); | 320 hostnameToURL(requestHostname).hostname, |
460 test.equal(getBaseDomain("localhost.localdomain"), "localhost.localdomain"); | 321 |
461 test.equal( | 322 // Chrome's URL object normalizes IP addresses. So some test |
462 getBaseDomain("mail.localhost.localdomain"), | 323 // will fail if we don't normalize the document host as well. |
463 "localhost.localdomain" | 324 hostnameToURL(documentHostname).hostname |
464 ); | 325 ), |
465 test.equal(getBaseDomain("www.example.localhost.localdomain"), | 326 expected, |
466 "localhost.localdomain"); | 327 message |
467 | 328 ); |
468 // Unknown suffixes that overlap partly with known suffixes. | 329 } |
469 test.equal(typeof publicSuffixes["example.com"], "undefined"); | 330 |
470 test.equal(typeof publicSuffixes["africa.com"], "number"); | 331 testThirdParty("foo", "foo", false, "same domain isn't third-party"); |
471 test.equal(typeof publicSuffixes["compute.amazonaws.com"], "number"); | 332 testThirdParty("foo", "bar", true, "different domain is third-party"); |
472 | 333 testThirdParty("foo.com", "foo.com", false, |
473 test.equal(getBaseDomain("example.com"), "example.com"); | 334 "same domain with TLD (.com) isn't third-party"); |
474 test.equal(getBaseDomain("mail.example.com"), "example.com"); | 335 testThirdParty("foo.com", "bar.com", true, |
475 test.equal(getBaseDomain("secure.mail.example.com"), "example.com"); | 336 "same TLD (.com) but different domain is third-party"); |
476 | 337 testThirdParty("foo.com", "www.foo.com", false, |
477 // Cascading offsets. | 338 "same domain but differend subdomain isn't third-party"); |
478 | 339 testThirdParty("foo.example.com", "bar.example.com", false, |
479 // If these sanity checks fail, look for other examles of cascading offsets | 340 "same basedomain (example.com) isn't third-party"); |
480 // from the public suffix list. | 341 testThirdParty("foo.uk", "bar.uk", true, |
481 test.equal( | 342 "same TLD (.uk) but different domain is third-party"); |
482 typeof publicSuffixes[ | 343 testThirdParty("foo.co.uk", "bar.co.uk", true, |
483 "images.example.s3.dualstack.us-east-1.amazonaws.com" | 344 "same TLD (.co.uk) but different domain is third-party"); |
484 ], | 345 testThirdParty("foo.example.co.uk", "bar.example.co.uk", false, |
485 "undefined" | 346 "same basedomain (example.co.uk) isn't third-party"); |
486 ); | 347 testThirdParty("1.2.3.4", "1.2.3.4", false, |
487 test.equal( | 348 "same IPv4 address isn't third-party"); |
488 typeof publicSuffixes["example.s3.dualstack.us-east-1.amazonaws.com"], | 349 testThirdParty("1.1.1.1", "2.1.1.1", true, |
489 "undefined" | 350 "different IPv4 address is third-party"); |
490 ); | 351 testThirdParty("0x01ff0101", "0x01ff0101", false, |
491 test.equal(publicSuffixes["s3.dualstack.us-east-1.amazonaws.com"], 1); | 352 "same IPv4 hexadecimal address isn't third-party"); |
492 test.equal(typeof publicSuffixes["dualstack.us-east-1.amazonaws.com"], | 353 testThirdParty("0x01ff0101", "0x01ff0102", true, |
493 "undefined"); | 354 "different IPv4 hexadecimal address is third-party"); |
494 test.equal(typeof publicSuffixes["example.us-east-1.amazonaws.com"], | 355 testThirdParty( |
495 "undefined"); | 356 "1.0xff.3.4", "1.0xff.3.4", false, |
496 test.equal(publicSuffixes["us-east-1.amazonaws.com"], 1); | 357 "same IPv4 address with hexadecimal octet isn't third-party" |
497 test.equal(typeof publicSuffixes["example.amazonaws.com"], "undefined"); | 358 ); |
498 test.equal(typeof publicSuffixes["amazonaws.com"], "undefined"); | 359 testThirdParty( |
499 | 360 "1.0xff.1.1", "2.0xff.1.1", true, |
500 test.equal( | 361 "different IPv4 address with hexadecimal octet is third-party" |
501 getBaseDomain("images.example.s3.dualstack.us-east-1.amazonaws.com"), | 362 ); |
502 "example.s3.dualstack.us-east-1.amazonaws.com" | 363 testThirdParty( |
503 ); | 364 "0xff.example.com", "example.com", false, |
504 test.equal(getBaseDomain("example.s3.dualstack.us-east-1.amazonaws.com"), | 365 "domain starts like a hexadecimal IPv4 address but isn't one" |
505 "example.s3.dualstack.us-east-1.amazonaws.com"); | 366 ); |
506 test.equal(getBaseDomain("s3.dualstack.us-east-1.amazonaws.com"), | 367 testThirdParty( |
507 "s3.dualstack.us-east-1.amazonaws.com"); | 368 "[2001:db8:85a3::8a2e:370:7334]", "[2001:db8:85a3::8a2e:370:7334]", false, |
508 test.equal(getBaseDomain("dualstack.us-east-1.amazonaws.com"), | 369 "same IPv6 address isn't third-party" |
509 "dualstack.us-east-1.amazonaws.com"); | 370 ); |
510 test.equal(getBaseDomain("example.us-east-1.amazonaws.com"), | 371 testThirdParty( |
511 "example.us-east-1.amazonaws.com"); | 372 "[2001:db8:85a3::8a2e:370:7334]", "[5001:db8:85a3::8a2e:370:7334]", true, |
512 test.equal( | 373 "different IPv6 address is third-party" |
513 getBaseDomain("us-east-1.amazonaws.com"), | 374 ); |
514 "us-east-1.amazonaws.com" | 375 testThirdParty( |
515 ); | 376 "[::ffff:192.0.2.128]", "[::ffff:192.0.2.128]", false, |
516 test.equal(getBaseDomain("example.amazonaws.com"), "amazonaws.com"); | 377 "same IPv4-mapped IPv6 address isn't third-party" |
517 test.equal(getBaseDomain("amazonaws.com"), "amazonaws.com"); | 378 ); |
518 | 379 testThirdParty( |
519 // Edge case. | 380 "[::ffff:192.0.2.128]", "[::ffff:192.1.2.128]", true, |
520 test.equal(getBaseDomain(""), ""); | 381 "different IPv4-mapped IPv6 address is third-party" |
521 | 382 ); |
522 test.done(); | 383 testThirdParty("xn--f-1gaa.com", "f\u00f6\u00f6.com", false, |
523 }; | 384 "same IDN isn't third-party"); |
| 385 testThirdParty("example.com..", "example.com....", false, |
| 386 "traling dots are ignored"); |
| 387 }); |
| 388 |
| 389 it("Get Base Domain", () => |
| 390 { |
| 391 let parts = ["aaa", "bbb", "ccc", "ddd", "eee"]; |
| 392 let levels = 3; |
| 393 |
| 394 for (let suffix in publicSuffixes) |
| 395 { |
| 396 let offset = publicSuffixes[suffix]; |
| 397 |
| 398 // If this fails, add more parts. |
| 399 assert.ok(offset <= parts.length - levels, |
| 400 "Not enough domain parts for testing"); |
| 401 |
| 402 for (let i = 0; i < offset + levels; i++) |
| 403 { |
| 404 let hostname = parts.slice(0, i).join("."); |
| 405 hostname += (hostname ? "." : "") + suffix; |
| 406 |
| 407 let expected = parts.slice(Math.max(0, i - offset), i).join("."); |
| 408 expected += (expected ? "." : "") + suffix; |
| 409 |
| 410 assert.equal(getBaseDomain(hostname), expected, |
| 411 `getBaseDomain("${hostname}") == "${expected}"` + |
| 412 ` with {suffix: "${suffix}", offset: ${offset}}`); |
| 413 } |
| 414 } |
| 415 |
| 416 // Unknown suffixes. |
| 417 assert.equal(typeof publicSuffixes["localhost"], "undefined"); |
| 418 assert.equal(typeof publicSuffixes["localhost.localdomain"], "undefined"); |
| 419 |
| 420 assert.equal(getBaseDomain("localhost"), "localhost"); |
| 421 assert.equal(getBaseDomain("localhost.localdomain"), "localhost.localdomain"
); |
| 422 assert.equal( |
| 423 getBaseDomain("mail.localhost.localdomain"), |
| 424 "localhost.localdomain" |
| 425 ); |
| 426 assert.equal(getBaseDomain("www.example.localhost.localdomain"), |
| 427 "localhost.localdomain"); |
| 428 |
| 429 // Unknown suffixes that overlap partly with known suffixes. |
| 430 assert.equal(typeof publicSuffixes["example.com"], "undefined"); |
| 431 assert.equal(typeof publicSuffixes["africa.com"], "number"); |
| 432 assert.equal(typeof publicSuffixes["compute.amazonaws.com"], "number"); |
| 433 |
| 434 assert.equal(getBaseDomain("example.com"), "example.com"); |
| 435 assert.equal(getBaseDomain("mail.example.com"), "example.com"); |
| 436 assert.equal(getBaseDomain("secure.mail.example.com"), "example.com"); |
| 437 |
| 438 // Cascading offsets. |
| 439 |
| 440 // If these sanity checks fail, look for other examles of cascading offsets |
| 441 // from the public suffix list. |
| 442 assert.equal( |
| 443 typeof publicSuffixes[ |
| 444 "images.example.s3.dualstack.us-east-1.amazonaws.com" |
| 445 ], |
| 446 "undefined" |
| 447 ); |
| 448 assert.equal( |
| 449 typeof publicSuffixes["example.s3.dualstack.us-east-1.amazonaws.com"], |
| 450 "undefined" |
| 451 ); |
| 452 assert.equal(publicSuffixes["s3.dualstack.us-east-1.amazonaws.com"], 1); |
| 453 assert.equal(typeof publicSuffixes["dualstack.us-east-1.amazonaws.com"], |
| 454 "undefined"); |
| 455 assert.equal(typeof publicSuffixes["example.us-east-1.amazonaws.com"], |
| 456 "undefined"); |
| 457 assert.equal(publicSuffixes["us-east-1.amazonaws.com"], 1); |
| 458 assert.equal(typeof publicSuffixes["example.amazonaws.com"], "undefined"); |
| 459 assert.equal(typeof publicSuffixes["amazonaws.com"], "undefined"); |
| 460 |
| 461 assert.equal( |
| 462 getBaseDomain("images.example.s3.dualstack.us-east-1.amazonaws.com"), |
| 463 "example.s3.dualstack.us-east-1.amazonaws.com" |
| 464 ); |
| 465 assert.equal(getBaseDomain("example.s3.dualstack.us-east-1.amazonaws.com"), |
| 466 "example.s3.dualstack.us-east-1.amazonaws.com"); |
| 467 assert.equal(getBaseDomain("s3.dualstack.us-east-1.amazonaws.com"), |
| 468 "s3.dualstack.us-east-1.amazonaws.com"); |
| 469 assert.equal(getBaseDomain("dualstack.us-east-1.amazonaws.com"), |
| 470 "dualstack.us-east-1.amazonaws.com"); |
| 471 assert.equal(getBaseDomain("example.us-east-1.amazonaws.com"), |
| 472 "example.us-east-1.amazonaws.com"); |
| 473 assert.equal( |
| 474 getBaseDomain("us-east-1.amazonaws.com"), |
| 475 "us-east-1.amazonaws.com" |
| 476 ); |
| 477 assert.equal(getBaseDomain("example.amazonaws.com"), "amazonaws.com"); |
| 478 assert.equal(getBaseDomain("amazonaws.com"), "amazonaws.com"); |
| 479 |
| 480 // Edge case. |
| 481 assert.equal(getBaseDomain(""), ""); |
| 482 }); |
| 483 }); |
| 484 |
OLD | NEW |