Skip to content

fix: prevent node mode to be used as script runner by other apps

trop / Backportable? - 26-x-y completed Nov 27, 2023 in 17s

Backport Failed

This PR was checked and could not be automatically backported to "26-x-y" cleanly

Details

Failed Diff:

diff --cc spec/api-autoupdater-darwin-spec.ts
index 28004cf1e1,373ee884b7..0000000000
--- a/spec/api-autoupdater-darwin-spec.ts
+++ b/spec/api-autoupdater-darwin-spec.ts
@@@ -1,22 -1,18 +1,23 @@@
  import { expect } from 'chai';
 -import * as cp from 'node:child_process';
 -import * as http from 'node:http';
 +import * as cp from 'child_process';
 +import * as http from 'http';
  import * as express from 'express';
  import * as fs from 'fs-extra';
++<<<<<<< HEAD
 +import * as os from 'os';
 +import * as path from 'path';
++=======
+ import * as path from 'node:path';
++>>>>>>> fix: prevent node mode to be used as script runner by other apps
  import * as psList from 'ps-list';
 -import { AddressInfo } from 'node:net';
 +import { AddressInfo } from 'net';
  import { ifdescribe, ifit } from './lib/spec-helpers';
+ import { copyApp, getCodesignIdentity, shouldRunCodesignTests, signApp, spawn, withTempDirectory } from './lib/codesign-helpers';
  import * as uuid from 'uuid';
 -import { autoUpdater, systemPreferences } from 'electron';
 +import { systemPreferences } from 'electron';
  
- const features = process._linkedBinding('electron_common_features');
- 
- const fixturesPath = path.resolve(__dirname, 'fixtures');
- 
  // We can only test the auto updater on darwin non-component builds
- ifdescribe(process.platform === 'darwin' && !(process.env.CI && process.arch === 'arm64') && !process.mas && !features.isComponentBuild())('autoUpdater behavior', function () {
+ ifdescribe(shouldRunCodesignTests)('autoUpdater behavior', function () {
    this.timeout(120000);
  
    let identity = '';
@@@ -143,7 -71,13 +74,17 @@@
            appPJPath,
            (await fs.readFile(appPJPath, 'utf8')).replace('1.0.0', version)
          );
++<<<<<<< HEAD
 +        await signApp(secondAppPath);
++=======
+         const infoPath = path.resolve(secondAppPath, 'Contents', 'Info.plist');
+         await fs.writeFile(
+           infoPath,
+           (await fs.readFile(infoPath, 'utf8')).replace(/(<key>CFBundleShortVersionString<\/key>\s+<string>)[^<]+/g, `$1${version}`)
+         );
+         await mutateAppPreSign?.mutate(secondAppPath);
+         await signApp(secondAppPath, identity);
++>>>>>>> fix: prevent node mode to be used as script runner by other apps
          await mutateAppPostSign?.mutate(secondAppPath);
          updateZipPath = path.resolve(dir, 'update.zip');
          await spawn('zip', ['-0', '-r', '--symlinks', updateZipPath, './'], {
@@@ -286,9 -218,15 +227,19 @@@
      }, fn: (appPath: string, zipPath: string) => Promise<void>) => {
        await withTempDirectory(async (dir) => {
          const appPath = await copyApp(dir, opts.startFixture);
++<<<<<<< HEAD
 +        await signApp(appPath);
++=======
+         await opts.mutateAppPreSign?.mutate(appPath);
+         const infoPath = path.resolve(appPath, 'Contents', 'Info.plist');
+         await fs.writeFile(
+           infoPath,
+           (await fs.readFile(infoPath, 'utf8')).replace(/(<key>CFBundleShortVersionString<\/key>\s+<string>)[^<]+/g, '$11.0.0')
+         );
+         await signApp(appPath, identity);
++>>>>>>> fix: prevent node mode to be used as script runner by other apps
  
 -        const updateZipPath = await getOrCreateUpdateZipPath(opts.nextVersion, opts.endFixture, opts.mutateAppPreSign, opts.mutateAppPostSign);
 +        const updateZipPath = await getOrCreateUpdateZipPath(opts.nextVersion, opts.endFixture, opts.mutateAppPostSign);
  
          await fn(appPath, updateZipPath);
        });
diff --cc spec/node-spec.ts
index eac32fa0d7,0af015131a..0000000000
--- a/spec/node-spec.ts
+++ b/spec/node-spec.ts
@@@ -1,14 -1,14 +1,22 @@@
  import { expect } from 'chai';
++<<<<<<< HEAD
 +import * as childProcess from 'child_process';
 +import * as fs from 'fs';
 +import * as path from 'path';
 +import * as util from 'util';
++=======
+ import * as childProcess from 'node:child_process';
+ import * as fs from 'fs-extra';
+ import * as path from 'node:path';
+ import * as util from 'node:util';
++>>>>>>> fix: prevent node mode to be used as script runner by other apps
  import { getRemoteContext, ifdescribe, ifit, itremote, useRemoteContext } from './lib/spec-helpers';
+ import { copyApp, getCodesignIdentity, shouldRunCodesignTests, signApp, spawn, withTempDirectory } from './lib/codesign-helpers';
  import { webContents } from 'electron/main';
 -import { EventEmitter } from 'node:stream';
 -import { once } from 'node:events';
 +import { EventEmitter } from 'stream';
 +import { once } from 'events';
  
 +const features = process._linkedBinding('electron_common_features');
  const mainFixturesPath = path.resolve(__dirname, 'fixtures');
  
  describe('node feature', () => {
@@@ -660,7 -660,67 +668,71 @@@
      });
    });
  
++<<<<<<< HEAD
 +  ifdescribe(features.isRunAsNodeEnabled())('Node.js cli flags', () => {
++=======
+   ifdescribe(shouldRunCodesignTests)('NODE_OPTIONS in signed app', function () {
+     let identity = '';
+ 
+     beforeEach(function () {
+       const result = getCodesignIdentity();
+       if (result === null) {
+         this.skip();
+       } else {
+         identity = result;
+       }
+     });
+ 
+     const script = path.join(fixtures, 'api', 'fork-with-node-options.js');
+     const nodeOptionsWarning = 'NODE_OPTIONS is disabled because this process is invoked by other apps';
+ 
+     it('is disabled when invoked by other apps in ELECTRON_RUN_AS_NODE mode', async () => {
+       await withTempDirectory(async (dir) => {
+         const appPath = await copyApp(dir);
+         await signApp(appPath, identity);
+         // Invoke Electron by using the system node binary as middle layer, so
+         // the check of NODE_OPTIONS will think the process is started by other
+         // apps.
+         const { code, out } = await spawn('node', [script, path.join(appPath, 'Contents/MacOS/Electron')]);
+         expect(code).to.equal(0);
+         expect(out).to.include(nodeOptionsWarning);
+       });
+     });
+ 
+     it('is disabled when invoked by alien binary in app bundle in ELECTRON_RUN_AS_NODE mode', async function () {
+       await withTempDirectory(async (dir) => {
+         const appPath = await copyApp(dir);
+         await signApp(appPath, identity);
+         // Find system node and copy it to app bundle.
+         const nodePath = process.env.PATH?.split(path.delimiter).find(dir => fs.existsSync(path.join(dir, 'node')));
+         if (!nodePath) {
+           this.skip();
+           return;
+         }
+         const alienBinary = path.join(appPath, 'Contents/MacOS/node');
+         await fs.copy(path.join(nodePath, 'node'), alienBinary);
+         // Try to execute electron app from the alien node in app bundle.
+         const { code, out } = await spawn(alienBinary, [script, path.join(appPath, 'Contents/MacOS/Electron')]);
+         expect(code).to.equal(0);
+         expect(out).to.include(nodeOptionsWarning);
+       });
+     });
+ 
+     it('is respected when invoked from self', async () => {
+       await withTempDirectory(async (dir) => {
+         const appPath = await copyApp(dir, null);
+         await signApp(appPath, identity);
+         const appExePath = path.join(appPath, 'Contents/MacOS/Electron');
+         const { code, out } = await spawn(appExePath, [script, appExePath]);
+         expect(code).to.equal(1);
+         expect(out).to.not.include(nodeOptionsWarning);
+         expect(out).to.include('NODE_OPTIONS passed to child');
+       });
+     });
+   });
+ 
+   describe('Node.js cli flags', () => {
++>>>>>>> fix: prevent node mode to be used as script runner by other apps
      let child: childProcess.ChildProcessWithoutNullStreams;
      let exitPromise: Promise<any[]>;
  

Annotations

Check failure on line 9 in spec/api-autoupdater-darwin-spec.ts

See this annotation in the file changed.

@trop trop / Backportable? - 26-x-y

spec/api-autoupdater-darwin-spec.ts#L8-L9

Patch Conflict
Raw output
++<<<<<<< HEAD
 +import * as os from 'os';
 +import * as path from 'path';
++=======
+ import * as path from 'node:path';
++>>>>>>> fix: prevent node mode to be used as script runner by other apps

Check failure on line 77 in spec/api-autoupdater-darwin-spec.ts

See this annotation in the file changed.

@trop trop / Backportable? - 26-x-y

spec/api-autoupdater-darwin-spec.ts#L77

Patch Conflict
Raw output
++<<<<<<< HEAD
 +        await signApp(secondAppPath);
++=======
+         const infoPath = path.resolve(secondAppPath, 'Contents', 'Info.plist');
+         await fs.writeFile(
+           infoPath,
+           (await fs.readFile(infoPath, 'utf8')).replace(/(<key>CFBundleShortVersionString<\/key>\s+<string>)[^<]+/g, `$1${version}`)
+         );
+         await mutateAppPreSign?.mutate(secondAppPath);
+         await signApp(secondAppPath, identity);
++>>>>>>> fix: prevent node mode to be used as script runner by other apps

Check failure on line 230 in spec/api-autoupdater-darwin-spec.ts

See this annotation in the file changed.

@trop trop / Backportable? - 26-x-y

spec/api-autoupdater-darwin-spec.ts#L230

Patch Conflict
Raw output
++<<<<<<< HEAD
 +        await signApp(appPath);
++=======
+         await opts.mutateAppPreSign?.mutate(appPath);
+         const infoPath = path.resolve(appPath, 'Contents', 'Info.plist');
+         await fs.writeFile(
+           infoPath,
+           (await fs.readFile(infoPath, 'utf8')).replace(/(<key>CFBundleShortVersionString<\/key>\s+<string>)[^<]+/g, '$11.0.0')
+         );
+         await signApp(appPath, identity);
++>>>>>>> fix: prevent node mode to be used as script runner by other apps

Check failure on line 5 in spec/node-spec.ts

See this annotation in the file changed.

@trop trop / Backportable? - 26-x-y

spec/node-spec.ts#L2-L5

Patch Conflict
Raw output
++<<<<<<< HEAD
 +import * as childProcess from 'child_process';
 +import * as fs from 'fs';
 +import * as path from 'path';
 +import * as util from 'util';
++=======
+ import * as childProcess from 'node:child_process';
+ import * as fs from 'fs-extra';
+ import * as path from 'node:path';
+ import * as util from 'node:util';
++>>>>>>> fix: prevent node mode to be used as script runner by other apps

Check failure on line 671 in spec/node-spec.ts

See this annotation in the file changed.

@trop trop / Backportable? - 26-x-y

spec/node-spec.ts#L671

Patch Conflict
Raw output
++<<<<<<< HEAD
 +  ifdescribe(features.isRunAsNodeEnabled())('Node.js cli flags', () => {
++=======
+   ifdescribe(shouldRunCodesignTests)('NODE_OPTIONS in signed app', function () {
+     let identity = '';
+ 
+     beforeEach(function () {
+       const result = getCodesignIdentity();
+       if (result === null) {
+         this.skip();
+       } else {
+         identity = result;
+       }
+     });
+ 
+     const script = path.join(fixtures, 'api', 'fork-with-node-options.js');
+     const nodeOptionsWarning = 'NODE_OPTIONS is disabled because this process is invoked by other apps';
+ 
+     it('is disabled when invoked by other apps in ELECTRON_RUN_AS_NODE mode', async () => {
+       await withTempDirectory(async (dir) => {
+         const appPath = await copyApp(dir);
+         await signApp(appPath, identity);
+         // Invoke Electron by using the system node binary as middle layer, so
+         // the check of NODE_OPTIONS will think the process is started by other
+         // apps.
+         const { code, out } = await spawn('node', [script, path.join(appPath, 'Contents/MacOS/Electron')]);
+         expect(code).to.equal(0);
+         expect(out).to.include(nodeOptionsWarning);
+       });
+     });
+ 
+     it('is disabled when invoked by alien binary in app bundle in ELECTRON_RUN_AS_NODE mode', async function () {
+       await withTempDirectory(async (dir) => {
+         const appPath = await copyApp(dir);
+         await signApp(appPath, identity);
+         // Find system node and copy it to app bundle.
+         const nodePath = process.env.PATH?.split(path.delimiter).find(dir => fs.existsSync(path.join(dir, 'node')));
+         if (!nodePath) {
+           this.skip();
+           return;
+         }
+         const alienBinary = path.join(appPath, 'Contents/MacOS/node');
+         await fs.copy(path.join(nodePath, 'node'), alienBinary);
+         // Try to execute electron app from the alien node in app bundle.
+         const { code, out } = await spawn(alienBinary, [script, path.join(appPath, 'Contents/MacOS/Electron')]);
+         expect(code).to.equal(0);
+         expect(out).to.include(nodeOptionsWarning);
+       });
+     });
+ 
+     it('is respected when invoked from self', async () => {
+       await withTempDirectory(async (dir) => {
+         const appPath = await copyApp(dir, null);
+         await signApp(appPath, identity);
+         const appExePath = path.join(appPath, 'Contents/MacOS/Electron');
+         const { code, out } = await spawn(appExePath, [script, appExePath]);
+         expect(code).to.equal(1);
+         expect(out).to.not.include(nodeOptionsWarning);
+         expect(out).to.include('NODE_OPTIONS passed to child');
+       });
+     });
+   });
+ 
+   describe('Node.js cli flags', () => {
++>>>>>>> fix: prevent node mode to be used as script runner by other apps