Skip to content

Commit

Permalink
Add support for unicode domans to PhishingController
Browse files Browse the repository at this point in the history
The PhishingController now supports unicode domains. Previously it
supported plain ASCII domains (including punycode domains), but any
domains with non-ASCII unicode characters would always be considered
as safe.

Tests have been added to ensure the `test` and `bypass` methods both
normalize input to punycode.

Note that any whitelisted unicode domains will now be ignored. We could
fix this with a migration, but it's exceedingly unlikely that any
unicode domains could have been whitelisted in the first place, because
we only offer that ability for blocked domains. And unicode domains
were never blocked.

The `eth-phishing-detect` package was updated to make a pre-existing
test work correctly. Previously the "should bypass a given domain" test
was broken because the example unsafe domain was not blocked in the
version of `eth-phishing-detect` used. It is blocked in the current
version, and an assertion has been added to ensure these tests are
working correctly.
  • Loading branch information
Gudahtt committed May 20, 2021
1 parent 0bdd87c commit 6decd8e
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 13 deletions.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"eth-json-rpc-infura": "^5.1.0",
"eth-keyring-controller": "^6.2.1",
"eth-method-registry": "1.1.0",
"eth-phishing-detect": "^1.1.13",
"eth-phishing-detect": "^1.1.14",
"eth-query": "^2.1.2",
"eth-rpc-errors": "^4.0.0",
"eth-sig-util": "^3.0.0",
Expand All @@ -62,6 +62,7 @@
"isomorphic-fetch": "^3.0.0",
"jsonschema": "^1.2.4",
"nanoid": "^3.1.12",
"punycode": "^2.1.1",
"single-call-balance-checker-abi": "^1.0.0",
"uuid": "^8.3.2",
"web3": "^0.20.7",
Expand All @@ -75,6 +76,7 @@
"@metamask/eslint-config-typescript": "^6.0.0",
"@types/jest": "^26.0.22",
"@types/node": "^14.14.31",
"@types/punycode": "^2.1.0",
"@types/sinon": "^9.0.10",
"@types/web3": "^1.0.6",
"@typescript-eslint/eslint-plugin": "^4.22.0",
Expand Down
76 changes: 72 additions & 4 deletions src/third-party/PhishingController.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { strict as assert } from 'assert';
import { stub } from 'sinon';
import nock from 'nock';
import PhishingController from './PhishingController';
Expand Down Expand Up @@ -77,16 +78,83 @@ describe('PhishingController', () => {
expect(async () => await controller.updatePhishingLists()).not.toThrow();
});

it('should verify approved domain', () => {
it('should return negative result for safe domain', () => {
const controller = new PhishingController();
expect(controller.test('metamask.io')).toBe(false);
});

it('should return negative result for safe unicode domain', () => {
const controller = new PhishingController();
expect(controller.test('i❤.ws')).toBe(false);
});

it('should return negative result for safe punycode domain', () => {
const controller = new PhishingController();
expect(controller.test('xn--i-7iq.ws')).toBe(false);
});

it('should return positive result for unsafe domain', () => {
const controller = new PhishingController();
expect(controller.test('etnerscan.io')).toBe(true);
});

it('should return positive result for unsafe unicode domain', () => {
const controller = new PhishingController();
expect(controller.test('myetherẉalletṭ.com')).toBe(true);
});

it('should return positive result for unsafe punycode domain', () => {
const controller = new PhishingController();
expect(controller.test('xn--myetherallet-4k5fwn.com')).toBe(true);
});

it('should bypass a given domain', () => {
const controller = new PhishingController();
controller.bypass('electrum.mx');
controller.bypass('electrum.mx');
expect(controller.test('electrum.mx')).toBe(false);
const unsafeDomain = 'electrum.mx';
assert.equal(
controller.test(unsafeDomain),
true,
'Example unsafe domain seems to be safe',
);
controller.bypass(unsafeDomain);
expect(controller.test(unsafeDomain)).toBe(false);
});

it('should ignore second attempt to bypass a domain', () => {
const controller = new PhishingController();
const unsafeDomain = 'electrum.mx';
assert.equal(
controller.test(unsafeDomain),
true,
'Example unsafe domain seems to be safe',
);
controller.bypass(unsafeDomain);
controller.bypass(unsafeDomain);
expect(controller.test(unsafeDomain)).toBe(false);
});

it('should bypass a given unicode domain', () => {
const controller = new PhishingController();
const unsafeDomain = 'myetherẉalletṭ.com';
assert.equal(
controller.test(unsafeDomain),
true,
'Example unsafe domain seems to be safe',
);
controller.bypass(unsafeDomain);
expect(controller.test(unsafeDomain)).toBe(false);
});

it('should bypass a given punycode domain', () => {
const controller = new PhishingController();
const unsafeDomain = 'xn--myetherallet-4k5fwn.com';
assert.equal(
controller.test(unsafeDomain),
true,
'Example unsafe domain seems to be safe',
);
controller.bypass(unsafeDomain);
expect(controller.test(unsafeDomain)).toBe(false);
});

it('should not update phishing lists if fetch returns 304', async () => {
Expand Down
11 changes: 7 additions & 4 deletions src/third-party/PhishingController.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import punycode from 'punycode/';
import DEFAULT_PHISHING_RESPONSE from 'eth-phishing-detect/src/config.json';
import PhishingDetector from 'eth-phishing-detect/src/detector';
import BaseController, { BaseConfig, BaseState } from '../BaseController';
Expand Down Expand Up @@ -108,21 +109,23 @@ export class PhishingController extends BaseController<
* @returns - True if the origin is an unapproved origin
*/
test(origin: string): boolean {
if (this.state.whitelist.indexOf(origin) !== -1) {
const punycodeOrigin = punycode.toASCII(origin);
if (this.state.whitelist.indexOf(punycodeOrigin) !== -1) {
return false;
}
return this.detector.check(origin).result;
return this.detector.check(punycodeOrigin).result;
}

/**
* Temporarily marks a given origin as approved
*/
bypass(origin: string) {
const punycodeOrigin = punycode.toASCII(origin);
const { whitelist } = this.state;
if (whitelist.indexOf(origin) !== -1) {
if (whitelist.indexOf(punycodeOrigin) !== -1) {
return;
}
this.update({ whitelist: [...whitelist, origin] });
this.update({ whitelist: [...whitelist, punycodeOrigin] });
}

/**
Expand Down
13 changes: 9 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -900,6 +900,11 @@
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.1.0.tgz#5f96562c1075ee715a5b138f0b7f591c1f40f6b8"
integrity sha512-hiYA88aHiEIgDmeKlsyVsuQdcFn3Z2VuFd/Xm/HCnGnPD8UFU5BM128uzzRVVGEzKDKYUrRsRH9S2o+NUy/3IA==

"@types/punycode@^2.1.0":
version "2.1.0"
resolved "https://registry.yarnpkg.com/@types/punycode/-/punycode-2.1.0.tgz#89e4f3d09b3f92e87a80505af19be7e0c31d4e83"
integrity sha512-PG5aLpW6PJOeV2fHRslP4IOMWn+G+Uq8CfnyJ+PDS8ndCbU+soO+fB3NKCKo0p/Jh2Y4aPaiQZsrOXFdzpcA6g==

"@types/secp256k1@^4.0.1":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.1.tgz#fb3aa61a1848ad97d7425ff9dcba784549fca5a4"
Expand Down Expand Up @@ -2577,10 +2582,10 @@ eth-method-registry@1.1.0:
dependencies:
ethjs "^0.3.0"

eth-phishing-detect@^1.1.13:
version "1.1.13"
resolved "https://registry.yarnpkg.com/eth-phishing-detect/-/eth-phishing-detect-1.1.13.tgz#ed718b933c8a69fef0cefa6604538824b472dbea"
integrity sha512-1KQcKvAQIjJgFMVwxaw2+BlzM9Momzl0e+/torPdMjg7WGq6LmCIS7ddg84diH5zIQp9quGyRVIEawCCuErgVQ==
eth-phishing-detect@^1.1.14:
version "1.1.14"
resolved "https://registry.yarnpkg.com/eth-phishing-detect/-/eth-phishing-detect-1.1.14.tgz#64dcd35dd3a7a95266d875cbc5280842cda133d7"
integrity sha512-nMQmzrgYabZ52YpuKZ38lStJy9Lozww2WBhyFbu/oepjsZzPvnl2KwJ6TJ78kk14USdl8Htt+eEMk2WAdAjlWg==
dependencies:
fast-levenshtein "^2.0.6"

Expand Down

0 comments on commit 6decd8e

Please sign in to comment.