6
votes

I have an electron app. My client script (renderer) needs to access electron APIs, but this gave me a security warning, so I moved it into a preload script and disabled nodeIntegration. I was then receiving a warning about contextIsolation, so I enabled it. My preload script was previously attaching a function to window that the client could read as follows:

window.readClipboard = function(){
    return clipboard.readText()
}

Unfortunately, context isolation meant that the client could no longer access this function. Is there a way to make this work with context isolation or should I disable it?

Extra Details

The warning message that made me try to turn on context isolation was the following:

Electron Deprecation Warning (contextIsolation default change) This window has context isolation disabled by default. In Electron 5.0.0, context isolation will be enabled by default. To prepare for this change, set {contextIsolation: false} in the webPreferences for this window, or ensure that this window does not rely on context isolation being disabled, and set {contextIsolation: true}.

In client.js I try accessing:

console.log("window.readClipboard", window.readClipboard)

With output:

window.readClipboard undefined

1
are you trying to access clipboard object from the renderer process ? - 0.sh
do you recall what security warning you were getting initially? - pushkin
@0.sh: Yeah, I want the renderer process to be able to access the clipboard via calling a function defined in the preload script - Casebash
@Casebash why can't you use do require("electron").remote.clipboard from the renderer process ? - 0.sh
@0.sh: I have the nodeIntegration setting set to false for security reasons, so I can't use require - Casebash

1 Answers

5
votes

As far as I see context isolation is designed to prevent the very case you described. So if you want to add data to window the best you can do is disable isolation.

However, I looked up the Content Scripts docs referred in BrowserWindow docs at the contextIsolation definition and find a way to use postMessage to get the clipboard text.

main.js

const { app, BrowserWindow } = require('electron')
const path = require('path')

app.once('ready', () => {
  let win = new BrowserWindow({
    webPreferences: {
      nodeIntegration: false,
      contextIsolation: true,
      preload: path.join(__dirname, 'preload.js')
    }
  })
  win.loadURL(path.join(__dirname, 'index.html'))
})

preload.js

const { clipboard } = require('electron')

window.addEventListener("message", (event) => {
  if (event.source != window) return
  if (event.data.type && (event.data.type == "READCLIP_REQ")) {
    window.postMessage({ type: "READCLIP_ANS", text: window.readClipboard() }, "*")
  }
}, false)

window.readClipboard = function(){
  return clipboard.readText()
}

index.html

<html>
  <body>
    <p></p>
    <p></p>
    <script>
      // Try window.readClipboard directly (works with no isolation)
      document.getElementsByTagName("p")[0].innerText =
        window.readClipboard && window.readClipboard()
      // Try the same with postMessage
      const readClipboardMessage = () => {
        window.postMessage({ type: "READCLIP_REQ" }, "*")
      }
      window.addEventListener("message", (event) => {
        if (event.source != window) return
        if (event.data.type && (event.data.type == "READCLIP_ANS")) {
          document.getElementsByTagName("p")[1].innerText = event.data.text
        }
      }, false)
      readClipboardMessage()
    </script>
  </body>
</html>