Skip to content

Commit

Permalink
Merge pull request #4566 from MetaMask/notice-phishing
Browse files Browse the repository at this point in the history
Push new notice on recent phishing incidents
  • Loading branch information
danfinlay authored Jun 14, 2018
2 parents 1f83a11 + c2afb79 commit 3a5089d
Show file tree
Hide file tree
Showing 23 changed files with 140 additions and 183 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- Fix bug where account reset did not work with custom RPC providers.
- Fix bug where nonce mutex was never released
- Stop reloading browser page on Ethereum network change
- Add phishing notice

## 4.7.4 Tue Jun 05 2018

Expand Down
3 changes: 0 additions & 3 deletions app/scripts/metamask-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,6 @@ module.exports = class MetamaskController extends EventEmitter {
version,
firstVersion: initState.firstTimeInfo.version,
})
this.noticeController.updateNoticesList()
// to be uncommented when retrieving notices from a remote server.
// this.noticeController.startPolling()

this.shapeshiftController = new ShapeShiftController({
initState: initState.ShapeShiftController,
Expand Down
32 changes: 10 additions & 22 deletions app/scripts/notice-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const EventEmitter = require('events').EventEmitter
const semver = require('semver')
const extend = require('xtend')
const ObservableStore = require('obs-store')
const hardCodedNotices = require('../../notices/notices.json')
const hardCodedNotices = require('../../notices/notices.js')
const uniqBy = require('lodash.uniqby')

module.exports = class NoticeController extends EventEmitter {
Expand All @@ -13,11 +13,12 @@ module.exports = class NoticeController extends EventEmitter {
this.firstVersion = opts.firstVersion
this.version = opts.version
const initState = extend({
noticesList: [],
noticesList: this._filterNotices(hardCodedNotices),
}, opts.initState)
this.store = new ObservableStore(initState)
this.memStore = new ObservableStore({})
this.store.subscribe(() => this._updateMemstore())
this._updateMemstore()
}

getNoticesList () {
Expand All @@ -29,9 +30,9 @@ module.exports = class NoticeController extends EventEmitter {
return notices.filter((notice) => notice.read === false)
}

getLatestUnreadNotice () {
getNextUnreadNotice () {
const unreadNotices = this.getUnreadNotices()
return unreadNotices[unreadNotices.length - 1]
return unreadNotices[0]
}

async setNoticesList (noticesList) {
Expand All @@ -47,7 +48,7 @@ module.exports = class NoticeController extends EventEmitter {
notices[index].read = true
notices[index].body = ''
this.setNoticesList(notices)
const latestNotice = this.getLatestUnreadNotice()
const latestNotice = this.getNextUnreadNotice()
cb(null, latestNotice)
} catch (err) {
cb(err)
Expand All @@ -64,15 +65,6 @@ module.exports = class NoticeController extends EventEmitter {
return result
}

startPolling () {
if (this.noticePoller) {
clearInterval(this.noticePoller)
}
this.noticePoller = setInterval(() => {
this.noticeController.updateNoticesList()
}, 300000)
}

_mergeNotices (oldNotices, newNotices) {
return uniqBy(oldNotices.concat(newNotices), 'id')
}
Expand All @@ -91,19 +83,15 @@ module.exports = class NoticeController extends EventEmitter {
})
}

_mapNoticeIds (notices) {
return notices.map((notice) => notice.id)
}

async _retrieveNoticeData () {
// Placeholder for the API.
return hardCodedNotices
return []
}

_updateMemstore () {
const lastUnreadNotice = this.getLatestUnreadNotice()
const noActiveNotices = !lastUnreadNotice
this.memStore.updateState({ lastUnreadNotice, noActiveNotices })
const nextUnreadNotice = this.getNextUnreadNotice()
const noActiveNotices = !nextUnreadNotice
this.memStore.updateState({ nextUnreadNotice, noActiveNotices })
}

}
2 changes: 1 addition & 1 deletion development/states/conf-tx.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
"conversionRate": 12.7200827,
"conversionDate": 1487363041,
"noActiveNotices": true,
"lastUnreadNotice": {
"nextUnreadNotice": {
"read": true,
"date": "Thu Feb 09 2017",
"title": "Terms of Use",
Expand Down
2 changes: 1 addition & 1 deletion development/states/first-time.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"conversionRate": 12.7527416,
"conversionDate": 1487624341,
"noActiveNotices": false,
"lastUnreadNotice": {
"nextUnreadNotice": {
"read": false,
"date": "Thu Feb 09 2017",
"title": "Terms of Use",
Expand Down
2 changes: 1 addition & 1 deletion development/states/notice.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"conversionRate": 8.3533002,
"conversionDate": 1481671082,
"noActiveNotices": false,
"lastUnreadNotice": {
"nextUnreadNotice": {
"read": false,
"date": "Tue Dec 13 2016",
"title": "MultiVault Support",
Expand Down
14 changes: 7 additions & 7 deletions mascara/src/app/first-time/notice-screen.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import LoadingScreen from './loading-screen'
class NoticeScreen extends Component {
static propTypes = {
address: PropTypes.string.isRequired,
lastUnreadNotice: PropTypes.shape({
nextUnreadNotice: PropTypes.shape({
title: PropTypes.string,
date: PropTypes.string,
body: PropTypes.string,
Expand All @@ -31,7 +31,7 @@ class NoticeScreen extends Component {
};

static defaultProps = {
lastUnreadNotice: {},
nextUnreadNotice: {},
};

state = {
Expand All @@ -47,8 +47,8 @@ class NoticeScreen extends Component {
}

acceptTerms = () => {
const { markNoticeRead, lastUnreadNotice, history } = this.props
markNoticeRead(lastUnreadNotice)
const { markNoticeRead, nextUnreadNotice, history } = this.props
markNoticeRead(nextUnreadNotice)
.then(hasActiveNotices => {
if (!hasActiveNotices) {
history.push(INITIALIZE_BACKUP_PHRASE_ROUTE)
Expand All @@ -72,7 +72,7 @@ class NoticeScreen extends Component {
render () {
const {
address,
lastUnreadNotice: { title, body },
nextUnreadNotice: { title, body },
isLoading,
} = this.props
const { atBottom } = this.state
Expand Down Expand Up @@ -113,12 +113,12 @@ class NoticeScreen extends Component {
}

const mapStateToProps = ({ metamask, appState }) => {
const { selectedAddress, lastUnreadNotice, noActiveNotices } = metamask
const { selectedAddress, nextUnreadNotice, noActiveNotices } = metamask
const { isLoading } = appState

return {
address: selectedAddress,
lastUnreadNotice,
nextUnreadNotice,
noActiveNotices,
isLoading,
}
Expand Down
6 changes: 6 additions & 0 deletions notices/archive/notice_4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Dear MetaMask Users,

There have been several instances of high-profile legitimate websites such as BTC Manager and Games Workshop that have had their websites temporarily compromised. This involves showing a fake MetaMask window on the page asking for user's seed phrases. MetaMask will never open itself in this way and users are encouraged to report these instances immediately to either [our phishing blacklist](https://github.com/MetaMask/eth-phishing-detect/issues) or our support email at [[email protected]]([email protected]).

Please read our full article on this ongoing issue at [https://medium.com/metamask/new-phishing-strategy-becoming-common-1b1123837168](https://medium.com/metamask/new-phishing-strategy-becoming-common-1b1123837168).

27 changes: 0 additions & 27 deletions notices/notice-delete.js

This file was deleted.

33 changes: 0 additions & 33 deletions notices/notice-generator.js

This file was deleted.

1 change: 0 additions & 1 deletion notices/notice-nonce.json

This file was deleted.

34 changes: 34 additions & 0 deletions notices/notices.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// fs.readFileSync is inlined by browserify transform "brfs"
const fs = require('fs')

module.exports = [
{
id: 0,
read: false,
date: 'Thu Feb 09 2017',
title: 'Terms of Use',
body: fs.readFileSync(__dirname + '/archive/notice_0.md', 'utf8'),
},
{
id: 2,
read: false,
date: 'Mon May 08 2017',
title: 'Privacy Notice',
body: fs.readFileSync(__dirname + '/archive/notice_2.md', 'utf8'),
},
{
id: 3,
read: false,
date: 'Tue Nov 28 2017',
title: 'Seed Phrase Alert',
firstVersion: '<=3.12.0',
body: fs.readFileSync(__dirname + '/archive/notice_3.md', 'utf8'),
},
{
id: 4,
read: false,
date: 'Wed Jun 13 2018',
title: 'Phishing Warning',
body: fs.readFileSync(__dirname + '/archive/notice_4.md', 'utf8'),
}
]
1 change: 0 additions & 1 deletion notices/notices.json

This file was deleted.

6 changes: 3 additions & 3 deletions old-ui/app/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ function mapStateToProps (state) {
network: state.metamask.network,
provider: state.metamask.provider,
forgottenPassword: state.appState.forgottenPassword,
lastUnreadNotice: state.metamask.lastUnreadNotice,
nextUnreadNotice: state.metamask.nextUnreadNotice,
lostAccounts: state.metamask.lostAccounts,
frequentRpcList: state.metamask.frequentRpcList || [],
featureFlags,
Expand Down Expand Up @@ -460,9 +460,9 @@ App.prototype.renderPrimary = function () {
}, [

h(NoticeScreen, {
notice: props.lastUnreadNotice,
notice: props.nextUnreadNotice,
key: 'NoticeScreen',
onConfirm: () => props.dispatch(actions.markNoticeRead(props.lastUnreadNotice)),
onConfirm: () => props.dispatch(actions.markNoticeRead(props.nextUnreadNotice)),
}),

!props.isInitialized && h('.flex-row.flex-center.flex-grow', [
Expand Down
2 changes: 0 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,6 @@
"disc": "gulp disc --debug",
"announce": "node development/announcer.js",
"version:bump": "node development/run-version-bump.js",
"generateNotice": "node notices/notice-generator.js",
"deleteNotice": "node notices/notice-delete.js",
"storybook": "start-storybook -p 6006 -c .storybook"
},
"browserify": {
Expand Down
29 changes: 21 additions & 8 deletions test/e2e/beta/from-import-beta-ui.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,19 +134,32 @@ describe('Using MetaMask with an existing account', function () {
await delay(regularDelayMs)
})

it('clicks through the ToS', async () => {
// terms of use
const canClickThrough = await driver.findElement(By.css('.tou button')).isEnabled()
assert.equal(canClickThrough, false, 'disabled continue button')
const bottomOfTos = await findElement(driver, By.linkText('Attributions'))
await driver.executeScript('arguments[0].scrollIntoView(true)', bottomOfTos)
await delay(regularDelayMs)
const acceptTos = await findElement(driver, By.css('.tou button'))
await acceptTos.click()
await delay(regularDelayMs)
})

it('clicks through the privacy notice', async () => {
const [nextScreen] = await findElements(driver, By.css('.tou button'))
// privacy notice
const nextScreen = await findElement(driver, By.css('.tou button'))
await nextScreen.click()
await delay(regularDelayMs)
})

const canClickThrough = await driver.findElement(By.css('.tou button')).isEnabled()
assert.equal(canClickThrough, false, 'disabled continue button')
const element = await findElement(driver, By.linkText('Attributions'))
await driver.executeScript('arguments[0].scrollIntoView(true)', element)
it('clicks through the phishing notice', async () => {
// phishing notice
const noticeElement = await driver.findElement(By.css('.markdown'))
await driver.executeScript('arguments[0].scrollTop = arguments[0].scrollHeight', noticeElement)
await delay(regularDelayMs)

const acceptTos = await findElement(driver, By.xpath(`//button[contains(text(), 'Accept')]`))
await acceptTos.click()
const nextScreen = await findElement(driver, By.css('.tou button'))
await nextScreen.click()
await delay(regularDelayMs)
})
})
Expand Down
Loading

0 comments on commit 3a5089d

Please sign in to comment.