-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Address PR comments and increase test coverage.
- Loading branch information
Showing
12 changed files
with
377 additions
and
135 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,51 +1,122 @@ | ||
"use strict"; | ||
|
||
var isValid = require('./is-valid.js'); | ||
var cleanHostValue = require('./clean-host.js'); | ||
var getPublicSuffix = require('./public-suffix.js'); | ||
|
||
|
||
/** | ||
* Polyfill for `endsWith` | ||
* | ||
* @param {string} str | ||
* @param {string} pattern | ||
* @return {boolean} | ||
*/ | ||
function endsWith(str, pattern) { | ||
return ( | ||
str.lastIndexOf(pattern) === (str.length - pattern.length) | ||
); | ||
} | ||
|
||
|
||
/** | ||
* 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 | ||
* | ||
* @param {string} hostname | ||
* @param {string} vhost | ||
* @return {boolean} | ||
*/ | ||
function shareSameDomainSuffix(hostname, vhost) { | ||
if (endsWith(hostname, 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. | ||
* | ||
* @param {string} hostname | ||
* @param {string} publicSuffix | ||
* @return {string} | ||
*/ | ||
function extractDomainWithSuffix(hostname, publicSuffix) { | ||
// 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 | ||
var publicSuffixIndex = hostname.length - publicSuffix.length - 2; | ||
var 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.substr(lastDotBeforeSuffixIndex + 1); | ||
} | ||
|
||
|
||
/** | ||
* Detects the domain based on rules and upon and a host string | ||
* | ||
* @api | ||
* @param {string} host | ||
* @return {String} | ||
*/ | ||
module.exports = function getDomain(allRules, validHosts, host, isHostClean) { | ||
var _validHosts = validHosts || []; | ||
var cleanHost = cleanHostValue(host, isHostClean); | ||
module.exports = function getDomain(rules, validHosts, hostname) { | ||
hostname = cleanHostValue(hostname); | ||
|
||
if (isValid(_validHosts, cleanHost) === false) { | ||
if (isValid(validHosts, hostname) === false) { | ||
return null; | ||
} | ||
|
||
// Check if `host` ends with '.' followed by one host specified in validHosts. | ||
for (var i = 0; i < validHosts.length; i++) { | ||
// Check if `hostname` ends with a member of `validHosts`. | ||
for (var i = 0; i < validHosts.length; i += 1) { | ||
var vhost = validHosts[i]; | ||
if (cleanHost.indexOf(vhost) === (cleanHost.length - vhost.length) && ( | ||
cleanHost.length === vhost.length || | ||
cleanHost[cleanHost.length - vhost.length - 1] === '.')) { | ||
if (shareSameDomainSuffix(hostname, vhost)) { | ||
return vhost; | ||
} | ||
} | ||
|
||
var suffix = getPublicSuffix(allRules, cleanHost, true); | ||
if (suffix === null) { | ||
// TODO - shouldn't it be null? | ||
// Otherwise 'should return the known valid host' fails | ||
// return cleanHost; | ||
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`). | ||
var suffix = getPublicSuffix(rules, hostname); | ||
|
||
if (suffix.length === cleanHost.length) { | ||
// 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) { | ||
return null; | ||
} | ||
|
||
// google.fr (length 9) | ||
// suffix = fr (length 2) | ||
// 5 = 9 - 2 - 1 (ignore the dot) - 1 (zero-based indexing) | ||
var lastDotBeforeSuffixIndex = cleanHost.lastIndexOf('.', cleanHost.length - suffix.length - 2); | ||
if (lastDotBeforeSuffixIndex === -1) { | ||
return cleanHost; | ||
} | ||
|
||
return cleanHost.substring(lastDotBeforeSuffixIndex + 1); | ||
return extractDomainWithSuffix(hostname, suffix); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,26 @@ | ||
"use strict"; | ||
|
||
var cleanHostValue = require('./clean-host.js'); | ||
var getDomain = require('./domain.js'); | ||
|
||
|
||
/** | ||
* Returns the subdomain of a host string | ||
* Returns the subdomain of a hostname string | ||
* | ||
* @api | ||
* @param {string} host | ||
* @return {string|null} a subdomain string if any, blank string if subdomain is empty, otherwise null | ||
* @param {string} hostname | ||
* @return {string|null} a subdomain string if any, blank string if subdomain | ||
* is empty, otherwise null. | ||
*/ | ||
module.exports = function getSubdomain(allRules, validHosts, host, isHostClean) { | ||
var cleanHost = cleanHostValue(host, isHostClean); | ||
var domain = getDomain(allRules, validHosts, cleanHost, true); | ||
module.exports = function getSubdomain(rules, validHosts, hostname) { | ||
hostname = cleanHostValue(hostname); | ||
|
||
var domain = getDomain(rules, validHosts, hostname); | ||
|
||
// No domain found? Just abort, abort! | ||
if (domain === null) { | ||
return null; | ||
} | ||
|
||
return cleanHost.substring(0, cleanHost.length - domain.length - 1); | ||
return hostname.substr(0, hostname.length - domain.length - 1); | ||
}; |
Oops, something went wrong.