Skip to content
This repository has been archived by the owner on Dec 11, 2019. It is now read-only.

Commit

Permalink
Track extension states in a safer way
Browse files Browse the repository at this point in the history
This ensures a loaded extension is never loaded again, ditto an enabled extension is never enabled. etc.

Auditors: @bridiver

Fix #4425

Requires: brave/muon@43f9830
  • Loading branch information
bbondy committed Oct 2, 2016
1 parent e71a1ab commit d1e86c4
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 19 deletions.
78 changes: 59 additions & 19 deletions app/extensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const {fileUrl} = require('../js/lib/appUrlUtil')
const {getAppUrl, getExtensionsPath, getIndexHTML} = require('../js/lib/appUrlUtil')
const {getSetting} = require('../js/settings')
const settings = require('../js/constants/settings')
const extensionStates = require('../js/constants/extensionStates')
const {passwordManagers, extensionIds} = require('../js/constants/passwordManagers')
const appStore = require('../js/stores/appStore')
const extensionState = require('./common/state/extensionState')
Expand Down Expand Up @@ -142,9 +143,39 @@ let generateBraveManifest = () => {
return baseManifest
}

const extensionInfo = {
isEnabled: function (extensionId) {
return this.extensionState[extensionId] === extensionStates.ENABLED
},
isDisabled: function (extensionId) {
return this.extensionState[extensionId] === extensionStates.DISABLED
},
isLoading: function (extensionId) {
return this.extensionState[extensionId] === extensionStates.LOADING
},
isLoaded: function (extensionId) {
return [extensionStates.LOADED, extensionStates.ENABLED, extensionStates.DISABLED].includes(this.extensionState[extensionId])
},
isRegistered: function (extensionId) {
return [extensionStates.REGISTERED, extensionStates.LOADING, extensionStates.LOADED, extensionStates.ENABLED, extensionStates.DISABLED].includes(this.extensionState[extensionId])
},
isRegistering: function (extensionId) {
return this.extensionState[extensionId] === extensionStates.REGISTERING
},
getInstallInfo: function (extensionId) {
return this.installInfo[extensionId]
},
setState: function (extensionId, state) {
this.extensionState[extensionId] = state
},
setInstallInfo: function (extensionId, installInfo) {
this.installInfo[extensionId] = installInfo
},
extensionState: {},
installInfo: {}
}

module.exports.init = () => {
const loadedExtensions = {}
const registeredExtensions = {}
browserActions.init()

const {componentUpdater} = require('electron')
Expand All @@ -163,14 +194,14 @@ module.exports.init = () => {
componentUpdater.on('component-ready', (e, extensionId, extensionPath) => {
// console.log('component-ready', extensionId, extensionPath)
// Re-setup the loadedExtensions info if it exists
delete loadedExtensions[extensionId]
extensionInfo.setState(extensionId, extensionStates.REGISTERED)
loadExtension(extensionId, extensionPath)
})
componentUpdater.on('component-not-updated', () => {
// console.log('update-not-updated')
})
componentUpdater.on('component-registered', (e, extensionId) => {
// console.log('component-registered')
extensionInfo.setState(extensionId, extensionStates.REGISTERED)
const extensions = extensionState.getExtensions(appStore.getState())
const extensionPath = extensions.getIn([extensionId, 'filePath'])
// If we don't have info on the extension yet, check for an update / install
Expand All @@ -181,33 +212,40 @@ module.exports.init = () => {
}
})

let extensionInstalled = (installInfo) => {
const extensionLoaded = (installInfo) => {
if (installInfo.error) {
console.error(installInfo.error)
// TODO(bridiver) extensionActions.extensionInstallFailed
if (installInfo.id) {
extensionInfo.setState(installInfo.id, extensionStates.INSTALL_FAILED)
} else {
extensionInfo.setState(installInfo.id, undefined)
}
return
}
loadedExtensions[installInfo.id] = installInfo
extensionInfo.setState(installInfo.id, extensionStates.LOADED)
extensionInfo.setInstallInfo(installInfo.id, installInfo)
installInfo.filePath = installInfo.base_path
installInfo.base_path = fileUrl(installInfo.base_path)
extensionActions.extensionInstalled(installInfo.id, installInfo)
enableExtension(installInfo.id)
}

let loadExtension = (extensionId, extensionPath, options = {}) => {
if (!loadedExtensions[extensionId]) {
if (!extensionInfo.isLoaded(extensionId) && !extensionInfo.isLoading(extensionId)) {
extensionInfo.setState(extensionId, extensionStates.LOADING)
if (extensionId === config.braveExtensionId) {
process.emit('load-extension', extensionPath, options, extensionInstalled)
process.emit('load-extension', extensionPath, options, extensionLoaded)
return
}
// Verify we don't have info about an extension which doesn't exist
// on disk anymore. It will crash if it doesn't exist, so this is
// just a safety net.
fs.exists(path.join(extensionPath, 'manifest.json'), (exists) => {
if (exists) {
process.emit('load-extension', extensionPath, options, extensionInstalled)
process.emit('load-extension', extensionPath, options, extensionLoaded)
} else {
delete loadedExtensions[extensionId]
// This is an error condition, but we can recover.
extensionInfo.setState(extensionId, undefined)
componentUpdater.checkNow(extensionId)
}
})
Expand All @@ -217,27 +255,29 @@ module.exports.init = () => {
}

let enableExtension = (extensionId) => {
var installInfo = loadedExtensions[extensionId]
if (installInfo) {
if (extensionInfo.isLoaded(extensionId) && !extensionInfo.isEnabled(extensionId)) {
extensionInfo.setState(extensionId, extensionStates.ENABLED)
const installInfo = extensionInfo.getInstallInfo(extensionId)
process.emit('enable-extension', installInfo.id)
extensionActions.extensionEnabled(installInfo.id)
}
}

let disableExtension = (extensionId) => {
var installInfo = loadedExtensions[extensionId]
if (installInfo) {
if (extensionInfo.isLoaded(extensionId) && !extensionInfo.isDisabled(extensionId)) {
extensionInfo.setState(extensionId, extensionStates.DISABLED)
const installInfo = extensionInfo.getInstallInfo(extensionId)
process.emit('disable-extension', installInfo.id)
extensionActions.extensionDisabled(installInfo.id)
}
}

let registerExtension = (extensionId) => {
const extensions = extensionState.getExtensions(appStore.getState())
if (!registeredExtensions[extensionId]) {
if (!extensionInfo.isRegistered(extensionId) && !extensionInfo.isRegistering(extensionId)) {
extensionInfo.setState(extensionId, extensionStates.REGISTERING)
componentUpdater.registerComponent(extensionId)
registeredExtensions[extensionId] = true
} else {
const extensions = extensionState.getExtensions(appStore.getState())
const extensionPath = extensions.getIn([extensionId, 'filePath'])
if (extensionPath) {
// Otheriwse just install it
Expand All @@ -247,7 +287,7 @@ module.exports.init = () => {
}

// Manually install only the braveExtension
registeredExtensions[config.braveExtensionId] = true
extensionInfo.setState(config.braveExtensionId, extensionStates.REGISTERED)
loadExtension(config.braveExtensionId, getExtensionsPath('brave'), {manifest_location: 'component', manifest: generateBraveManifest()})

let registerExtensions = () => {
Expand Down
18 changes: 18 additions & 0 deletions js/constants/extensionStates.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

const mapValuesByKeys = require('../lib/functional').mapValuesByKeys

const _ = null
const extensionStates = {
REGISTERING: _,
REGISTERED: _,
LOADING: _,
LOADED: _,
ENABLED: _,
DISABLED: _,
INSTALL_FAILED: _
}

module.exports = mapValuesByKeys(extensionStates)

0 comments on commit d1e86c4

Please sign in to comment.