From c76c9ca2c86317f902f443db2c5704d4bf6311c0 Mon Sep 17 00:00:00 2001
From: bitpshr <mail@bitpshr.net>
Date: Thu, 27 Sep 2018 14:19:09 -0400
Subject: EIP-1102: updated implementation

---
 app/scripts/contentscript.js | 61 ++++++++++++++++++++++++++++++++++++--------
 1 file changed, 51 insertions(+), 10 deletions(-)

(limited to 'app/scripts/contentscript.js')

diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js
index 33523eb46..060343031 100644
--- a/app/scripts/contentscript.js
+++ b/app/scripts/contentscript.js
@@ -11,6 +11,7 @@ const PortStream = require('extension-port-stream')
 const inpageContent = fs.readFileSync(path.join(__dirname, '..', '..', 'dist', 'chrome', 'inpage.js')).toString()
 const inpageSuffix = '//# sourceURL=' + extension.extension.getURL('inpage.js') + '\n'
 const inpageBundle = inpageContent + inpageSuffix
+let originApproved = false
 
 // Eventually this streaming injection could be replaced with:
 // https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Language_Bindings/Components.utils.exportFunction
@@ -20,24 +21,24 @@ const inpageBundle = inpageContent + inpageSuffix
 // MetaMask will be much faster loading and performant on Firefox.
 
 if (shouldInjectWeb3()) {
-  setupInjection()
+  injectScript(inpageBundle)
   setupStreams()
+  listenForProviderRequest()
 }
 
 /**
- * Creates a script tag that injects inpage.js
+ * Injects a script tag into the current document
+ *
+ * @param {string} content - Code to be executed in the current document
  */
-function setupInjection () {
+function injectScript (content) {
   try {
-    // inject in-page script
-    var scriptTag = document.createElement('script')
-    scriptTag.textContent = inpageBundle
-    scriptTag.onload = function () { this.parentNode.removeChild(this) }
-    var container = document.head || document.documentElement
-    // append as first child
+    const container = document.head || document.documentElement
+    const scriptTag = document.createElement('script')
+    scriptTag.textContent = content
     container.insertBefore(scriptTag, container.children[0])
   } catch (e) {
-    console.error('Metamask injection failed.', e)
+    console.error('Metamask script injection failed.', e)
   }
 }
 
@@ -54,6 +55,16 @@ function setupStreams () {
   const pluginPort = extension.runtime.connect({ name: 'contentscript' })
   const pluginStream = new PortStream(pluginPort)
 
+  // Until this origin is approved, cut-off publicConfig stream writes at the content
+  // script level so malicious sites can't snoop on the currently-selected address
+  pageStream._write = function (data, encoding, cb) {
+    if (typeof data === 'object' && data.name && data.name === 'publicConfig' && !originApproved) {
+      cb()
+      return
+    }
+    LocalMessageDuplexStream.prototype._write.apply(pageStream, arguments)
+  }
+
   // forward communication plugin->inpage
   pump(
     pageStream,
@@ -97,6 +108,36 @@ function setupStreams () {
   mux.ignoreStream('publicConfig')
 }
 
+/**
+ * Establishes listeners for requests to fully-enable the provider from the dapp context
+ * and for full-provider approvals and rejections from the background script context. Dapps
+ * should not post messages directly and should instead call provider.enable(), which
+ * handles posting these messages automatically.
+ */
+function listenForProviderRequest () {
+  window.addEventListener('message', (event) => {
+    if (event.source !== window) { return }
+    if (!event.data || !event.data.type || event.data.type !== 'ETHEREUM_ENABLE_PROVIDER') { return }
+    extension.runtime.sendMessage({
+      action: 'init-provider-request',
+      origin: event.source.location.hostname,
+    })
+  })
+
+  extension.runtime.onMessage.addListener(({ action }) => {
+    if (!action) { return }
+    switch (action) {
+      case 'approve-provider-request':
+        originApproved = true
+        injectScript(`window.dispatchEvent(new CustomEvent('ethereumprovider', { detail: {}}))`)
+        break
+      case 'reject-provider-request':
+        injectScript(`window.dispatchEvent(new CustomEvent('ethereumprovider', { detail: { error: 'User rejected provider access' }}))`)
+        break
+    }
+  })
+}
+
 
 /**
  * Error handler for page to plugin stream disconnections
-- 
cgit