AfterShotzZHD
AfterShotzZHD

Reputation: 321

Would it be safe to enable nodeIntegration in Electron on a local page that is packaged with the app?

I am stuck when creating custom window controls like close, min/max and restore with nodeIntegration turned off. I created the buttons in my renderer's local html file

main.js

mainWindow = new BrowserWindow({
    x, y, width, height,
    frame: false,
    show: false,
    webPreferences: { devTools: true }
});

mainWindow.loadURL(url.format({
    protocol: 'file:',
    slashes: true,
    pathname: path.join(__dirname, 'assets', 'index.html')
}));

index.html

<div id='minimize' class='noSelect'>&#xE921;</div>
<div id='maximize' class='noSelect'>&#xE922;</div>
<div id='restore' class='noSelect'>&#xE923;</div>
<div id='close' class='noSelect'>&#xE8BB;</div>

<script type='text/javascript' src='../assets/js/index.js'></script>

By default, nodeIntegration is off so index.js has no access to Node. However, I need to be able to add functionality to the buttons to close, min/max and restore the window.

index.js

const { remote } = require('electron');
const mainWindow = remote.getCurrentWindow();

document.getElementById('close').addEventListener('click', () => {
  mainWindow.close();
});

This wouldn't work because of nodeIntegration being disabled. Is it safe to have it enabled in a local page? If not, what is a safe way of doing this?

Upvotes: 16

Views: 30065

Answers (3)

Alexander Leithner
Alexander Leithner

Reputation: 3442

TL;DR: Enabling nodeIntegration only imposes risks if you load and execute code from untrusted sources, i.e. the internet or from user input.

If you are completely sure that your application will only run the code you have created (and no NodeJS module loads scripts from the internet), basically, there is no to very little risk if enabling nodeIntegration.

However, if you allow the user to run code (i.e. input and then eval it) or you provide plug-in APIs from which you do not have any control over the plug-ins loaded, the risk level rises because NodeJS allows any NodeJS script, ex., to manipulate the filesystem.

On the other hand, if you disable nodeIntegration, you have no way of communicating with the main process or manipulating the BrowserWindow, thus cannot create custom window controls. However, you can use a "preload" script file to build a bridge between the completely isolated renderer and the NodeJS world.

This is done by creating a script file which is then passed to the BrowserWindow as the preload: configuration option upon creation. Electron's documentation has some examples to get you started. Also, it's a good idea to familiarise yourself with Eelectron's security recommendations.

Upvotes: 32

KRISHNARAJ S
KRISHNARAJ S

Reputation: 9

you don't need nodeIntegration since it delivers security issues, Instead use preload.js file

Why ?

-> Electron's main process (main.js) is a Node.js environment that has full OS access, renderer process (renderer.js) renders web pages not node.js because anyone can take data and can see the functionalities.

-> To bridge electron's different process types together, we need special script called 'preload.js'

-> preload script are injected before a webpage loads in the renderer.

Upvotes: 0

avalanche1
avalanche1

Reputation: 3592

Keep in mind, that in year 2021 you do not need nodeIntegration to communicate with the main process from the renderer process.

Instead, you use message passing, like this:
main.js

const {app, BrowserWindow, ipcMain} = require("electron");
const path = require("path");

app.whenReady().then(open_window);

function open_window() {
  // Explain: Create app window.
  const appWin = new BrowserWindow({
    width: 800,
    height: 600,
    opacity: 0.5,
    webPreferences: {
      preload: path.join(__dirname, "preload.js"),
    },

  // Explain: Render the app.
  void minisWindow.loadFile("index.html");
  
  // Spec: User can hide App window by clicking the button.
  ipcMain.on("hide-me", () => appWin.minimize());
  
  // Spec-start: When User focuses App window - it becomes fully opaque.
  ipcMain.on("make-window-opaque", () => appWin.setOpacity(1));
  appWin.on("show", () => minisWindow.setOpacity(1));
  appWin.on("blur", () => minisWindow.setOpacity(0.5));
  // Spec-end.
}

preload.js

const {ipcRenderer} = require("electron");

// All of the Node.js APIs are available in the preload process.
// It has the same sandbox as a Chrome extension.
window.addEventListener("DOMContentLoaded", () => {
  // Spec: User can hide App window by clicking the button.
  document.querySelector("#hideBtn").addEventListener("click", 
    () => ipcRenderer.send("hide-me"));
  });

  // Spec: When User focuses App window - it becomes fully opaque.
  document.body.addEventListener("click", () => ipcRenderer.send("make-window-opaque"));
});

This example illustrates two instances of message passing:

  1. When User clicks the #hideBtn button - a message is dispatched that instructs main to hide the window.
  2. By default the window is half-transparent; when User clicks on the window (essentially, activating the clickz event on the body) - a message is dispatched that instructs main to make the window fully opaque.

Upvotes: 9

Related Questions