Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Push new notice on recent phishing incidents #4566

Merged
merged 9 commits into from
Jun 14, 2018
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,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 [support@metamask.io](support@metamask.io).

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