From c9f83fe8bc60a87876242a2dfb5350fceafffca1 Mon Sep 17 00:00:00 2001 From: bitpshr Date: Wed, 18 Apr 2018 17:02:08 -0400 Subject: Add JSDoc to various background scripts --- app/scripts/config.js | 39 ++++++++++- app/scripts/contentscript.js | 42 ++++++++++++ app/scripts/edge-encryptor.js | 142 ++++++++++++++++++++++++---------------- app/scripts/first-time-state.js | 16 +++-- app/scripts/inpage.js | 11 ++-- app/scripts/popup-core.js | 25 ++++++- 6 files changed, 205 insertions(+), 70 deletions(-) (limited to 'app') diff --git a/app/scripts/config.js b/app/scripts/config.js index a8470ed82..634d7a013 100644 --- a/app/scripts/config.js +++ b/app/scripts/config.js @@ -15,7 +15,41 @@ const BETA_UI_NETWORK_TYPE = 'networkBeta' global.METAMASK_DEBUG = process.env.METAMASK_DEBUG -module.exports = { +/** + * @typedef {Object} UrlConfig + * @property {string} localhost URL of local RPC provider + * @property {string} mainnet URL of mainnet RPC provider + * @property {string} ropsten URL of Ropsten testnet RPC provider + * @property {string} kovan URL of Kovan testnet RPC provider + * @property {string} rinkeby URL of Rinkeby testnet RPC provider + */ + +/** + * @typedef {Object} NameConfig + * @property {string} 3 URL of local RPC provider + * @property {string} 4 URL of mainnet RPC provider + * @property {string} 42 URL of Ropsten testnet RPC provider + */ + +/** + * @typedef {Object} EnumConfig + * @property {string} DEFAULT_RPC Default network provider URL + * @property {string} OLD_UI_NETWORK_TYPE + * @property {string} BETA_UI_NETWORK_TYPE + */ + +/** + * @typedef {Object} Config + * @property {UrlConfig} network Network configuration parameters + * @property {UrlConfig} networkBeta Beta UI network configuration parameters + * @property {NameConfig} networkNames Network name configuration parameters + * @property {EnumConfig} enums Application-wide string constants + */ + +/** + * @type {Config} Application configuration object + **/ +const config = { network: { localhost: LOCALHOST_RPC_URL, mainnet: MAINET_RPC_URL, @@ -23,7 +57,6 @@ module.exports = { kovan: KOVAN_RPC_URL, rinkeby: RINKEBY_RPC_URL, }, - // Used for beta UI networkBeta: { localhost: LOCALHOST_RPC_URL, mainnet: MAINET_RPC_URL_BETA, @@ -42,3 +75,5 @@ module.exports = { BETA_UI_NETWORK_TYPE, }, } + +module.exports = config \ No newline at end of file diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js index fe1766273..728aab8cb 100644 --- a/app/scripts/contentscript.js +++ b/app/scripts/contentscript.js @@ -23,6 +23,9 @@ if (shouldInjectWeb3()) { setupStreams() } +/** + * Creates a script tag that injects inpage.js + */ function setupInjection () { try { // inject in-page script @@ -37,6 +40,10 @@ function setupInjection () { } } +/** + * Sets up two-way communication streams between the + * browser extension and local per-page browser context + */ function setupStreams () { // setup communication to page and plugin const pageStream = new LocalMessageDuplexStream({ @@ -89,17 +96,34 @@ function setupStreams () { mux.ignoreStream('publicConfig') } + +/** + * Error handler for page to plugin stream disconnections + * + * @param {string} remoteLabel Remote stream name + * @param {Error} err Stream connection error + */ function logStreamDisconnectWarning (remoteLabel, err) { let warningMsg = `MetamaskContentscript - lost connection to ${remoteLabel}` if (err) warningMsg += '\n' + err.stack console.warn(warningMsg) } +/** + * Determines if Web3 should be injected + * + * @returns {boolean} True of Web3 should be injected + */ function shouldInjectWeb3 () { return doctypeCheck() && suffixCheck() && documentElementCheck() && !blacklistedDomainCheck() } +/** + * Checks the doctype of the current document if it exists + * + * @returns {boolean} True if the doctype is html or if none exists + */ function doctypeCheck () { const doctype = window.document.doctype if (doctype) { @@ -109,6 +133,11 @@ function doctypeCheck () { } } +/** + * Checks the current document extension + * + * @returns {boolean} True if the current extension is not prohibited + */ function suffixCheck () { var prohibitedTypes = ['xml', 'pdf'] var currentUrl = window.location.href @@ -122,6 +151,11 @@ function suffixCheck () { return true } +/** + * Checks the documentElement of the current document + * + * @returns {boolean} True if the documentElement is an html node or if none exists + */ function documentElementCheck () { var documentElement = document.documentElement.nodeName if (documentElement) { @@ -130,6 +164,11 @@ function documentElementCheck () { return true } +/** + * Checks if the current domain is blacklisted + * + * @returns {boolean} True if the current domain is blacklisted + */ function blacklistedDomainCheck () { var blacklistedDomains = [ 'uscourts.gov', @@ -148,6 +187,9 @@ function blacklistedDomainCheck () { return false } +/** + * Redirects the current page to a phishing information page + */ function redirectToPhishingWarning () { console.log('MetaMask - redirecting to phishing warning') window.location.href = 'https://metamask.io/phishing.html' diff --git a/app/scripts/edge-encryptor.js b/app/scripts/edge-encryptor.js index 24c0c93a8..5f6699746 100644 --- a/app/scripts/edge-encryptor.js +++ b/app/scripts/edge-encryptor.js @@ -1,69 +1,97 @@ const asmcrypto = require('asmcrypto.js') const Unibabel = require('browserify-unibabel') +/** + * A Microsoft Edge-specific encryption class that exposes + * the interface expected by eth-keykeyring-controller + */ class EdgeEncryptor { + /** + * Encrypts an arbitrary JavaScript object to cypher text + * + * @param {string} password Password used to unlock a cryptographic key + * @param {Object} dataObject Data to encrypt + * @returns {Object} Object containing cypher text, generation vectors, and salt + */ + encrypt (password, dataObject) { + var salt = this._generateSalt() + return this._keyFromPassword(password, salt) + .then(function (key) { + var data = JSON.stringify(dataObject) + var dataBuffer = Unibabel.utf8ToBuffer(data) + var vector = global.crypto.getRandomValues(new Uint8Array(16)) + var resultbuffer = asmcrypto.AES_GCM.encrypt(dataBuffer, key, vector) - encrypt (password, dataObject) { + var buffer = new Uint8Array(resultbuffer) + var vectorStr = Unibabel.bufferToBase64(vector) + var vaultStr = Unibabel.bufferToBase64(buffer) + return JSON.stringify({ + data: vaultStr, + iv: vectorStr, + salt: salt, + }) + }) + } - var salt = this._generateSalt() - return this._keyFromPassword(password, salt) - .then(function (key) { + /** + * Decrypts an arbitrary JavaScript object from cypher text + * + * @param {string} password Password used to unlock a cryptographic key + * @param {string} text Cypher text of an encrypted JavaScript object + * @returns {Promise} Promise resolving to copy of decrypted JavaScript object + */ + decrypt (password, text) { + const payload = JSON.parse(text) + const salt = payload.salt + return this._keyFromPassword(password, salt) + .then(function (key) { + const encryptedData = Unibabel.base64ToBuffer(payload.data) + const vector = Unibabel.base64ToBuffer(payload.iv) + return new Promise((resolve, reject) => { + var result + try { + result = asmcrypto.AES_GCM.decrypt(encryptedData, key, vector) + } catch (err) { + return reject(new Error('Incorrect password')) + } + const decryptedData = new Uint8Array(result) + const decryptedStr = Unibabel.bufferToUtf8(decryptedData) + const decryptedObj = JSON.parse(decryptedStr) + resolve(decryptedObj) + }) + }) + } - var data = JSON.stringify(dataObject) - var dataBuffer = Unibabel.utf8ToBuffer(data) - var vector = global.crypto.getRandomValues(new Uint8Array(16)) - var resultbuffer = asmcrypto.AES_GCM.encrypt(dataBuffer, key, vector) + /** + * Retrieves a cryptographic key using a password + * + * @private + * @param {string} password Password used to unlock a cryptographic key + * @param {string} salt Random base-64 data + * @returns {Promise} Promise resolving to a derived key + */ + _keyFromPassword (password, salt) { - var buffer = new Uint8Array(resultbuffer) - var vectorStr = Unibabel.bufferToBase64(vector) - var vaultStr = Unibabel.bufferToBase64(buffer) - return JSON.stringify({ - data: vaultStr, - iv: vectorStr, - salt: salt, - }) - }) - } + var passBuffer = Unibabel.utf8ToBuffer(password) + var saltBuffer = Unibabel.base64ToBuffer(salt) + return new Promise((resolve) => { + var key = asmcrypto.PBKDF2_HMAC_SHA256.bytes(passBuffer, saltBuffer, 10000) + resolve(key) + }) + } - decrypt (password, text) { - - const payload = JSON.parse(text) - const salt = payload.salt - return this._keyFromPassword(password, salt) - .then(function (key) { - const encryptedData = Unibabel.base64ToBuffer(payload.data) - const vector = Unibabel.base64ToBuffer(payload.iv) - return new Promise((resolve, reject) => { - var result - try { - result = asmcrypto.AES_GCM.decrypt(encryptedData, key, vector) - } catch (err) { - return reject(new Error('Incorrect password')) - } - const decryptedData = new Uint8Array(result) - const decryptedStr = Unibabel.bufferToUtf8(decryptedData) - const decryptedObj = JSON.parse(decryptedStr) - resolve(decryptedObj) - }) - }) - } - - _keyFromPassword (password, salt) { - - var passBuffer = Unibabel.utf8ToBuffer(password) - var saltBuffer = Unibabel.base64ToBuffer(salt) - return new Promise((resolve) => { - var key = asmcrypto.PBKDF2_HMAC_SHA256.bytes(passBuffer, saltBuffer, 10000) - resolve(key) - }) - } - - _generateSalt (byteCount = 32) { - var view = new Uint8Array(byteCount) - global.crypto.getRandomValues(view) - var b64encoded = btoa(String.fromCharCode.apply(null, view)) - return b64encoded - } + /** + * Generates random base-64 encoded data + * + * @private + * @returns {string} Randomized base-64 encoded data + */ + _generateSalt (byteCount = 32) { + var view = new Uint8Array(byteCount) + global.crypto.getRandomValues(view) + var b64encoded = btoa(String.fromCharCode.apply(null, view)) + return b64encoded + } } module.exports = EdgeEncryptor diff --git a/app/scripts/first-time-state.js b/app/scripts/first-time-state.js index 3063df627..0c8f35303 100644 --- a/app/scripts/first-time-state.js +++ b/app/scripts/first-time-state.js @@ -2,10 +2,16 @@ const env = process.env.METAMASK_ENV const METAMASK_DEBUG = process.env.METAMASK_DEBUG -// -// The default state of MetaMask -// -module.exports = { +/** + * @typedef {Object} FirstTimeState + * @property {Object} config Initial configuration parameters + * @property {Object} NetworkController Network controller state + */ + +/** + * @type {FirstTimeState} The default state of MetaMask + */ +const initialState = { config: {}, NetworkController: { provider: { @@ -13,3 +19,5 @@ module.exports = { }, }, } + +module.exports = initialState diff --git a/app/scripts/inpage.js b/app/scripts/inpage.js index 92c732813..798af78bf 100644 --- a/app/scripts/inpage.js +++ b/app/scripts/inpage.js @@ -42,20 +42,18 @@ log.debug('MetaMask - injected web3') setupDappAutoReload(web3, inpageProvider.publicConfigStore) // set web3 defaultAccount - inpageProvider.publicConfigStore.subscribe(function (state) { web3.eth.defaultAccount = state.selectedAddress }) -// -// util -// - // need to make sure we aren't affected by overlapping namespaces // and that we dont affect the app with our namespace // mostly a fix for web3's BigNumber if AMD's "define" is defined... var __define +/** + * Caches reference to global define object and deletes it + */ function cleanContextForImports () { __define = global.define try { @@ -65,6 +63,9 @@ function cleanContextForImports () { } } +/** + * Restores global define object from cached reference + */ function restoreContextAfterImports () { try { global.define = __define diff --git a/app/scripts/popup-core.js b/app/scripts/popup-core.js index 2e4334bb1..5a5fa95f9 100644 --- a/app/scripts/popup-core.js +++ b/app/scripts/popup-core.js @@ -7,10 +7,14 @@ const launchMetamaskUi = require('../../ui') const StreamProvider = require('web3-stream-provider') const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex - module.exports = initializePopup - +/** + * Asynchronously initializes the MetaMask popup UI + * + * @param {{ container: Element, connectionStream: any }} config Popup configuration object + * @param {Function} cb Called when initialization is comlete + */ function initializePopup ({ container, connectionStream }, cb) { // setup app async.waterfall([ @@ -19,6 +23,12 @@ function initializePopup ({ container, connectionStream }, cb) { ], cb) } +/** + * Establishes streamed connections to background scripts and a Web3 provider + * + * @param {any} connectionStream PortStream instance establishing a background connection + * @param {Function} cb Called when controller connection is established + */ function connectToAccountManager (connectionStream, cb) { // setup communication with background // setup multiplexing @@ -28,6 +38,11 @@ function connectToAccountManager (connectionStream, cb) { setupWeb3Connection(mx.createStream('provider')) } +/** + * Establishes a streamed connection to a Web3 provider + * + * @param {any} connectionStream PortStream instance establishing a background connection + */ function setupWeb3Connection (connectionStream) { var providerStream = new StreamProvider() providerStream.pipe(connectionStream).pipe(providerStream) @@ -38,6 +53,12 @@ function setupWeb3Connection (connectionStream) { global.eth = new Eth(providerStream) } +/** + * Establishes a streamed connection to the background account manager + * + * @param {any} connectionStream PortStream instance establishing a background connection + * @param {Function} cb Called when the remote account manager connection is established + */ function setupControllerConnection (connectionStream, cb) { // this is a really sneaky way of adding EventEmitter api // to a bi-directional dnode instance -- cgit v1.2.3