Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Electron Fuses support #6365

Closed
kaatt opened this issue Oct 22, 2021 · 10 comments
Closed

Electron Fuses support #6365

kaatt opened this issue Oct 22, 2021 · 10 comments

Comments

@kaatt
Copy link

kaatt commented Oct 22, 2021

I'm flipping fuses in my afterPack script. Is there a better way to get the path to the final packaged Electron executable?

const path = require('path')
const { flipFuses, FuseVersion, FuseV1Options } = require('@electron/fuses')

module.exports = async function afterPack(context) {
  const ext = {
    darwin: '.app',
    win32: '.exe',
  }[context.electronPlatformName]

  const electronBinaryPath = path.join(context.appOutDir, context.packager.appInfo.productFilename + ext) // is there a better way?
  await flipFuses(
    electronBinaryPath,
    {
      version: FuseVersion.V1,
      [FuseV1Options.EnableNodeCliInspectArguments]: false,
    },
  )
}
@liode1s
Copy link

liode1s commented Dec 28, 2021

Hi Brother, I also encountered this problem. Electron-builder is an installer, and you cannot run Fuses directly. I obtained the generated folder and packaged it again with other packaging software. I think this function is a very important function. option configure Fuses! This is great!

@stale
Copy link

stale bot commented Apr 16, 2022

Is this still relevant? If so, what is blocking it? Is there anything you can do to help move it forward?

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.

@stale stale bot added the backlog label Apr 16, 2022
@stale stale bot closed this as completed Apr 30, 2022
@kaatt
Copy link
Author

kaatt commented May 29, 2022

Relevant, please reopen.

@mmaietta
Copy link
Collaborator

mmaietta commented Jul 4, 2022

This is how I pull the executable in my work project:

import * as builder from "electron-builder";

const appExecutableDestinationPath = (context: builder.AfterPackContext) => {
    const { packager, appOutDir, electronPlatformName } = context;
    const resourcesDir = packager.getResourcesDir(appOutDir);
    return path.join(resourcesDir, "../", electronPlatformName === "darwin" ? "MacOS" : "");
}

Basically, the Resources folder is always nested one level down, so resolving to the higher directory pulls the folder I need to access the executable. Maybe an approach like that could work for your purposes?

I think in your case it would be for .app or .exe

path.join(resourcesDir, "../", electronPlatformName === "darwin" ? "../" : "");

@Nantris
Copy link

Nantris commented Jul 16, 2022

I don't think #6930 should be closed unless this issue explicitly includes support for EnableEmbeddedAsarIntegrityValidation, which as I understand it will require more substantive changes to electron-builder than the others, which can simply be added with a quick post-build script.

@Nantris
Copy link

Nantris commented Jul 16, 2022

This adapted code from @kaatt's shared code should cover most use cases with reasonable defaults and includes basic Linux workarounds.

// Adapted from https://github.com/electron-userland/electron-builder/issues/6365#issue-1033809141
const afterPack = async context => {
  const ext = {
    darwin: '.app',
    win32: '.exe',
    linux: [''],
  }[context.electronPlatformName];

  const IS_LINUX = context.electronPlatformName === 'linux';
  const executableName = IS_LINUX
    ? context.packager.appInfo.productFilename.toLowerCase().replace('-dev', '')
    : context.packager.appInfo.productFilename; // .toLowerCase() to accomodate Linux file named `name` but productFileName is `Name` -- Replaces '-dev' because on Linux the executable name is `name` even for the DEV builds

  const electronBinaryPath = path.join(
    context.appOutDir,
    `${executableName}${ext}`
  );

  await flipFuses(electronBinaryPath, {
    version: FuseVersion.V1,
    [FuseV1Options.EnableCookieEncryption]: true,
    [FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false,
    [FuseV1Options.EnableNodeCliInspectArguments]: false,
    // [FuseV1Options.EnableEmbeddedAsarIntegrityValidation]: true, // Only affects macOS builds, but breaks them -- https://github.com/electron/fuses/issues/7
    [FuseV1Options.OnlyLoadAppFromAsar]: true,
  });
};

@mmaietta
Copy link
Collaborator

mmaietta commented Jul 21, 2022

Adding on to ☝️ , when building for MacOS Universal distributable and want to flip fuses, you'll need to run flipFuses only on the afterPack of the universal callback stage (added in https://github.com/electron-userland/electron-builder/releases/tag/v23.1.0) as afterPack is called for x64, arm64, and universal stages
You'll need to wrap the code above with:
(note: I use electron-builder.js as a config file to achieve typesafety. More info: https://www.electron.build/api/programmatic-usage)

import * as builder from "electron-builder";
....

afterPack: async (context: builder.AfterPackContext) => {
    if (context.electronPlatformName !== 'darwin' || context.arch === builder.Arch.universal) {
        await addElectronFuses(context)
    }
},

...

// Adapted from https://github.com/electron-userland/electron-builder/issues/6365#issue-1033809141
async function addElectronFuses(context: builder.AfterPackContext) {
    const { appOutDir, packager: { appInfo }, electronPlatformName, arch } = context
    const ext = {
      darwin: '.app',
      win32: '.exe',
      linux: [''],
    }[electronPlatformName];
  
    const electronBinaryPath = path.join(appOutDir, `${appInfo.productFilename}${ext}`);
    console.log('Flipping fuses for: ', electronBinaryPath)

    await flipFuses(electronBinaryPath, {
      version: FuseVersion.V1,
      resetAdHocDarwinSignature: electronPlatformName === 'darwin' && arch === builder.Arch.arm64, // necessary for building on Apple Silicon
      [FuseV1Options.RunAsNode]: false,
      [FuseV1Options.EnableCookieEncryption]: true,
      [FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false,
      [FuseV1Options.EnableNodeCliInspectArguments]: false,
      [FuseV1Options.OnlyLoadAppFromAsar]: true,
    });
  };

@mmaietta mmaietta pinned this issue Jul 21, 2022
@mmaietta
Copy link
Collaborator

mmaietta commented Jul 27, 2022

Hey guys, I investigated further to see if I could integrate Fuses package directly into electron-builder. Got the logic flow but the schema validator fails due to having to use FuseConfig from @electron/fuses https://github.com/electron/fuses/blob/d69eb9cc92b13e77b08bdf3b594635fc0a2c83b8/src/config.ts#L17-L22

This theoretically would allow version overrides since electron-builder schema wouldn't need to have a breaking change when a FuseV2Config eventually comes out. Instead, it'd just be a simple package update and the schema would automatically accept the new version.

Due to the way the package's FuseOptions was written, the schema comes out all weird because it can't handle enums. This is what is generated in the schema

{
          "additionalProperties": false,
          "properties": {
            "0": {
              "type": "boolean"
            },
            "1": {
              "type": "boolean"
            },
            "2": {
              "type": "boolean"
            },
            "3": {
              "type": "boolean"
            },
            "4": {
              "type": "boolean"
            },
            "5": {
              "type": "boolean"
            }
          },
          "type": "object"
        },

Which translates to:

  {
    version: FuseVersion.V1,
    [FuseV1Options.RunAsNode]: false,
    [FuseV1Options.EnableCookieEncryption]: true,
    [FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false,
    [FuseV1Options.EnableNodeCliInspectArguments]: false,
    [FuseV1Options.EnableEmbeddedAsarIntegrityValidation]: true
    [FuseV1Options.OnlyLoadAppFromAsar]: true
  }

version and EnableCookieEncryption overlap values and aren't rendered in the schema validator.

This was the configuration property

  /**
   * *electron frameworks only* Electron fuses configuration. 
   */
  readonly electronFuses?: FuseConfig

The documentation site won't even generate either. 😢

I don't see a way for this to be able to be published. Thoughts are welcome. For now, I'm closing this ticket and suggesting this approach #6365 (comment). Sorry folks

@Nantris
Copy link

Nantris commented Jul 27, 2022

@mmaietta that makes sense but please consider re-opening #6930 which is quite distinct from this issue.

@theogravity
Copy link

theogravity commented Apr 27, 2023

In the example @mmaietta provided, resetAdHocDarwinSignature will always return false since addElectronFuses will only run when the platform is apple and the arch is universal.

These are the settings that worked for me for universal:

// https://github.com/electron-userland/electron-builder/issues/6365
const path = require('path');
const { flipFuses, FuseVersion, FuseV1Options } = require('@electron/fuses');
const builder = require('electron-builder');

// Adapted from https://github.com/electron-userland/electron-builder/issues/6365#issue-1033809141
async function addElectronFuses(context) {
  const { electronPlatformName, arch } = context;

  const ext = {
    darwin: '.app',
    win32: '.exe',
    linux: [''],
  }[electronPlatformName];

  const IS_LINUX = context.electronPlatformName === 'linux';
  const executableName = IS_LINUX
    ? context.packager.appInfo.productFilename.toLowerCase().replace('-dev', '').replace(' ', '-')
    : context.packager.appInfo.productFilename; // .toLowerCase() to accomodate Linux file named `name` but productFileName is `Name` -- Replaces '-dev' because on Linux the executable name is `name` even for the DEV builds

  const electronBinaryPath = path.join(context.appOutDir, `${executableName}${ext}`);

  await flipFuses(electronBinaryPath, {
    version: FuseVersion.V1,
    [FuseV1Options.EnableCookieEncryption]: true,
    resetAdHocDarwinSignature: electronPlatformName === 'darwin' && arch === builder.Arch.universal,
    [FuseV1Options.RunAsNode]: false,
    [FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false,
    [FuseV1Options.EnableNodeCliInspectArguments]: false,
    [FuseV1Options.OnlyLoadAppFromAsar]: true,
    // Mac app crashes when enabled for us on arm, might be fine for you
    [FuseV1Options.LoadBrowserProcessSpecificV8Snapshot]: false,
    // https://github.com/electron/fuses/issues/7
    [FuseV1Options.EnableEmbeddedAsarIntegrityValidation]: false,
  });
}

module.exports = async (context) => {
  if (context.electronPlatformName !== 'darwin' || context.arch === builder.Arch.universal) {
    await addElectronFuses(context);
  }
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants