SimulatedGREG / electron-vue

An Electron & Vue.js quick start boilerplate with vue-cli scaffolding, common Vue plugins, electron-packager/electron-builder, unit/e2e testing, vue-devtools, and webpack.
https://simulatedgreg.gitbooks.io/electron-vue/content/
MIT License
15.47k stars 1.55k forks source link

Electron build does not work without nodeIntegration #644

Open FINDarkside opened 6 years ago

FINDarkside commented 6 years ago

I'm trying to disable nodeIntegration from the renderer process for security purposes.

Describe the issue / bug.

Electron build stops working when nodeIntegration is set to false for the mainWindow (white screen)

How can I reproduce this problem?
$ vue init simulatedgreg/electron-vue my-project

Disable everything optional except axios Delete LandingPage/SystemInformation, remove from LandingPage.vue too Remove require('electron') line from LandingPage.vue Test that web build works ($ npm run build:web) Change mainWindow creation to this:

  mainWindow = new BrowserWindow({
    height: 563,
    width: 1000,
    webPreferences: {
      nodeIntegration: false
    }
  })
  mainWindow.openDevTools();
$ npm run build

Run the program, and you'll get white screen with error

Uncaught ReferenceError: module is not defined

If you remove libraryTarget: 'commonjs2' line from webpack.renderer.js you get the error

Uncaught ReferenceError: axios is not defined

Now if you remove axios, everything will work, but I need axios.

cloverich commented 5 years ago

I've run into this issue on other projects so just throwing a guess out since there's no response. Webpack builds have a "target" (different from libraryTarget) that tell it how to bundle and what to polyfill. E.g. if the target is node (or electron-main), its in a node environment. If its in a browser (e.g. electron-renderer), things like require and module will not be defined. In this case, it sounds like:

If you change the webpack target to electron-renderer, it should fix that.

copitz commented 5 years ago

Maybe its because of that https://github.com/electron/electron/issues/17241#issuecomment-470166582?

Sanjit1 commented 5 years ago

You could set nodeIntegration to true, like shown here

FINDarkside commented 5 years ago

You know, the first line of my issue says:

I'm trying to disable nodeIntegration from the renderer process for security purposes.

So enabling nodeIntegration is obviously not a solution. 😄

Sanjit1 commented 5 years ago

Hmm. That's so silly of me. Have you solved this yet? How about using Browserify

Praveer1981 commented 5 years ago

I've run into this issue on other projects so just throwing a guess out since there's no response. Webpack builds have a "target" (different from libraryTarget) that tell it how to bundle and what to polyfill. E.g. if the target is node (or electron-main), its in a node environment. If its in a browser (e.g. electron-renderer), things like require and module will not be defined. In this case, it sounds like:

  • You remove nodeIntegration, so module and require are no longer avialable
  • The webpack build is still targeting a node environment

If you change the webpack target to electron-renderer, it should fix that.

I also encounter the same issue, even I use the target as electron-renderer. but it didnt solve the issue. In my case I do not get any build error. In fact I get the runtime error Below is error: Uncaught ReferenceError: global is not defined global._babelPolyfill&&"undefined"!=typeof console&&console.warn&&console.warn("@babel/polyfill is loaded more than once on this page. This is probably not desirable/intended and may have consequences if different versions of the polyfills are applied sequentially. If you do need to load the polyfill more than once, use @babel/polyfill/noConflict instead to bypass the warning.")

MutableLoss commented 4 years ago

I just ran into this after upgrading Electron to 7.x, and like yourself, do not like having to add node integration to every single sub-window call.

It looks like there is an experimental BrowserWindow preference nodeIntegrationInSubFrames that could at the very least make it easier in the future, and back to something similar to earlier versions.

reZach commented 4 years ago

@3DEsprit How do you add node integration to every sub-window call?

-edit (SOLUTION) My problem after upgrading to Electron 7.1.7 was that my app.html (main entry point) did not recognize the process variable any longer from the template I'm using (https://github.com/electron-react-boilerplate).

<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <title>My App</title>    
    <script>
      (function() {
        if (!process.env.HOT) {
          const link = document.createElement('link');
          link.rel = 'stylesheet';
          link.href = './dist/style.css';
          // HACK: Writing the script path should be done with webpack
          document.getElementsByTagName('head')[0].appendChild(link);
        }
      }());
    </script>
    <script src="polyfill.js"></script>
  </head>
  <body>
    <div id="root"></div>
    <script>
      {
        const scripts = [];

        // Dynamically insert the DLL script in development env in the
        // renderer process
        if (process.env.NODE_ENV === 'development') {
          scripts.push('../dll/renderer.dev.dll.js');
        }

        // Dynamically insert the bundled app script in the renderer process
        const port = process.env.PORT || 1212;
        scripts.push(
          (process.env.HOT)
            ? 'http://localhost:' + port + '/dist/renderer.dev.js'
            : './dist/renderer.prod.js'
        );

        document.write(
          scripts
            .map(script => `<script defer src="${script}"><\/script>`)
            .join('')
        );
      }
    </script>
  </body>
</html>

This is because, like others says, the BrowserWindow no longer defaults nodeIntegration to true. This property is a security vulnerability, but we (I) still need to use process in my main html file. The solution is to preload a script. Docs here;

preload String (optional) - Specifies a script that will be loaded before other scripts run in the page. This script will always have access to node APIs no matter whether node integration is turned on or off. The value should be the absolute file path to the script. When node integration is turned off, the preload script can reintroduce Node global symbols back to the global scope. See example here.

I create a preload.js script and put this in that file. Note - it's important to not expose properties you don't need, as I imagine that creates more of a surface for an attacker to compromise your app. It also appears process is available without any import statement. I imagine if you want to use fs or some other library call you will need to import it in?

// https://electronjs.org/docs/tutorial/security#2-do-not-enable-nodejs-integration-for-remote-content
window.env = function(){

    // Don't need to expose everything
    return {
        HOT: process.env.HOT,
        NODE_ENV: process.env.NODE_ENV,
        PORT: process.env.PORT
    };
}

And then I updated my main html page to this:

<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <title>My App</title>    
    <script>
      (function() {
        if (!window.env().HOT) {
          const link = document.createElement('link');
          link.rel = 'stylesheet';
          link.href = './dist/style.css';
          // HACK: Writing the script path should be done with webpack
          document.getElementsByTagName('head')[0].appendChild(link);
        }
      }());
    </script>
    <script src="polyfill.js"></script>
  </head>
  <body>
    <div id="root"></div>
    <script>
      {
        const scripts = [];

        // Dynamically insert the DLL script in development env in the
        // renderer process
        if (window.env().NODE_ENV === 'development') {
          scripts.push('../dll/renderer.dev.dll.js');
        }

        // Dynamically insert the bundled app script in the renderer process
        const port = window.env().PORT || 1212;
        scripts.push(
          (window.env().HOT)
            ? 'http://localhost:' + port + '/dist/renderer.dev.js'
            : './dist/renderer.prod.js'
        );

        document.write(
          scripts
            .map(script => `<script defer src="${script}"><\/script>`)
            .join('')
        );
      }
    </script>
  </body>
</html>

and everything works just fine!

--edit 2 other things are breaking, but at least we made some progress.