domain.js 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. /**
  2. * Check if `vhost` is a valid suffix of `hostname` (top-domain)
  3. *
  4. * It means that `vhost` needs to be a suffix of `hostname` and we then need to
  5. * make sure that: either they are equal, or the character preceding `vhost` in
  6. * `hostname` is a '.' (it should not be a partial label).
  7. *
  8. * * hostname = 'not.evil.com' and vhost = 'vil.com' => not ok
  9. * * hostname = 'not.evil.com' and vhost = 'evil.com' => ok
  10. * * hostname = 'not.evil.com' and vhost = 'not.evil.com' => ok
  11. */
  12. function shareSameDomainSuffix(hostname, vhost) {
  13. if (hostname.endsWith(vhost)) {
  14. return (hostname.length === vhost.length ||
  15. hostname[hostname.length - vhost.length - 1] === '.');
  16. }
  17. return false;
  18. }
  19. /**
  20. * Given a hostname and its public suffix, extract the general domain.
  21. */
  22. function extractDomainWithSuffix(hostname, publicSuffix) {
  23. // Locate the index of the last '.' in the part of the `hostname` preceding
  24. // the public suffix.
  25. //
  26. // examples:
  27. // 1. not.evil.co.uk => evil.co.uk
  28. // ^ ^
  29. // | | start of public suffix
  30. // | index of the last dot
  31. //
  32. // 2. example.co.uk => example.co.uk
  33. // ^ ^
  34. // | | start of public suffix
  35. // |
  36. // | (-1) no dot found before the public suffix
  37. const publicSuffixIndex = hostname.length - publicSuffix.length - 2;
  38. const lastDotBeforeSuffixIndex = hostname.lastIndexOf('.', publicSuffixIndex);
  39. // No '.' found, then `hostname` is the general domain (no sub-domain)
  40. if (lastDotBeforeSuffixIndex === -1) {
  41. return hostname;
  42. }
  43. // Extract the part between the last '.'
  44. return hostname.slice(lastDotBeforeSuffixIndex + 1);
  45. }
  46. /**
  47. * Detects the domain based on rules and upon and a host string
  48. */
  49. export default function getDomain(suffix, hostname, options) {
  50. // Check if `hostname` ends with a member of `validHosts`.
  51. if (options.validHosts !== null) {
  52. const validHosts = options.validHosts;
  53. for (const vhost of validHosts) {
  54. if ( /*@__INLINE__*/shareSameDomainSuffix(hostname, vhost)) {
  55. return vhost;
  56. }
  57. }
  58. }
  59. let numberOfLeadingDots = 0;
  60. if (hostname.startsWith('.')) {
  61. while (numberOfLeadingDots < hostname.length &&
  62. hostname[numberOfLeadingDots] === '.') {
  63. numberOfLeadingDots += 1;
  64. }
  65. }
  66. // If `hostname` is a valid public suffix, then there is no domain to return.
  67. // Since we already know that `getPublicSuffix` returns a suffix of `hostname`
  68. // there is no need to perform a string comparison and we only compare the
  69. // size.
  70. if (suffix.length === hostname.length - numberOfLeadingDots) {
  71. return null;
  72. }
  73. // To extract the general domain, we start by identifying the public suffix
  74. // (if any), then consider the domain to be the public suffix with one added
  75. // level of depth. (e.g.: if hostname is `not.evil.co.uk` and public suffix:
  76. // `co.uk`, then we take one more level: `evil`, giving the final result:
  77. // `evil.co.uk`).
  78. return /*@__INLINE__*/ extractDomainWithSuffix(hostname, suffix);
  79. }
  80. //# sourceMappingURL=domain.js.map