| 301 | } |
| 302 | |
| 303 | function check(hostParts, pattern, wildcards) { |
| 304 | // Empty strings, null, undefined, etc. never match. |
| 305 | if (!pattern) |
| 306 | return false; |
| 307 | |
| 308 | const patternParts = splitHost(pattern); |
| 309 | |
| 310 | if (hostParts.length !== patternParts.length) |
| 311 | return false; |
| 312 | |
| 313 | // Pattern has empty components, e.g. "bad..example.com". |
| 314 | if (patternParts.includes('')) |
| 315 | return false; |
| 316 | |
| 317 | // RFC 6125 allows IDNA U-labels (Unicode) in names but we have no |
| 318 | // good way to detect their encoding or normalize them so we simply |
| 319 | // reject them. Control characters and blanks are rejected as well |
| 320 | // because nothing good can come from accepting them. |
| 321 | const isBad = (s) => /[^\u0021-\u007F]/u.test(s); |
| 322 | if (patternParts.some(isBad)) |
| 323 | return false; |
| 324 | |
| 325 | // Check host parts from right to left first. |
| 326 | for (let i = hostParts.length - 1; i > 0; i -= 1) { |
| 327 | if (hostParts[i] !== patternParts[i]) |
| 328 | return false; |
| 329 | } |
| 330 | |
| 331 | const hostSubdomain = hostParts[0]; |
| 332 | const patternSubdomain = patternParts[0]; |
| 333 | const patternSubdomainParts = patternSubdomain.split('*', 3); |
| 334 | |
| 335 | // Short-circuit when the subdomain does not contain a wildcard. |
| 336 | // RFC 6125 does not allow wildcard substitution for components |
| 337 | // containing IDNA A-labels (Punycode) so match those verbatim. |
| 338 | if (patternSubdomainParts.length === 1 || |
| 339 | patternSubdomain.includes('xn--')) |
| 340 | return hostSubdomain === patternSubdomain; |
| 341 | |
| 342 | if (!wildcards) |
| 343 | return false; |
| 344 | |
| 345 | // More than one wildcard is always wrong. |
| 346 | if (patternSubdomainParts.length > 2) |
| 347 | return false; |
| 348 | |
| 349 | // *.tld wildcards are not allowed. |
| 350 | if (patternParts.length <= 2) |
| 351 | return false; |
| 352 | |
| 353 | const { 0: prefix, 1: suffix } = patternSubdomainParts; |
| 354 | |
| 355 | if (prefix.length + suffix.length > hostSubdomain.length) |
| 356 | return false; |
| 357 | |
| 358 | if (!hostSubdomain.startsWith(prefix)) |
| 359 | return false; |
| 360 | |