From d6642b7b16127fd20d2ede104e76e0575a2c32c0 Mon Sep 17 00:00:00 2001 From: Darshan Sen Date: Wed, 3 May 2023 20:41:05 +0530 Subject: [PATCH] test: wrap inject + code signing part into a helper Refs: https://github.com/nodejs/node/pull/47588#discussion_r1182802436 Signed-off-by: Darshan Sen --- test/common/README.md | 21 ++++- test/common/index.js | 42 --------- test/common/sea.js | 92 +++++++++++++++++++ ...cation-disable-experimental-sea-warning.js | 50 +++------- .../test-single-executable-application.js | 47 ++-------- 5 files changed, 128 insertions(+), 124 deletions(-) create mode 100644 test/common/sea.js diff --git a/test/common/README.md b/test/common/README.md index 208ec0ff646d44..603fa3ed232b78 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -420,11 +420,6 @@ will not be run. Logs '1..0 # Skipped: ' + `msg` and exits with exit code `0`. -### `skipIfSingleExecutableIsNotSupported()` - -Skip the rest of the tests if single executable applications are not supported -in the current configuration. - ### `skipIfDumbTerminal()` Skip the rest of the tests if the current terminal is a dumb terminal @@ -996,6 +991,22 @@ Validates the schema of a diagnostic report file whose path is specified in Validates the schema of a diagnostic report whose content is specified in `report`. If the report fails validation, an exception is thrown. +## SEA Module + +The `sea` module provides helper functions for testing Single Executable +Application functionality. + +### `skipIfSingleExecutableIsNotSupported()` + +Skip the rest of the tests if single executable applications are not supported +in the current configuration. + +### `injectAndCodeSign(targetExecutable, resource)` + +Uses Postect to inject the contents of the file at the path `resource` into +the target executable file at the path `targetExecutable` and ultimately code +sign the final binary. + ## tick Module The `tick` module provides a helper function that can be used to call a callback diff --git a/test/common/index.js b/test/common/index.js index 175640b527f354..f3caa9d1d4bdce 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -822,47 +822,6 @@ function invalidArgTypeHelper(input) { return ` Received type ${typeof input} (${inspected})`; } -function skipIfSingleExecutableIsNotSupported() { - if (!process.config.variables.single_executable_application) - skip('Single Executable Application support has been disabled.'); - - if (!['darwin', 'win32', 'linux'].includes(process.platform)) - skip(`Unsupported platform ${process.platform}.`); - - if (process.platform === 'linux' && process.config.variables.is_debug === 1) - skip('Running the resultant binary fails with `Couldn\'t read target executable"`.'); - - if (process.config.variables.node_shared) - skip('Running the resultant binary fails with ' + - '`/home/iojs/node-tmp/.tmp.2366/sea: error while loading shared libraries: ' + - 'libnode.so.112: cannot open shared object file: No such file or directory`.'); - - if (process.config.variables.icu_gyp_path === 'tools/icu/icu-system.gyp') - skip('Running the resultant binary fails with ' + - '`/home/iojs/node-tmp/.tmp.2379/sea: error while loading shared libraries: ' + - 'libicui18n.so.71: cannot open shared object file: No such file or directory`.'); - - if (!process.config.variables.node_use_openssl || process.config.variables.node_shared_openssl) - skip('Running the resultant binary fails with `Node.js is not compiled with OpenSSL crypto support`.'); - - if (process.config.variables.want_separate_host_toolset !== 0) - skip('Running the resultant binary fails with `Segmentation fault (core dumped)`.'); - - if (process.platform === 'linux') { - const osReleaseText = fs.readFileSync('/etc/os-release', { encoding: 'utf-8' }); - const isAlpine = /^NAME="Alpine Linux"/m.test(osReleaseText); - if (isAlpine) skip('Alpine Linux is not supported.'); - - if (process.arch === 's390x') { - skip('On s390x, postject fails with `memory access out of bounds`.'); - } - - if (process.arch === 'ppc64') { - skip('On ppc64, this test times out.'); - } - } -} - function skipIfDumbTerminal() { if (isDumbTerminal) { skip('skipping - dumb terminal'); @@ -985,7 +944,6 @@ const common = { runWithInvalidFD, skip, skipIf32Bits, - skipIfSingleExecutableIsNotSupported, skipIfDumbTerminal, skipIfEslintMissing, skipIfInspectorDisabled, diff --git a/test/common/sea.js b/test/common/sea.js new file mode 100644 index 00000000000000..b2df249cb9fcc3 --- /dev/null +++ b/test/common/sea.js @@ -0,0 +1,92 @@ +'use strict'; + +const common = require('../common'); +const fixtures = require('../common/fixtures'); + +const { readFileSync } = require('fs'); +const { execFileSync } = require('child_process'); + +function skipIfSingleExecutableIsNotSupported() { + if (!process.config.variables.single_executable_application) + common.skip('Single Executable Application support has been disabled.'); + + if (!['darwin', 'win32', 'linux'].includes(process.platform)) + common.skip(`Unsupported platform ${process.platform}.`); + + if (process.platform === 'linux' && process.config.variables.is_debug === 1) + common.skip('Running the resultant binary fails with `Couldn\'t read target executable"`.'); + + if (process.config.variables.node_shared) + common.skip('Running the resultant binary fails with ' + + '`/home/iojs/node-tmp/.tmp.2366/sea: error while loading shared libraries: ' + + 'libnode.so.112: cannot open shared object file: No such file or directory`.'); + + if (process.config.variables.icu_gyp_path === 'tools/icu/icu-system.gyp') + common.skip('Running the resultant binary fails with ' + + '`/home/iojs/node-tmp/.tmp.2379/sea: error while loading shared libraries: ' + + 'libicui18n.so.71: cannot open shared object file: No such file or directory`.'); + + if (!process.config.variables.node_use_openssl || process.config.variables.node_shared_openssl) + common.skip('Running the resultant binary fails with `Node.js is not compiled with OpenSSL crypto support`.'); + + if (process.config.variables.want_separate_host_toolset !== 0) + common.skip('Running the resultant binary fails with `Segmentation fault (core dumped)`.'); + + if (process.platform === 'linux') { + const osReleaseText = readFileSync('/etc/os-release', { encoding: 'utf-8' }); + const isAlpine = /^NAME="Alpine Linux"/m.test(osReleaseText); + if (isAlpine) common.skip('Alpine Linux is not supported.'); + + if (process.arch === 's390x') { + common.skip('On s390x, postject fails with `memory access out of bounds`.'); + } + + if (process.arch === 'ppc64') { + common.skip('On ppc64, this test times out.'); + } + } +} + +function injectAndCodeSign(targetExecutable, resource) { + const postjectFile = fixtures.path('postject-copy', 'node_modules', 'postject', 'dist', 'cli.js'); + execFileSync(process.execPath, [ + postjectFile, + targetExecutable, + 'NODE_SEA_BLOB', + resource, + '--sentinel-fuse', 'NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2', + ...process.platform === 'darwin' ? [ '--macho-segment-name', 'NODE_SEA' ] : [], + ]); + + if (process.platform === 'darwin') { + execFileSync('codesign', [ '--sign', '-', targetExecutable ]); + execFileSync('codesign', [ '--verify', targetExecutable ]); + } else if (process.platform === 'win32') { + let signtoolFound = false; + try { + execFileSync('where', [ 'signtool' ]); + signtoolFound = true; + } catch (err) { + console.log(err.message); + } + if (signtoolFound) { + let certificatesFound = false; + try { + execFileSync('signtool', [ 'sign', '/fd', 'SHA256', targetExecutable ]); + certificatesFound = true; + } catch (err) { + if (!/SignTool Error: No certificates were found that met all the given criteria/.test(err)) { + throw err; + } + } + if (certificatesFound) { + execFileSync('signtool', 'verify', '/pa', 'SHA256', targetExecutable); + } + } + } +} + +module.exports = { + skipIfSingleExecutableIsNotSupported, + injectAndCodeSign, +}; diff --git a/test/sequential/test-single-executable-application-disable-experimental-sea-warning.js b/test/sequential/test-single-executable-application-disable-experimental-sea-warning.js index 641d93e55562d9..a20dce83988228 100644 --- a/test/sequential/test-single-executable-application-disable-experimental-sea-warning.js +++ b/test/sequential/test-single-executable-application-disable-experimental-sea-warning.js @@ -1,9 +1,16 @@ 'use strict'; -const common = require('../common'); -common.skipIfSingleExecutableIsNotSupported(); +require('../common'); -// This tests the creation of a single executable application. +const { + injectAndCodeSign, + skipIfSingleExecutableIsNotSupported, +} = require('../common/sea'); + +skipIfSingleExecutableIsNotSupported(); + +// This tests the creation of a single executable application which has the +// experimental SEA warning disabled. const fixtures = require('../common/fixtures'); const tmpdir = require('../common/tmpdir'); @@ -44,42 +51,7 @@ execFileSync(process.execPath, ['--experimental-sea-config', 'sea-config.json'], assert(existsSync(seaPrepBlob)); copyFileSync(process.execPath, outputFile); -const postjectFile = fixtures.path('postject-copy', 'node_modules', 'postject', 'dist', 'cli.js'); -execFileSync(process.execPath, [ - postjectFile, - outputFile, - 'NODE_SEA_BLOB', - seaPrepBlob, - '--sentinel-fuse', 'NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2', - ...process.platform === 'darwin' ? [ '--macho-segment-name', 'NODE_SEA' ] : [], -]); - -if (process.platform === 'darwin') { - execFileSync('codesign', [ '--sign', '-', outputFile ]); - execFileSync('codesign', [ '--verify', outputFile ]); -} else if (process.platform === 'win32') { - let signtoolFound = false; - try { - execFileSync('where', [ 'signtool' ]); - signtoolFound = true; - } catch (err) { - console.log(err.message); - } - if (signtoolFound) { - let certificatesFound = false; - try { - execFileSync('signtool', [ 'sign', '/fd', 'SHA256', outputFile ]); - certificatesFound = true; - } catch (err) { - if (!/SignTool Error: No certificates were found that met all the given criteria/.test(err)) { - throw err; - } - } - if (certificatesFound) { - execFileSync('signtool', 'verify', '/pa', 'SHA256', outputFile); - } - } -} +injectAndCodeSign(outputFile, seaPrepBlob); const singleExecutableApplicationOutput = execFileSync( outputFile, diff --git a/test/sequential/test-single-executable-application.js b/test/sequential/test-single-executable-application.js index 16c655f14919d6..99d0c0d6e352dc 100644 --- a/test/sequential/test-single-executable-application.js +++ b/test/sequential/test-single-executable-application.js @@ -1,7 +1,13 @@ 'use strict'; -const common = require('../common'); -common.skipIfSingleExecutableIsNotSupported(); +require('../common'); + +const { + injectAndCodeSign, + skipIfSingleExecutableIsNotSupported, +} = require('../common/sea'); + +skipIfSingleExecutableIsNotSupported(); // This tests the creation of a single executable application. @@ -44,42 +50,7 @@ execFileSync(process.execPath, ['--experimental-sea-config', 'sea-config.json'], assert(existsSync(seaPrepBlob)); copyFileSync(process.execPath, outputFile); -const postjectFile = fixtures.path('postject-copy', 'node_modules', 'postject', 'dist', 'cli.js'); -execFileSync(process.execPath, [ - postjectFile, - outputFile, - 'NODE_SEA_BLOB', - seaPrepBlob, - '--sentinel-fuse', 'NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2', - ...process.platform === 'darwin' ? [ '--macho-segment-name', 'NODE_SEA' ] : [], -]); - -if (process.platform === 'darwin') { - execFileSync('codesign', [ '--sign', '-', outputFile ]); - execFileSync('codesign', [ '--verify', outputFile ]); -} else if (process.platform === 'win32') { - let signtoolFound = false; - try { - execFileSync('where', [ 'signtool' ]); - signtoolFound = true; - } catch (err) { - console.log(err.message); - } - if (signtoolFound) { - let certificatesFound = false; - try { - execFileSync('signtool', [ 'sign', '/fd', 'SHA256', outputFile ]); - certificatesFound = true; - } catch (err) { - if (!/SignTool Error: No certificates were found that met all the given criteria/.test(err)) { - throw err; - } - } - if (certificatesFound) { - execFileSync('signtool', 'verify', '/pa', 'SHA256', outputFile); - } - } -} +injectAndCodeSign(outputFile, seaPrepBlob); const singleExecutableApplicationOutput = execFileSync( outputFile,