| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100 |
- import { IOptions } from './options';
- /**
- * Check if `vhost` is a valid suffix of `hostname` (top-domain)
- *
- * It means that `vhost` needs to be a suffix of `hostname` and we then need to
- * make sure that: either they are equal, or the character preceding `vhost` in
- * `hostname` is a '.' (it should not be a partial label).
- *
- * * hostname = 'not.evil.com' and vhost = 'vil.com' => not ok
- * * hostname = 'not.evil.com' and vhost = 'evil.com' => ok
- * * hostname = 'not.evil.com' and vhost = 'not.evil.com' => ok
- */
- function shareSameDomainSuffix(hostname: string, vhost: string): boolean {
- if (hostname.endsWith(vhost)) {
- return (
- hostname.length === vhost.length ||
- hostname[hostname.length - vhost.length - 1] === '.'
- );
- }
- return false;
- }
- /**
- * Given a hostname and its public suffix, extract the general domain.
- */
- function extractDomainWithSuffix(
- hostname: string,
- publicSuffix: string,
- ): string {
- // Locate the index of the last '.' in the part of the `hostname` preceding
- // the public suffix.
- //
- // examples:
- // 1. not.evil.co.uk => evil.co.uk
- // ^ ^
- // | | start of public suffix
- // | index of the last dot
- //
- // 2. example.co.uk => example.co.uk
- // ^ ^
- // | | start of public suffix
- // |
- // | (-1) no dot found before the public suffix
- const publicSuffixIndex = hostname.length - publicSuffix.length - 2;
- const lastDotBeforeSuffixIndex = hostname.lastIndexOf('.', publicSuffixIndex);
- // No '.' found, then `hostname` is the general domain (no sub-domain)
- if (lastDotBeforeSuffixIndex === -1) {
- return hostname;
- }
- // Extract the part between the last '.'
- return hostname.slice(lastDotBeforeSuffixIndex + 1);
- }
- /**
- * Detects the domain based on rules and upon and a host string
- */
- export default function getDomain(
- suffix: string,
- hostname: string,
- options: IOptions,
- ): string | null {
- // Check if `hostname` ends with a member of `validHosts`.
- if (options.validHosts !== null) {
- const validHosts = options.validHosts;
- for (const vhost of validHosts) {
- if (/*@__INLINE__*/ shareSameDomainSuffix(hostname, vhost)) {
- return vhost;
- }
- }
- }
- let numberOfLeadingDots = 0;
- if (hostname.startsWith('.')) {
- while (
- numberOfLeadingDots < hostname.length &&
- hostname[numberOfLeadingDots] === '.'
- ) {
- numberOfLeadingDots += 1;
- }
- }
- // If `hostname` is a valid public suffix, then there is no domain to return.
- // Since we already know that `getPublicSuffix` returns a suffix of `hostname`
- // there is no need to perform a string comparison and we only compare the
- // size.
- if (suffix.length === hostname.length - numberOfLeadingDots) {
- return null;
- }
- // To extract the general domain, we start by identifying the public suffix
- // (if any), then consider the domain to be the public suffix with one added
- // level of depth. (e.g.: if hostname is `not.evil.co.uk` and public suffix:
- // `co.uk`, then we take one more level: `evil`, giving the final result:
- // `evil.co.uk`).
- return /*@__INLINE__*/ extractDomainWithSuffix(hostname, suffix);
- }
|