Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 37 additions & 13 deletions src/__tests__/utils.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,45 +77,69 @@ describe('utils', () => {
});
});

function testPrefixes(isRegex) {
isRegex = !!isRegex;
const simplePattern = isRegex?
'@duckduckgo\\.com' : '!duckduckgo.com';
function testPrefixes(pattern, expectedUrl, evilUrl) {
return () => {
it('should match url without path', () => {
expect(
utils.matchesSavedMap(
'https://duckduckgo.com',
expectedUrl,
matchDomainOnly, {
host: simplePattern,
host: pattern,
})
).toBe(true);
});
it('should match url with path', () => {
expect(
utils.matchesSavedMap(
'https://duckduckgo.com/?q=search+me+baby',
expectedUrl + '/?q=search+me+baby',
matchDomainOnly, {
host: simplePattern,
host: pattern,
})
).toBe(true);
});
let prefix = matchDomainOnly ? 'should not' : 'should';
let description = `${prefix} match url with pattern only in path`;
let description = `${pattern} ${prefix} match ${evilUrl}`;
it(description, () => {
expect(
utils.matchesSavedMap(
'https://google.com/?q=duckduckgo',
evilUrl,
matchDomainOnly, {
host: simplePattern,
host: pattern,
})
).toBe(!matchDomainOnly);
Comment on lines 100 to 109
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section's purpose is to make sure that matchDomain=true really only uses the domain for matching and not the URL path contain the domain. When matchDomain=false the url should match because the URL path contains the domain.

It looks like you repurposed the section to use evilUrl which makes sense when you put the domain in the URL's path. However, it becomes a lot less clear in the other cases.

I'd recommend explicitly adding an it for your test and reworking this part to add the domain of expectedUrl to the URL path.

});
};
}

describe('with regex host prefix', testPrefixes(true));
describe('with glob host prefix', testPrefixes());
describe('with regex host prefix', testPrefixes(
'@duckduckgo\\.com',
'https://duckduckgo.com',
'https://google.com/?q=duckduckgo'));

describe('with glob host prefix', testPrefixes(
'!duckduckgo.com',
'https://duckduckgo.com',
'https://google.com/?q=duckduckgo'));

describe('with regex host prefix', testPrefixes(
'@duckduckgo\\.com',
'https://duckduckgo.com',
'https://evil.duckduckgo.com.evil.com'));

describe('with glob host prefix', testPrefixes(
'!duckduckgo.com',
'https://duckduckgo.com',
'https://evil.duckduckgo.com.evil.com'));

describe('with glob subdomain prefix', testPrefixes(
'!*.duckduckgo.com',
'https://example.duckduckgo.com',
'https://notduckduckgo.com'));

describe('with regex subdomain prefix', testPrefixes(
'@(.+)\\.duckduckgo\\.com',
'https://example.duckduckgo.com',
'https://notduckduckgo.com'));
};
}

Expand Down
28 changes: 23 additions & 5 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,11 @@ export const matchesSavedMap = (url, matchDomainOnly, {host}) => {
}

if (host[0] === PREFIX_REGEX) {
const regex = host.substr(1);
let regex = host.substr(1);
if (matchDomainOnly) {
// This might generate double ^^ characters, but that works anyway
regex = "^" + regex + "$";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a linting error. Please run npm lint.

}
Comment on lines -86 to +90
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned in the comments earlier, this is an unnecessary change based on the assumption that regexes should match from the beginning of the string.

try {
return new RegExp(regex).test(toMatch);
} catch (e) {
Expand All @@ -93,10 +97,14 @@ export const matchesSavedMap = (url, matchDomainOnly, {host}) => {
// turning glob into regex isn't the worst thing:
// 1. * becomes .*
// 2. ? becomes .?
return new RegExp(host.substr(1)
.replace(/\*/g, '.*')
.replace(/\?/g, '.?'))
.test(toMatch);
// Because the string is regex escaped, you must match \* to instead of *
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's either a word too many here or one missing.

let regex = escapeRegExp(host.substr(1))
.replace(/\\\*/g, '.*')
.replace(/\\\?/g, '.?')
if (matchDomainOnly) {
regex = "^" + regex + "$";
}
return new RegExp(regex).test(toMatch);
} else {
const key = urlKeyFromUrl(urlO);
const _url = ((key.indexOf('/') === -1) ? key.concat('/') : key).toLowerCase();
Expand Down Expand Up @@ -135,3 +143,13 @@ export function formatString(string, context) {
return replacement;
});
}

/**
* Escape all regex metacharacters in a string
*
* @param string {String}
*/
function escapeRegExp(string) {
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Escaping
return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}