From 08eeddfa8391fb8c085eb2d23ca155a9ae492d58 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Thu, 23 Jan 2025 20:38:49 -0800 Subject: [PATCH 01/38] test: enforce strict mode in test-zlib-const MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of checking that assignments fail silently in sloppy mode, check that they throw in strict mode. PR-URL: https://github.com/nodejs/node/pull/56689 Reviewed-By: Michaël Zasso Reviewed-By: Luigi Pinca Reviewed-By: James M Snell --- test/parallel/test-zlib-const.js | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/test/parallel/test-zlib-const.js b/test/parallel/test-zlib-const.js index 342c8c712a475b..5b9a127f0eaa02 100644 --- a/test/parallel/test-zlib-const.js +++ b/test/parallel/test-zlib-const.js @@ -1,4 +1,4 @@ -/* eslint-disable strict */ +'use strict'; require('../common'); const assert = require('assert'); @@ -9,27 +9,17 @@ assert.strictEqual(zlib.constants.Z_OK, 0, 'Expected Z_OK to be 0;', `got ${zlib.constants.Z_OK}`, ].join(' ')); -zlib.constants.Z_OK = 1; -assert.strictEqual(zlib.constants.Z_OK, 0, - [ - 'Z_OK should be immutable.', - `Expected to get 0, got ${zlib.constants.Z_OK}`, - ].join(' ')); + +assert.throws(() => { zlib.constants.Z_OK = 1; }, + TypeError, 'zlib.constants.Z_OK should be immutable'); assert.strictEqual(zlib.codes.Z_OK, 0, `Expected Z_OK to be 0; got ${zlib.codes.Z_OK}`); -zlib.codes.Z_OK = 1; -assert.strictEqual(zlib.codes.Z_OK, 0, - [ - 'Z_OK should be immutable.', - `Expected to get 0, got ${zlib.codes.Z_OK}`, - ].join(' ')); -zlib.codes = { Z_OK: 1 }; -assert.strictEqual(zlib.codes.Z_OK, 0, - [ - 'Z_OK should be immutable.', - `Expected to get 0, got ${zlib.codes.Z_OK}`, - ].join(' ')); +assert.throws(() => { zlib.codes.Z_OK = 1; }, + TypeError, 'zlib.codes.Z_OK should be immutable'); + +assert.throws(() => { zlib.codes = { Z_OK: 1 }; }, + TypeError, 'zlib.codes should be immutable'); assert.ok(Object.isFrozen(zlib.codes), [ From e55b02b368d479bdd27eea4aa67ecdfa62ebd7bd Mon Sep 17 00:00:00 2001 From: Aviv Keller Date: Fri, 24 Jan 2025 05:44:05 -0500 Subject: [PATCH 02/38] build: drop support for python 3.8 PR-URL: https://github.com/nodejs/node/pull/55239 Reviewed-By: Christian Clauss --- android-configure | 3 +-- configure | 3 +-- configure.py | 2 +- pyproject.toml | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/android-configure b/android-configure index fb237241d7413d..2558f52757e442 100755 --- a/android-configure +++ b/android-configure @@ -9,7 +9,6 @@ command -v python3.12 >/dev/null && exec python3.12 "$0" "$@" command -v python3.11 >/dev/null && exec python3.11 "$0" "$@" command -v python3.10 >/dev/null && exec python3.10 "$0" "$@" command -v python3.9 >/dev/null && exec python3.9 "$0" "$@" -command -v python3.8 >/dev/null && exec python3.8 "$0" "$@" command -v python3 >/dev/null && exec python3 "$0" "$@" exec python "$0" "$@" ''' "$0" "$@" @@ -23,7 +22,7 @@ except ImportError: from distutils.spawn import find_executable as which print('Node.js android configure: Found Python {}.{}.{}...'.format(*sys.version_info)) -acceptable_pythons = ((3, 13), (3, 12), (3, 11), (3, 10), (3, 9), (3, 8)) +acceptable_pythons = ((3, 13), (3, 12), (3, 11), (3, 10), (3, 9)) if sys.version_info[:2] in acceptable_pythons: import android_configure else: diff --git a/configure b/configure index 56720e8f4c42d9..412f0b416e79c3 100755 --- a/configure +++ b/configure @@ -9,7 +9,6 @@ command -v python3.12 >/dev/null && exec python3.12 "$0" "$@" command -v python3.11 >/dev/null && exec python3.11 "$0" "$@" command -v python3.10 >/dev/null && exec python3.10 "$0" "$@" command -v python3.9 >/dev/null && exec python3.9 "$0" "$@" -command -v python3.8 >/dev/null && exec python3.8 "$0" "$@" command -v python3 >/dev/null && exec python3 "$0" "$@" exec python "$0" "$@" ''' "$0" "$@" @@ -23,7 +22,7 @@ except ImportError: from distutils.spawn import find_executable as which print('Node.js configure: Found Python {}.{}.{}...'.format(*sys.version_info)) -acceptable_pythons = ((3, 13), (3, 12), (3, 11), (3, 10), (3, 9), (3, 8)) +acceptable_pythons = ((3, 13), (3, 12), (3, 11), (3, 10), (3, 9)) if sys.version_info[:2] in acceptable_pythons: import configure else: diff --git a/configure.py b/configure.py index c361676637c1cb..618117370238f7 100755 --- a/configure.py +++ b/configure.py @@ -2143,7 +2143,7 @@ def make_bin_override(): if sys.platform == 'win32': raise Exception('make_bin_override should not be called on win32.') # If the system python is not the python we are running (which should be - # python 3.8+), then create a directory with a symlink called `python` to our + # python 3.9+), then create a directory with a symlink called `python` to our # sys.executable. This directory will be prefixed to the PATH, so that # other tools that shell out to `python` will use the appropriate python diff --git a/pyproject.toml b/pyproject.toml index 45f540bd15170d..43da73beceee7a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ exclude = [ "tools/eslint/node_modules" ] line-length = 172 -target-version = "py38" +target-version = "py39" [tool.ruff.lint] select = [ From 1921371349c15d31b0b6884225a2093f0925a3d7 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Fri, 24 Jan 2025 15:22:12 +0100 Subject: [PATCH 03/38] tools: do not throw on missing `create-release-proposal.sh` PR-URL: https://github.com/nodejs/node/pull/56704 Reviewed-By: James M Snell Reviewed-By: Tierney Cyren Reviewed-By: Rafael Gonzaga --- .github/workflows/create-release-proposal.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/create-release-proposal.yml b/.github/workflows/create-release-proposal.yml index 33426bdcda4a5b..0b580eab81ac76 100644 --- a/.github/workflows/create-release-proposal.yml +++ b/.github/workflows/create-release-proposal.yml @@ -71,13 +71,10 @@ jobs: git config --local user.name "Node.js GitHub Bot" - name: Start git node release prepare - # `git update-index` tells git to ignore future changes to the `.sh` file, - # `|| true` is there to ignore the error if such file doesn't exist yet. # The curl command is to make sure we run the version of the script corresponding to the current workflow. run: | - git update-index --assume-unchanged tools/actions/create-release-proposal.sh || true - curl -fsSLo tools/actions/create-release-proposal.sh https://github.com/${GITHUB_REPOSITORY}/raw/${GITHUB_SHA}/tools/actions/create-release-proposal.sh - ./tools/actions/create-release-proposal.sh "${RELEASE_DATE}" "${RELEASE_LINE}" "${GITHUB_ACTOR}" + curl -fsSL https://github.com/${GITHUB_REPOSITORY}/raw/${GITHUB_SHA}/tools/actions/create-release-proposal.sh |\ + sh -s -- "${RELEASE_DATE}" "${RELEASE_LINE}" "${GITHUB_ACTOR}" env: GH_TOKEN: ${{ github.token }} # We want the bot to push the push the release commit so CI runs on it. From 19fabc0e3175d73e5b4c8acab66dd5424372ff55 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sun, 12 Jan 2025 13:04:40 -0800 Subject: [PATCH 04/38] util: inspect: do not crash on an Error stack that contains a Symbol See #56570 PR-URL: https://github.com/nodejs/node/pull/56573 Reviewed-By: James M Snell Reviewed-By: Luigi Pinca Reviewed-By: Jason Zhang Reviewed-By: Ruben Bridgewater --- lib/internal/util/inspect.js | 14 ++++++++++---- test/parallel/test-util-inspect.js | 20 ++++++++++++++++---- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index 60bee498e5ea8b..f38eecba6ae5fb 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -1287,8 +1287,14 @@ function identicalSequenceRange(a, b) { return { len: 0, offset: 0 }; } -function getStackString(error) { - return error.stack ? String(error.stack) : ErrorPrototypeToString(error); +function getStackString(ctx, error) { + if (error.stack) { + if (typeof error.stack === 'string') { + return error.stack; + } + return formatValue(ctx, error.stack); + } + return ErrorPrototypeToString(error); } function getStackFrames(ctx, err, stack) { @@ -1303,7 +1309,7 @@ function getStackFrames(ctx, err, stack) { // Remove stack frames identical to frames in cause. if (cause != null && isError(cause)) { - const causeStack = getStackString(cause); + const causeStack = getStackString(ctx, cause); const causeStackStart = StringPrototypeIndexOf(causeStack, '\n at'); if (causeStackStart !== -1) { const causeFrames = StringPrototypeSplit(StringPrototypeSlice(causeStack, causeStackStart + 1), '\n'); @@ -1426,7 +1432,7 @@ function safeGetCWD() { function formatError(err, constructor, tag, ctx, keys) { const name = err.name != null ? err.name : 'Error'; - let stack = getStackString(err); + let stack = getStackString(ctx, err); removeDuplicateErrorKeys(ctx, keys, err, stack); diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index 0b04e3b8dc7179..87d92369b8deca 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -777,16 +777,18 @@ assert.strictEqual(util.inspect(-5e-324), '-5e-324'); [undefined, 'RangeError: foo', '[RangeError: foo]'], [false, 'false [RangeError]: foo', '[RangeError: foo]'], ['', 'foo', '[RangeError: foo]'], - [[1, 2, 3], '1,2,3 [RangeError]: foo', '[1,2,3]'], + [[1, 2, 3], '1,2,3 [RangeError]: foo', '[[\n 1,\n 2,\n 3\n]]'], ].forEach(([value, outputStart, stack]) => { let err = new RangeError('foo'); err.name = value; + const result = util.inspect(err); assert( - util.inspect(err).startsWith(outputStart), + result.startsWith(outputStart), util.format( - 'The name set to %o did not result in the expected output "%s"', + 'The name set to %o did not result in the expected output "%s", got "%s"', value, - outputStart + outputStart, + result.split('\n')[0] ) ); @@ -3448,3 +3450,13 @@ assert.strictEqual( ${error.stack.split('\n').slice(1).join('\n')}`, ); } + +{ + const error = new Error(); + error.stack = [Symbol('foo')]; + + assert.strictEqual( + inspect(error), + '[[\n Symbol(foo)\n]]' + ); +} From 01a5aa2ac10c051336e8ad4062b87f73a4d7824e Mon Sep 17 00:00:00 2001 From: Jonas Date: Fri, 24 Jan 2025 11:22:54 -0500 Subject: [PATCH 05/38] test: add missing test for env file PR-URL: https://github.com/nodejs/node/pull/56642 Reviewed-By: Yagiz Nizipli Reviewed-By: Luigi Pinca Reviewed-By: Jake Yuesong Li Reviewed-By: Matteo Collina Reviewed-By: James M Snell --- test/parallel/test-dotenv-edge-cases.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/parallel/test-dotenv-edge-cases.js b/test/parallel/test-dotenv-edge-cases.js index 769d33a13b8ce9..926c8d0793ac8b 100644 --- a/test/parallel/test-dotenv-edge-cases.js +++ b/test/parallel/test-dotenv-edge-cases.js @@ -170,4 +170,16 @@ describe('.env supports edge cases', () => { assert.strictEqual(SingleQuotesChild.stderr, ''); assert.strictEqual(SingleQuotesChild.code, 0); }); + + it('should reject invalid env file flag', async () => { + const child = await common.spawnPromisified( + process.execPath, + ['--env-file-ABCD', validEnvFilePath], + { cwd: __dirname }, + ); + + assert.strictEqual(child.stdout, ''); + assert.strictEqual(child.code, 9); + assert.match(child.stderr, /bad option: --env-file-ABCD/); + }); }); From c752615e2bbea73ec59de0439c997d334577fdae Mon Sep 17 00:00:00 2001 From: Pietro Marchini Date: Sun, 19 Jan 2025 23:57:49 +0100 Subject: [PATCH 06/38] test_runner: print failing assertion only once with spec reporter Co-authored-by: Llorx PR-URL: https://github.com/nodejs/node/pull/56662 Reviewed-By: Chengzhong Wu Reviewed-By: Colin Ihrig Reviewed-By: James M Snell --- lib/internal/test_runner/reporter/spec.js | 2 +- lib/internal/test_runner/reporter/utils.js | 6 +- .../output/assertion-color-tty.snapshot | 12 - .../output/default_output.snapshot | 23 -- .../test-runner/output/eval_spec.snapshot | 9 - .../output/hooks_spec_reporter.snapshot | 311 ------------------ .../test-runner/output/spec_reporter.snapshot | 212 ------------ .../output/spec_reporter_cli.snapshot | 212 ------------ 8 files changed, 5 insertions(+), 782 deletions(-) diff --git a/lib/internal/test_runner/reporter/spec.js b/lib/internal/test_runner/reporter/spec.js index 2092d22e3fe77f..caee92a7ba13e1 100644 --- a/lib/internal/test_runner/reporter/spec.js +++ b/lib/internal/test_runner/reporter/spec.js @@ -52,7 +52,7 @@ class SpecReporter extends Transform { hasChildren = true; } const indentation = indent(data.nesting); - return `${formatTestReport(type, data, prefix, indentation, hasChildren)}\n`; + return `${formatTestReport(type, data, prefix, indentation, hasChildren, false)}\n`; } #handleEvent({ type, data }) { switch (type) { diff --git a/lib/internal/test_runner/reporter/utils.js b/lib/internal/test_runner/reporter/utils.js index 9b6cc96a185a9a..256619039e8e90 100644 --- a/lib/internal/test_runner/reporter/utils.js +++ b/lib/internal/test_runner/reporter/utils.js @@ -59,7 +59,7 @@ function formatError(error, indent) { return `\n${indent} ${message}\n`; } -function formatTestReport(type, data, prefix = '', indent = '', hasChildren = false) { +function formatTestReport(type, data, prefix = '', indent = '', hasChildren = false, showErrorDetails = true) { let color = reporterColorMap[type] ?? colors.white; let symbol = reporterUnicodeSymbolMap[type] ?? ' '; const { skip, todo } = data; @@ -71,10 +71,12 @@ function formatTestReport(type, data, prefix = '', indent = '', hasChildren = fa } else if (todo !== undefined) { title += ` # ${typeof todo === 'string' && todo.length ? todo : 'TODO'}`; } - const error = formatError(data.details?.error, indent); + + const error = showErrorDetails ? formatError(data.details?.error, indent) : ''; const err = hasChildren ? (!error || data.details?.error?.failureType === 'subtestsFailed' ? '' : `\n${error}`) : error; + if (skip !== undefined) { color = colors.gray; symbol = reporterUnicodeSymbolMap['hyphen:minus']; diff --git a/test/fixtures/test-runner/output/assertion-color-tty.snapshot b/test/fixtures/test-runner/output/assertion-color-tty.snapshot index 2909d909351743..a74016febc5df4 100644 --- a/test/fixtures/test-runner/output/assertion-color-tty.snapshot +++ b/test/fixtures/test-runner/output/assertion-color-tty.snapshot @@ -1,16 +1,4 @@ [31m✖ failing assertion [90m(*ms)[39m[39m - [AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: - [32mactual[39m [31mexpected[39m - - [39m'[39m[32m![39m[39mH[39m[39me[39m[39ml[39m[39ml[39m[39mo[39m[39m [39m[39mW[39m[39mo[39m[39mr[39m[39ml[39m[39md[39m[31m![39m[39m'[39m - ] { - generatedMessage: [33mtrue[39m, - code: [32m'ERR_ASSERTION'[39m, - actual: [32m'!Hello World'[39m, - expected: [32m'Hello World!'[39m, - operator: [32m'strictEqual'[39m - } - [34mℹ tests 1[39m [34mℹ suites 0[39m [34mℹ pass 0[39m diff --git a/test/fixtures/test-runner/output/default_output.snapshot b/test/fixtures/test-runner/output/default_output.snapshot index 73bfc1da5e92e9..d0a83395733924 100644 --- a/test/fixtures/test-runner/output/default_output.snapshot +++ b/test/fixtures/test-runner/output/default_output.snapshot @@ -1,32 +1,9 @@ [32m✔ should pass [90m(*ms)[39m[39m [31m✖ should fail [90m(*ms)[39m[39m - Error: fail - *[39m - *[39m - *[39m - *[39m - *[39m - *[39m - *[39m - [90m﹣ should skip [90m(*ms)[39m # SKIP[39m ▶ parent [31m✖ should fail [90m(*ms)[39m[39m - Error: fail - *[39m - *[39m - *[39m - *[39m - *[39m - *[39m - *[39m - *[39m - *[39m - *[39m - [31m✖ should pass but parent fail [90m(*ms)[39m[39m - [32m'test did not finish before its parent and was cancelled'[39m - [31m✖ parent [90m(*ms)[39m[39m [34mℹ tests 6[39m [34mℹ suites 0[39m diff --git a/test/fixtures/test-runner/output/eval_spec.snapshot b/test/fixtures/test-runner/output/eval_spec.snapshot index 5c9a53009508e1..116c23ccf97077 100644 --- a/test/fixtures/test-runner/output/eval_spec.snapshot +++ b/test/fixtures/test-runner/output/eval_spec.snapshot @@ -1,14 +1,5 @@ ✔ passes (*ms) ✖ fails (*ms) - Error: fail - * - * - * - * - * - * - * - ℹ tests 2 ℹ suites 0 ℹ pass 1 diff --git a/test/fixtures/test-runner/output/hooks_spec_reporter.snapshot b/test/fixtures/test-runner/output/hooks_spec_reporter.snapshot index ea916c2ee754c4..8c267672b9a951 100644 --- a/test/fixtures/test-runner/output/hooks_spec_reporter.snapshot +++ b/test/fixtures/test-runner/output/hooks_spec_reporter.snapshot @@ -10,161 +10,29 @@ describe hooks - no subtests (*ms) before throws 1 - 'test did not finish before its parent and was cancelled' - 2 - 'test did not finish before its parent and was cancelled' - before throws (*ms) - - Error: before - * - * - * - * - * - * - * - * - before throws - no subtests (*ms) - Error: before - * - * - * - * - * - * - * - * - after throws 1 (*ms) 2 (*ms) after throws (*ms) - - Error: after - * - * - * - * - * - * - * - * - * - * - after throws - no subtests (*ms) - Error: after - * - * - * - * - * - * - * - * - * - * - beforeEach throws 1 (*ms) - Error: beforeEach - * - * - * - * - * - * - * - * - * - at new Promise () - 2 (*ms) - Error: beforeEach - * - * - * - * - * - * - * - * - * - at async Promise.all (index 0) - beforeEach throws (*ms) afterEach throws 1 (*ms) - Error: afterEach - * - * - * - * - * - * - * - * - at async Promise.all (index 0) - * - 2 (*ms) - Error: afterEach - * - * - * - * - * - * - * - * - * - afterEach throws (*ms) afterEach when test fails 1 (*ms) - Error: test - * - * - * - * - * - * - at new Promise () - * - * - at Array.map () - 2 (*ms) afterEach when test fails (*ms) afterEach throws and test fails 1 (*ms) - Error: test - * - * - * - * - * - * - at new Promise () - * - * - at Array.map () - 2 (*ms) - Error: afterEach - * - * - * - * - * - * - * - * - * - afterEach throws and test fails (*ms) test hooks 1 (*ms) @@ -177,155 +45,24 @@ test hooks - no subtests (*ms) t.before throws 1 (*ms) - Error: before - * - * - * - * - * - * - * - * - * - * - 2 (*ms) - Error: before - * - * - * - * - * - * - * - * - * - * - t.before throws (*ms) - - Error: before - * - * - * - * - * - * - * - * - * - * - t.before throws - no subtests (*ms) - Error: before - * - * - * - * - * - * - * - * - * - * - t.after throws 1 (*ms) 2 (*ms) t.after throws (*ms) - - Error: after - * - * - * - * - * - * - * - * - * - t.after throws - no subtests (*ms) - Error: after - * - * - * - * - * - * - * - * - * - t.beforeEach throws 1 (*ms) - Error: beforeEach - * - * - * - * - * - * - * - * - * - * - 2 (*ms) - Error: beforeEach - * - * - * - * - * - * - * - * - * - * - t.beforeEach throws (*ms) t.afterEach throws 1 (*ms) - Error: afterEach - * - * - * - * - * - * - * - * - * - * - 2 (*ms) - Error: afterEach - * - * - * - * - * - * - * - * - * - * - t.afterEach throws (*ms) afterEach when test fails 1 (*ms) - Error: test - * - * - * - * - * - * - * - * - * - 2 (*ms) afterEach when test fails (*ms) afterEach context when test passes @@ -333,64 +70,16 @@ afterEach context when test passes (*ms) afterEach context when test fails 1 (*ms) - Error: test - * - * - * - * - afterEach context when test fails (*ms) afterEach throws and test fails 1 (*ms) - Error: test - * - * - * - * - * - * - * - * - * - 2 (*ms) - Error: afterEach - * - * - * - * - * - * - * - * - * - * - afterEach throws and test fails (*ms) t.after() is called if test body throws (*ms) - Error: bye - * - * - * - * - - after() called run after when before throws 1 - 'test did not finish before its parent and was cancelled' - run after when before throws (*ms) - - Error: before - * - * - * - * - * - * - * - * - test hooks - async 1 (*ms) 2 (*ms) diff --git a/test/fixtures/test-runner/output/spec_reporter.snapshot b/test/fixtures/test-runner/output/spec_reporter.snapshot index f3aebbd7fe5aae..1892069327f92d 100644 --- a/test/fixtures/test-runner/output/spec_reporter.snapshot +++ b/test/fixtures/test-runner/output/spec_reporter.snapshot @@ -1,91 +1,19 @@ sync pass todo (*ms) # TODO sync pass todo with message (*ms) # this is a passing todo sync fail todo (*ms) # TODO - Error: thrown from sync fail todo - * - * - * - * - * - * - * - sync fail todo with message (*ms) # this is a failing todo - Error: thrown from sync fail todo with message - * - * - * - * - * - * - * - sync skip pass (*ms) # SKIP sync skip pass with message (*ms) # this is skipped sync pass (*ms) this test should pass sync throw fail (*ms) - Error: thrown from sync throw fail - * - * - * - * - * - * - * - async skip pass (*ms) # SKIP async pass (*ms) async throw fail (*ms) - Error: thrown from async throw fail - * - * - * - * - * - * - * - async skip fail (*ms) # SKIP - Error: thrown from async throw fail - * - * - * - * - * - * - * - async assertion fail (*ms) - AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: - - true !== false - - * - * - * - * - * - * - * { - generatedMessage: true, - code: 'ERR_ASSERTION', - actual: true, - expected: false, - operator: 'strictEqual' - } - resolve pass (*ms) reject fail (*ms) - Error: rejected from reject fail - * - * - * - * - * - * - * - unhandled rejection - passes but warns (*ms) async unhandled rejection - passes but warns (*ms) immediate throw - passes but warns (*ms) @@ -93,23 +21,9 @@ immediate resolve pass (*ms) subtest sync throw fail +sync throw fail (*ms) - Error: thrown from subtest sync throw fail - * - * - * - * - * - * - * - * - * - * - this subtest should make its parent test fail subtest sync throw fail (*ms) sync throw non-error fail (*ms) - Symbol(thrown symbol from sync throw non-error fail) - level 0a level 1a (*ms) level 1b (*ms) @@ -118,8 +32,6 @@ level 0a (*ms) top level +long running (*ms) - 'test did not finish before its parent and was cancelled' - +short running ++short running (*ms) +short running (*ms) @@ -128,15 +40,6 @@ sync skip option (*ms) # SKIP sync skip option with message (*ms) # this is skipped sync skip option is false fail (*ms) - Error: this should be executed - * - * - * - * - * - * - * - (*ms) functionOnly (*ms) (*ms) @@ -147,43 +50,15 @@ functionAndOptions (*ms) # SKIP callback pass (*ms) callback fail (*ms) - Error: callback failure - * - * - sync t is this in test (*ms) async t is this in test (*ms) callback t is this in test (*ms) callback also returns a Promise (*ms) - 'passed a callback but also returned a Promise' - callback throw (*ms) - Error: thrown from callback throw - * - * - * - * - * - * - * - callback called twice (*ms) - 'callback invoked multiple times' - callback called twice in different ticks (*ms) callback called twice in future tick (*ms) - Error [ERR_TEST_FAILURE]: callback invoked multiple times - * { - code: 'ERR_TEST_FAILURE', - failureType: 'multipleCallbackInvocations', - cause: 'callback invoked multiple times' - } - callback async throw (*ms) - Error: thrown from callback async throw - * - * - callback async throw after done (*ms) only is set on subtests but not in only mode running subtest 1 (*ms) @@ -191,108 +66,21 @@ running subtest 4 (*ms) only is set on subtests but not in only mode (*ms) custom inspect symbol fail (*ms) - customized - custom inspect symbol that throws fail (*ms) - { foo: 1, Symbol(nodejs.util.inspect.custom): [Function: [nodejs.util.inspect.custom]] } - subtest sync throw fails sync throw fails at first (*ms) - Error: thrown from subtest sync throw fails at first - * - * - * - * - * - * - * - * - * - * - sync throw fails at second (*ms) - Error: thrown from subtest sync throw fails at second - * - * - * - * - * - * - * - * - subtest sync throw fails (*ms) timed out async test (*ms) - 'test timed out after *ms' - timed out callback test (*ms) - 'test timed out after *ms' - large timeout async test is ok (*ms) large timeout callback test is ok (*ms) successful thenable (*ms) rejected thenable (*ms) - 'custom error' - unfinished test with uncaughtException (*ms) - Error: foo - * - * - * - unfinished test with unhandledRejection (*ms) - Error: bar - * - * - * - assertion errors display actual and expected properly (*ms) - AssertionError [ERR_ASSERTION]: Expected values to be loosely deep-equal: - - { - bar: 1, - baz: { - date: 1970-01-01T00:00:00.000Z, - null: null, - number: 1, - string: 'Hello', - undefined: undefined - }, - boo: [ - 1 - ], - foo: 1 - } - - should loosely deep-equal - - { - baz: { - date: 1970-01-01T00:00:00.000Z, - null: null, - number: 1, - string: 'Hello', - undefined: undefined - }, - boo: [ - 1 - ], - circular: { - bar: 2, - c: [Circular *1] - } - } - * { - generatedMessage: true, - code: 'ERR_ASSERTION', - actual: [Object], - expected: [Object], - operator: 'deepEqual' - } - invalid subtest fail (*ms) - 'test could not be started because its parent finished' - Error: Test "unhandled rejection - passes but warns" at test/fixtures/test-runner/output/output.js:72:1 generated asynchronous activity after the test ended. This activity created the error "Error: rejected from unhandled rejection fail" and would have caused the test to fail, but instead triggered an unhandledRejection event. Error: Test "async unhandled rejection - passes but warns" at test/fixtures/test-runner/output/output.js:76:1 generated asynchronous activity after the test ended. This activity created the error "Error: rejected from async unhandled rejection fail" and would have caused the test to fail, but instead triggered an unhandledRejection event. Error: A resource generated asynchronous activity after the test ended. This activity created the error "Error: uncaught from outside of a test" which triggered an uncaughtException event, caught by the test runner. diff --git a/test/fixtures/test-runner/output/spec_reporter_cli.snapshot b/test/fixtures/test-runner/output/spec_reporter_cli.snapshot index 2e5f263e1a5e3a..52dc40bb366e2c 100644 --- a/test/fixtures/test-runner/output/spec_reporter_cli.snapshot +++ b/test/fixtures/test-runner/output/spec_reporter_cli.snapshot @@ -1,91 +1,19 @@ sync pass todo (*ms) # TODO sync pass todo with message (*ms) # this is a passing todo sync fail todo (*ms) # TODO - Error: thrown from sync fail todo - * - * - * - * - * - * - * - sync fail todo with message (*ms) # this is a failing todo - Error: thrown from sync fail todo with message - * - * - * - * - * - * - * - sync skip pass (*ms) # SKIP sync skip pass with message (*ms) # this is skipped sync pass (*ms) this test should pass sync throw fail (*ms) - Error: thrown from sync throw fail - * - * - * - * - * - * - * - async skip pass (*ms) # SKIP async pass (*ms) async throw fail (*ms) - Error: thrown from async throw fail - * - * - * - * - * - * - * - async skip fail (*ms) # SKIP - Error: thrown from async throw fail - * - * - * - * - * - * - * - async assertion fail (*ms) - AssertionError [ERR_ASSERTION]: Expected values to be strictly equal: - - true !== false - - * - * - * - * - * - * - * { - generatedMessage: true, - code: 'ERR_ASSERTION', - actual: true, - expected: false, - operator: 'strictEqual' - } - resolve pass (*ms) reject fail (*ms) - Error: rejected from reject fail - * - * - * - * - * - * - * - unhandled rejection - passes but warns (*ms) async unhandled rejection - passes but warns (*ms) immediate throw - passes but warns (*ms) @@ -93,23 +21,9 @@ immediate resolve pass (*ms) subtest sync throw fail +sync throw fail (*ms) - Error: thrown from subtest sync throw fail - * - * - * - * - * - * - * - * - * - * - this subtest should make its parent test fail subtest sync throw fail (*ms) sync throw non-error fail (*ms) - Symbol(thrown symbol from sync throw non-error fail) - level 0a level 1a (*ms) level 1b (*ms) @@ -118,8 +32,6 @@ level 0a (*ms) top level +long running (*ms) - 'test did not finish before its parent and was cancelled' - +short running ++short running (*ms) +short running (*ms) @@ -128,15 +40,6 @@ sync skip option (*ms) # SKIP sync skip option with message (*ms) # this is skipped sync skip option is false fail (*ms) - Error: this should be executed - * - * - * - * - * - * - * - (*ms) functionOnly (*ms) (*ms) @@ -147,43 +50,15 @@ functionAndOptions (*ms) # SKIP callback pass (*ms) callback fail (*ms) - Error: callback failure - * - * - sync t is this in test (*ms) async t is this in test (*ms) callback t is this in test (*ms) callback also returns a Promise (*ms) - 'passed a callback but also returned a Promise' - callback throw (*ms) - Error: thrown from callback throw - * - * - * - * - * - * - * - callback called twice (*ms) - 'callback invoked multiple times' - callback called twice in different ticks (*ms) callback called twice in future tick (*ms) - Error [ERR_TEST_FAILURE]: callback invoked multiple times - * { - code: 'ERR_TEST_FAILURE', - failureType: 'multipleCallbackInvocations', - cause: 'callback invoked multiple times' - } - callback async throw (*ms) - Error: thrown from callback async throw - * - * - callback async throw after done (*ms) only is set on subtests but not in only mode running subtest 1 (*ms) @@ -194,108 +69,21 @@ running subtest 4 (*ms) only is set on subtests but not in only mode (*ms) custom inspect symbol fail (*ms) - customized - custom inspect symbol that throws fail (*ms) - { foo: 1 } - subtest sync throw fails sync throw fails at first (*ms) - Error: thrown from subtest sync throw fails at first - * - * - * - * - * - * - * - * - * - * - sync throw fails at second (*ms) - Error: thrown from subtest sync throw fails at second - * - * - * - * - * - * - * - * - subtest sync throw fails (*ms) timed out async test (*ms) - 'test timed out after *ms' - timed out callback test (*ms) - 'test timed out after *ms' - large timeout async test is ok (*ms) large timeout callback test is ok (*ms) successful thenable (*ms) rejected thenable (*ms) - 'custom error' - unfinished test with uncaughtException (*ms) - Error: foo - * - * - * - unfinished test with unhandledRejection (*ms) - Error: bar - * - * - * - assertion errors display actual and expected properly (*ms) - AssertionError [ERR_ASSERTION]: Expected values to be loosely deep-equal: - - { - bar: 1, - baz: { - date: 1970-01-01T00:00:00.000Z, - null: null, - number: 1, - string: 'Hello', - undefined: undefined - }, - boo: [ - 1 - ], - foo: 1 - } - - should loosely deep-equal - - { - baz: { - date: 1970-01-01T00:00:00.000Z, - null: null, - number: 1, - string: 'Hello', - undefined: undefined - }, - boo: [ - 1 - ], - circular: { - bar: 2, - c: [Circular *1] - } - } - * { - generatedMessage: true, - code: 'ERR_ASSERTION', - actual: { foo: 1, bar: 1, boo: [ 1 ], baz: { date: 1970-01-01T00:00:00.000Z, null: null, number: 1, string: 'Hello', undefined: undefined } }, - expected: { boo: [ 1 ], baz: { date: 1970-01-01T00:00:00.000Z, null: null, number: 1, string: 'Hello', undefined: undefined }, circular: { bar: 2, c: [Circular *1] } }, - operator: 'deepEqual' - } - invalid subtest fail (*ms) - 'test could not be started because its parent finished' - Error: Test "unhandled rejection - passes but warns" at test/fixtures/test-runner/output/output.js:72:1 generated asynchronous activity after the test ended. This activity created the error "Error: rejected from unhandled rejection fail" and would have caused the test to fail, but instead triggered an unhandledRejection event. Error: Test "async unhandled rejection - passes but warns" at test/fixtures/test-runner/output/output.js:76:1 generated asynchronous activity after the test ended. This activity created the error "Error: rejected from async unhandled rejection fail" and would have caused the test to fail, but instead triggered an unhandledRejection event. Error: A resource generated asynchronous activity after the test ended. This activity created the error "Error: uncaught from outside of a test" which triggered an uncaughtException event, caught by the test runner. From 761de815c5cce3fb67f0d14ecabc5397497285ff Mon Sep 17 00:00:00 2001 From: James M Snell Date: Fri, 24 Jan 2025 16:58:32 -0800 Subject: [PATCH 07/38] test: move crypto related common utilities in common/crypto Since `common/crypto` already exists, it makes sense to keep crypto-related utilities there. The only exception being common.hasCrypto which is needed up front to determine if tests should be skipped. Eliminate the redundant check in hasFipsCrypto and just use crypto.getFips() directly where needed. PR-URL: https://github.com/nodejs/node/pull/56714 Reviewed-By: Yagiz Nizipli Reviewed-By: Luigi Pinca --- test/addons/openssl-providers/providers.cjs | 7 ++- test/benchmark/test-benchmark-crypto.js | 5 +- test/common/README.md | 17 ------ test/common/crypto.js | 52 ++++++++++++++++++- test/common/index.js | 52 ------------------- test/common/index.mjs | 2 - test/parallel/test-cli-node-options.js | 3 +- test/parallel/test-crypto-authenticated.js | 20 ++++--- .../test-crypto-cipheriv-decipheriv.js | 10 ++-- test/parallel/test-crypto-classes.js | 6 +-- test/parallel/test-crypto-dh-constructor.js | 3 +- test/parallel/test-crypto-dh-errors.js | 3 +- test/parallel/test-crypto-dh-generate-keys.js | 3 +- test/parallel/test-crypto-dh-leak.js | 3 +- test/parallel/test-crypto-dh-odd-key.js | 8 +-- test/parallel/test-crypto-dh-stateless.js | 7 +-- test/parallel/test-crypto-dh.js | 17 +++--- test/parallel/test-crypto-ecb.js | 13 +++-- test/parallel/test-crypto-fips.js | 5 +- test/parallel/test-crypto-hash.js | 7 +-- test/parallel/test-crypto-hkdf.js | 5 +- test/parallel/test-crypto-hmac.js | 5 +- test/parallel/test-crypto-key-objects.js | 10 ++-- ...test-crypto-keygen-async-dsa-key-object.js | 8 +-- test/parallel/test-crypto-keygen-async-dsa.js | 8 +-- ...-explicit-elliptic-curve-encrypted-p256.js | 4 +- ...nc-explicit-elliptic-curve-encrypted.js.js | 3 +- ...ync-named-elliptic-curve-encrypted-p256.js | 3 +- ...en-async-named-elliptic-curve-encrypted.js | 3 +- test/parallel/test-crypto-keygen-async-rsa.js | 3 +- .../parallel/test-crypto-keygen-bit-length.js | 3 +- ...rypto-keygen-empty-passphrase-no-prompt.js | 3 +- .../test-crypto-keygen-missing-oid.js | 4 +- test/parallel/test-crypto-keygen.js | 3 +- test/parallel/test-crypto-no-algorithm.js | 4 +- test/parallel/test-crypto-oneshot-hash.js | 3 +- test/parallel/test-crypto-padding.js | 5 +- test/parallel/test-crypto-pbkdf2.js | 3 +- .../test-crypto-private-decrypt-gh32240.js | 4 +- ...t-crypto-publicDecrypt-fails-first-time.js | 8 ++- test/parallel/test-crypto-rsa-dsa.js | 7 +-- test/parallel/test-crypto-secure-heap.js | 13 +++-- test/parallel/test-crypto-sign-verify.js | 13 +++-- test/parallel/test-crypto-stream.js | 8 +-- test/parallel/test-crypto-x509.js | 5 +- test/parallel/test-crypto.js | 7 +-- test/parallel/test-dsa-fips-invalid-key.js | 10 +++- .../test-https-agent-session-eviction.js | 9 ++-- .../test-https-client-renegotiation-limit.js | 8 ++- test/parallel/test-https-foafssl.js | 10 ++-- ...rocess-env-allowed-flags-are-documented.js | 7 +-- test/parallel/test-process-versions.js | 3 +- test/parallel/test-tls-alert-handling.js | 20 ++++--- test/parallel/test-tls-alert.js | 15 ++++-- test/parallel/test-tls-alpn-server-client.js | 5 +- test/parallel/test-tls-cert-ext-encoding.js | 4 +- test/parallel/test-tls-client-auth.js | 7 ++- .../test-tls-client-getephemeralkeyinfo.js | 3 +- test/parallel/test-tls-client-mindhsize.js | 5 +- .../test-tls-client-renegotiation-13.js | 8 ++- .../test-tls-client-renegotiation-limit.js | 8 ++- test/parallel/test-tls-dhe.js | 17 ++++-- test/parallel/test-tls-ecdh-auto.js | 10 ++-- test/parallel/test-tls-ecdh-multiple.js | 14 +++-- test/parallel/test-tls-ecdh.js | 10 ++-- test/parallel/test-tls-empty-sni-context.js | 4 +- test/parallel/test-tls-getprotocol.js | 6 ++- test/parallel/test-tls-junk-server.js | 7 ++- test/parallel/test-tls-key-mismatch.js | 6 ++- test/parallel/test-tls-legacy-pfx.js | 9 +++- test/parallel/test-tls-min-max-version.js | 16 ++++-- test/parallel/test-tls-no-sslv3.js | 10 ++-- test/parallel/test-tls-ocsp-callback.js | 15 ++++-- test/parallel/test-tls-psk-circuit.js | 10 ++-- test/parallel/test-tls-psk-server.js | 11 ++-- test/parallel/test-tls-securepair-server.js | 10 ++-- test/parallel/test-tls-server-verify.js | 10 ++-- test/parallel/test-tls-session-cache.js | 20 ++++--- test/parallel/test-tls-set-ciphers.js | 16 ++++-- test/parallel/test-tls-set-secure-context.js | 6 ++- test/parallel/test-tls-set-sigalgs.js | 7 ++- test/parallel/test-trace-env.js | 8 +-- test/parallel/test-x509-escaping.js | 5 +- test/pummel/test-crypto-dh-hash.js | 4 +- test/pummel/test-crypto-dh-keys.js | 3 +- test/pummel/test-dh-regr.js | 3 +- test/sequential/test-tls-psk-client.js | 11 ++-- test/sequential/test-tls-securepair-client.js | 25 +++++---- test/sequential/test-tls-session-timeout.js | 10 ++-- 89 files changed, 505 insertions(+), 288 deletions(-) diff --git a/test/addons/openssl-providers/providers.cjs b/test/addons/openssl-providers/providers.cjs index 2dabbf020e2a41..efa1019c62d99c 100644 --- a/test/addons/openssl-providers/providers.cjs +++ b/test/addons/openssl-providers/providers.cjs @@ -1,11 +1,14 @@ 'use strict'; const common = require('../../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} +const { hasOpenSSL3 } = require('../../common/crypto'); -if (!common.hasOpenSSL3) +if (!hasOpenSSL3) { common.skip('this test requires OpenSSL 3.x'); +} const assert = require('node:assert'); const { createHash, getCiphers, getHashes } = require('node:crypto'); const { debuglog } = require('node:util'); diff --git a/test/benchmark/test-benchmark-crypto.js b/test/benchmark/test-benchmark-crypto.js index 7f6988acf234d8..72d79ece13e787 100644 --- a/test/benchmark/test-benchmark-crypto.js +++ b/test/benchmark/test-benchmark-crypto.js @@ -5,8 +5,11 @@ const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); -if (common.hasFipsCrypto) +const { getFips } = require('crypto'); + +if (getFips()) { common.skip('some benchmarks are FIPS-incompatible'); +} const runBenchmark = require('../common/benchmark'); diff --git a/test/common/README.md b/test/common/README.md index 5f5ff75fca2431..ee36503f920001 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -226,17 +226,6 @@ The TTY file descriptor is assumed to be capable of being writable. Indicates whether OpenSSL is available. -### `hasFipsCrypto` - -* [\][] - -Indicates that Node.js has been linked with a FIPS compatible OpenSSL library, -and that FIPS as been enabled using `--enable-fips`. - -To only detect if the OpenSSL library is FIPS compatible, regardless if it has -been enabled or not, then `process.config.variables.openssl_is_fips` can be -used to determine that situation. - ### `hasIntl` * [\][] @@ -417,12 +406,6 @@ Returns `true` if the exit code `exitCode` and/or signal name `signal` represent the exit code and/or signal name of a node process that aborted, `false` otherwise. -### `opensslCli` - -* [\][] - -Indicates whether 'opensslCli' is supported. - ### `platformTimeout(ms)` * `ms` [\][] | [\][] diff --git a/test/common/crypto.js b/test/common/crypto.js index 10432d7e7a7e32..f50d3895a1783b 100644 --- a/test/common/crypto.js +++ b/test/common/crypto.js @@ -1,8 +1,9 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} const assert = require('assert'); const crypto = require('crypto'); @@ -98,6 +99,27 @@ const pkcs8EncExp = getRegExpForPEM('ENCRYPTED PRIVATE KEY'); const sec1Exp = getRegExpForPEM('EC PRIVATE KEY'); const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher); +// Synthesize OPENSSL_VERSION_NUMBER format with the layout 0xMNN00PPSL +const opensslVersionNumber = (major = 0, minor = 0, patch = 0) => { + assert(major >= 0 && major <= 0xf); + assert(minor >= 0 && minor <= 0xff); + assert(patch >= 0 && patch <= 0xff); + return (major << 28) | (minor << 20) | (patch << 4); +}; + +let OPENSSL_VERSION_NUMBER; +const hasOpenSSL = (major = 0, minor = 0, patch = 0) => { + if (!common.hasCrypto) return false; + if (OPENSSL_VERSION_NUMBER === undefined) { + const regexp = /(?\d+)\.(?\d+)\.(?

\d+)/; + const { m, n, p } = process.versions.openssl.match(regexp).groups; + OPENSSL_VERSION_NUMBER = opensslVersionNumber(m, n, p); + } + return OPENSSL_VERSION_NUMBER >= opensslVersionNumber(major, minor, patch); +}; + +let opensslCli = null; + module.exports = { modp2buf, assertApproximateSize, @@ -111,4 +133,32 @@ module.exports = { pkcs8EncExp, // used once sec1Exp, sec1EncExp, + hasOpenSSL, + get hasOpenSSL3() { + return hasOpenSSL(3); + }, + // opensslCli defined lazily to reduce overhead of spawnSync + get opensslCli() { + if (opensslCli !== null) return opensslCli; + + if (process.config.variables.node_shared_openssl) { + // Use external command + opensslCli = 'openssl'; + } else { + const path = require('path'); + // Use command built from sources included in Node.js repository + opensslCli = path.join(path.dirname(process.execPath), 'openssl-cli'); + } + + if (exports.isWindows) opensslCli += '.exe'; + + const { spawnSync } = require('child_process'); + + const opensslCmd = spawnSync(opensslCli, ['version']); + if (opensslCmd.status !== 0 || opensslCmd.error !== undefined) { + // OpenSSL command cannot be executed + opensslCli = false; + } + return opensslCli; + }, }; diff --git a/test/common/index.js b/test/common/index.js index b5592a66a081c3..d2c39578324600 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -19,7 +19,6 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -/* eslint-disable node-core/crypto-check */ 'use strict'; const process = global.process; // Some tests tamper with the process global. @@ -57,25 +56,6 @@ const noop = () => {}; const hasCrypto = Boolean(process.versions.openssl) && !process.env.NODE_SKIP_CRYPTO; -// Synthesize OPENSSL_VERSION_NUMBER format with the layout 0xMNN00PPSL -const opensslVersionNumber = (major = 0, minor = 0, patch = 0) => { - assert(major >= 0 && major <= 0xf); - assert(minor >= 0 && minor <= 0xff); - assert(patch >= 0 && patch <= 0xff); - return (major << 28) | (minor << 20) | (patch << 4); -}; - -let OPENSSL_VERSION_NUMBER; -const hasOpenSSL = (major = 0, minor = 0, patch = 0) => { - if (!hasCrypto) return false; - if (OPENSSL_VERSION_NUMBER === undefined) { - const regexp = /(?\d+)\.(?\d+)\.(?

\d+)/; - const { m, n, p } = process.versions.openssl.match(regexp).groups; - OPENSSL_VERSION_NUMBER = opensslVersionNumber(m, n, p); - } - return OPENSSL_VERSION_NUMBER >= opensslVersionNumber(major, minor, patch); -}; - const hasQuic = hasCrypto && !!process.config.variables.openssl_quic; function parseTestFlags(filename = process.argv[1]) { @@ -220,7 +200,6 @@ if (process.env.NODE_TEST_WITH_ASYNC_HOOKS) { }).enable(); } -let opensslCli = null; let inFreeBSDJail = null; let localhostIPv4 = null; @@ -985,7 +964,6 @@ const common = { getTTYfd, hasIntl, hasCrypto, - hasOpenSSL, hasQuic, hasMultiLocalhost, invalidArgTypeHelper, @@ -1027,10 +1005,6 @@ const common = { return require('os').totalmem() > 0x70000000; /* 1.75 Gb */ }, - get hasFipsCrypto() { - return hasCrypto && require('crypto').getFips(); - }, - get hasIPv6() { const iFaces = require('os').networkInterfaces(); let re; @@ -1047,10 +1021,6 @@ const common = { }); }, - get hasOpenSSL3() { - return hasOpenSSL(3); - }, - get inFreeBSDJail() { if (inFreeBSDJail !== null) return inFreeBSDJail; @@ -1100,28 +1070,6 @@ const common = { return localhostIPv4; }, - // opensslCli defined lazily to reduce overhead of spawnSync - get opensslCli() { - if (opensslCli !== null) return opensslCli; - - if (process.config.variables.node_shared_openssl) { - // Use external command - opensslCli = 'openssl'; - } else { - // Use command built from sources included in Node.js repository - opensslCli = path.join(path.dirname(process.execPath), 'openssl-cli'); - } - - if (exports.isWindows) opensslCli += '.exe'; - - const opensslCmd = spawnSync(opensslCli, ['version']); - if (opensslCmd.status !== 0 || opensslCmd.error !== undefined) { - // OpenSSL command cannot be executed - opensslCli = false; - } - return opensslCli; - }, - get PORT() { if (+process.env.TEST_PARALLEL) { throw new Error('common.PORT cannot be used in a parallelized test'); diff --git a/test/common/index.mjs b/test/common/index.mjs index b252f2dc4aac5e..23328ac90ea3c9 100644 --- a/test/common/index.mjs +++ b/test/common/index.mjs @@ -41,7 +41,6 @@ const { mustNotMutateObjectDeep, mustSucceed, nodeProcessAborted, - opensslCli, parseTestFlags, PIPE, platformTimeout, @@ -97,7 +96,6 @@ export { mustNotMutateObjectDeep, mustSucceed, nodeProcessAborted, - opensslCli, parseTestFlags, PIPE, platformTimeout, diff --git a/test/parallel/test-cli-node-options.js b/test/parallel/test-cli-node-options.js index 69bf136559c1a8..9e89200e9f6dfd 100644 --- a/test/parallel/test-cli-node-options.js +++ b/test/parallel/test-cli-node-options.js @@ -12,6 +12,7 @@ const { Worker } = require('worker_threads'); const fixtures = require('../common/fixtures'); const tmpdir = require('../common/tmpdir'); +const { hasOpenSSL3 } = require('../common/crypto'); tmpdir.refresh(); const printA = path.relative(tmpdir.path, fixtures.path('printA.js')); @@ -64,7 +65,7 @@ if (common.isLinux) { if (common.hasCrypto) { expectNoWorker('--use-openssl-ca', 'B\n'); expectNoWorker('--use-bundled-ca', 'B\n'); - if (!common.hasOpenSSL3) + if (!hasOpenSSL3) expectNoWorker('--openssl-config=_ossl_cfg', 'B\n'); } diff --git a/test/parallel/test-crypto-authenticated.js b/test/parallel/test-crypto-authenticated.js index d191ab7be2de20..181ea732b91281 100644 --- a/test/parallel/test-crypto-authenticated.js +++ b/test/parallel/test-crypto-authenticated.js @@ -21,13 +21,17 @@ // Flags: --no-warnings 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} const assert = require('assert'); const crypto = require('crypto'); const { inspect } = require('util'); const fixtures = require('../common/fixtures'); +const { hasOpenSSL3 } = require('../common/crypto'); + +const isFipsEnabled = crypto.getFips(); // // Test authenticated encryption modes. @@ -53,7 +57,7 @@ for (const test of TEST_CASES) { continue; } - if (common.hasFipsCrypto && test.iv.length < 24) { + if (isFipsEnabled && test.iv.length < 24) { common.printSkipMessage('IV len < 12 bytes unsupported in FIPS mode'); continue; } @@ -95,7 +99,7 @@ for (const test of TEST_CASES) { } { - if (isCCM && common.hasFipsCrypto) { + if (isCCM && isFipsEnabled) { assert.throws(() => { crypto.createDecipheriv(test.algo, Buffer.from(test.key, 'hex'), @@ -286,7 +290,7 @@ for (const test of TEST_CASES) { }); }, errMessages.authTagLength); - if (!common.hasFipsCrypto) { + if (!isFipsEnabled) { assert.throws(() => { crypto.createDecipheriv('aes-256-ccm', 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', @@ -312,7 +316,7 @@ for (const test of TEST_CASES) { }); // CCM decryption and create(De|C)ipher are unsupported in FIPS mode. - if (!common.hasFipsCrypto) { + if (!isFipsEnabled) { assert.throws(() => { crypto.createDecipheriv(`aes-256-${mode}`, 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', @@ -388,7 +392,7 @@ for (const test of TEST_CASES) { cipher.setAAD(Buffer.from('0123456789', 'hex')); }, /options\.plaintextLength required for CCM mode with AAD/); - if (!common.hasFipsCrypto) { + if (!isFipsEnabled) { assert.throws(() => { const cipher = crypto.createDecipheriv('aes-256-ccm', 'FxLKsqdmv0E9xrQhp0b1ZgI0K7JFZJM8', @@ -403,7 +407,7 @@ for (const test of TEST_CASES) { // Test that final() throws in CCM mode when no authentication tag is provided. { - if (!common.hasFipsCrypto) { + if (!isFipsEnabled) { const key = Buffer.from('1ed2233fa2223ef5d7df08546049406c', 'hex'); const iv = Buffer.from('7305220bca40d4c90e1791e9', 'hex'); const ct = Buffer.from('8beba09d4d4d861f957d51c0794f4abf8030848e', 'hex'); @@ -562,7 +566,7 @@ for (const test of TEST_CASES) { ]) { assert.throws(() => { cipher.final(); - }, common.hasOpenSSL3 ? { + }, hasOpenSSL3 ? { code: 'ERR_OSSL_TAG_NOT_SET' } : { message: /Unsupported state/ diff --git a/test/parallel/test-crypto-cipheriv-decipheriv.js b/test/parallel/test-crypto-cipheriv-decipheriv.js index 3e3632203af72c..88d07c3b957f57 100644 --- a/test/parallel/test-crypto-cipheriv-decipheriv.js +++ b/test/parallel/test-crypto-cipheriv-decipheriv.js @@ -5,6 +5,8 @@ if (!common.hasCrypto) const assert = require('assert'); const crypto = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); +const isFipsEnabled = crypto.getFips(); function testCipher1(key, iv) { // Test encryption and decryption with explicit key and iv @@ -150,7 +152,7 @@ testCipher1(Buffer.from('0123456789abcd0123456789'), '12345678'); testCipher1(Buffer.from('0123456789abcd0123456789'), Buffer.from('12345678')); testCipher2(Buffer.from('0123456789abcd0123456789'), Buffer.from('12345678')); -if (!common.hasFipsCrypto) { +if (!isFipsEnabled) { testCipher3(Buffer.from('000102030405060708090A0B0C0D0E0F', 'hex'), Buffer.from('A6A6A6A6A6A6A6A6', 'hex')); } @@ -193,10 +195,10 @@ assert.throws( errMessage); // But all other IV lengths should be accepted. -const minIvLength = common.hasOpenSSL3 ? 8 : 1; -const maxIvLength = common.hasOpenSSL3 ? 64 : 256; +const minIvLength = hasOpenSSL3 ? 8 : 1; +const maxIvLength = hasOpenSSL3 ? 64 : 256; for (let n = minIvLength; n < maxIvLength; n += 1) { - if (common.hasFipsCrypto && n < 12) continue; + if (isFipsEnabled && n < 12) continue; crypto.createCipheriv('aes-128-gcm', Buffer.alloc(16), Buffer.alloc(n)); } diff --git a/test/parallel/test-crypto-classes.js b/test/parallel/test-crypto-classes.js index f736921476a1c5..429bc91d4412be 100644 --- a/test/parallel/test-crypto-classes.js +++ b/test/parallel/test-crypto-classes.js @@ -4,9 +4,9 @@ const assert = require('assert'); if (!common.hasCrypto) { common.skip('missing crypto'); - return; } const crypto = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); // 'ClassName' : ['args', 'for', 'constructor'] const TEST_CASES = { @@ -21,8 +21,8 @@ const TEST_CASES = { 'ECDH': ['prime256v1'], }; -if (!common.hasFipsCrypto) { - TEST_CASES.DiffieHellman = [common.hasOpenSSL3 ? 1024 : 256]; +if (!crypto.getFips()) { + TEST_CASES.DiffieHellman = [hasOpenSSL3 ? 1024 : 256]; } for (const [clazz, args] of Object.entries(TEST_CASES)) { diff --git a/test/parallel/test-crypto-dh-constructor.js b/test/parallel/test-crypto-dh-constructor.js index c7eaca29347a2b..eb8674932484ed 100644 --- a/test/parallel/test-crypto-dh-constructor.js +++ b/test/parallel/test-crypto-dh-constructor.js @@ -5,8 +5,9 @@ if (!common.hasCrypto) const assert = require('assert'); const crypto = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); -const size = common.hasFipsCrypto || common.hasOpenSSL3 ? 1024 : 256; +const size = crypto.getFips() || hasOpenSSL3 ? 1024 : 256; const dh1 = crypto.createDiffieHellman(size); const p1 = dh1.getPrime('buffer'); diff --git a/test/parallel/test-crypto-dh-errors.js b/test/parallel/test-crypto-dh-errors.js index 476ca64b4425b5..0af4db0310750c 100644 --- a/test/parallel/test-crypto-dh-errors.js +++ b/test/parallel/test-crypto-dh-errors.js @@ -5,6 +5,7 @@ if (!common.hasCrypto) const assert = require('assert'); const crypto = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); // https://github.com/nodejs/node/issues/32738 // XXX(bnoordhuis) validateInt32() throwing ERR_OUT_OF_RANGE and RangeError @@ -24,7 +25,7 @@ assert.throws(() => crypto.createDiffieHellman('abcdef', 13.37), { }); for (const bits of [-1, 0, 1]) { - if (common.hasOpenSSL3) { + if (hasOpenSSL3) { assert.throws(() => crypto.createDiffieHellman(bits), { code: 'ERR_OSSL_DH_MODULUS_TOO_SMALL', name: 'Error', diff --git a/test/parallel/test-crypto-dh-generate-keys.js b/test/parallel/test-crypto-dh-generate-keys.js index fc277bb0d9b8e4..e4598274328bd8 100644 --- a/test/parallel/test-crypto-dh-generate-keys.js +++ b/test/parallel/test-crypto-dh-generate-keys.js @@ -6,9 +6,10 @@ if (!common.hasCrypto) const assert = require('assert'); const crypto = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); { - const size = common.hasFipsCrypto || common.hasOpenSSL3 ? 1024 : 256; + const size = crypto.getFips() || hasOpenSSL3 ? 1024 : 256; function unlessInvalidState(f) { try { diff --git a/test/parallel/test-crypto-dh-leak.js b/test/parallel/test-crypto-dh-leak.js index 1998d61d4affd7..3b5051feb43cd8 100644 --- a/test/parallel/test-crypto-dh-leak.js +++ b/test/parallel/test-crypto-dh-leak.js @@ -9,10 +9,11 @@ if (common.isASan) const assert = require('assert'); const crypto = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); const before = process.memoryUsage.rss(); { - const size = common.hasFipsCrypto || common.hasOpenSSL3 ? 1024 : 256; + const size = crypto.getFips() || hasOpenSSL3 ? 1024 : 256; const dh = crypto.createDiffieHellman(size); const publicKey = dh.generateKeys(); const privateKey = dh.getPrivateKey(); diff --git a/test/parallel/test-crypto-dh-odd-key.js b/test/parallel/test-crypto-dh-odd-key.js index 69a1eb56c866b3..fbe42be425ed1c 100644 --- a/test/parallel/test-crypto-dh-odd-key.js +++ b/test/parallel/test-crypto-dh-odd-key.js @@ -21,22 +21,24 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} const assert = require('assert'); const crypto = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); function test() { const odd = Buffer.alloc(39, 'A'); - const c = crypto.createDiffieHellman(common.hasOpenSSL3 ? 1024 : 32); + const c = crypto.createDiffieHellman(hasOpenSSL3 ? 1024 : 32); c.setPrivateKey(odd); c.generateKeys(); } // FIPS requires a length of at least 1024 -if (!common.hasFipsCrypto) { +if (!crypto.getFips()) { test(); } else { assert.throws(function() { test(); }, /key size too small/); diff --git a/test/parallel/test-crypto-dh-stateless.js b/test/parallel/test-crypto-dh-stateless.js index 2ccac322e23958..f4fc1849699e31 100644 --- a/test/parallel/test-crypto-dh-stateless.js +++ b/test/parallel/test-crypto-dh-stateless.js @@ -5,6 +5,7 @@ if (!common.hasCrypto) const assert = require('assert'); const crypto = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); assert.throws(() => crypto.diffieHellman(), { name: 'TypeError', @@ -150,7 +151,7 @@ const list = [ // TODO(danbev): Take a closer look if there should be a check in OpenSSL3 // when the dh parameters differ. -if (!common.hasOpenSSL3) { +if (!hasOpenSSL3) { // Same primes, but different generator. list.push([{ group: 'modp5' }, { prime: group.getPrime(), generator: 5 }]); // Same generator, but different primes. @@ -161,7 +162,7 @@ for (const [params1, params2] of list) { assert.throws(() => { test(crypto.generateKeyPairSync('dh', params1), crypto.generateKeyPairSync('dh', params2)); - }, common.hasOpenSSL3 ? { + }, hasOpenSSL3 ? { name: 'Error', code: 'ERR_OSSL_MISMATCHING_DOMAIN_PARAMETERS' } : { @@ -220,7 +221,7 @@ const not256k1 = crypto.getCurves().find((c) => /^sec.*(224|384|512)/.test(c)); assert.throws(() => { test(crypto.generateKeyPairSync('ec', { namedCurve: 'secp256k1' }), crypto.generateKeyPairSync('ec', { namedCurve: not256k1 })); -}, common.hasOpenSSL3 ? { +}, hasOpenSSL3 ? { name: 'Error', code: 'ERR_OSSL_MISMATCHING_DOMAIN_PARAMETERS' } : { diff --git a/test/parallel/test-crypto-dh.js b/test/parallel/test-crypto-dh.js index 9ebe14011eed22..d7ffbe5eca9273 100644 --- a/test/parallel/test-crypto-dh.js +++ b/test/parallel/test-crypto-dh.js @@ -1,13 +1,18 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} const assert = require('assert'); const crypto = require('crypto'); +const { + hasOpenSSL3, + hasOpenSSL, +} = require('../common/crypto'); { - const size = common.hasFipsCrypto || common.hasOpenSSL3 ? 1024 : 256; + const size = crypto.getFips() || hasOpenSSL3 ? 1024 : 256; const dh1 = crypto.createDiffieHellman(size); const p1 = dh1.getPrime('buffer'); const dh2 = crypto.createDiffieHellman(p1, 'buffer'); @@ -53,7 +58,7 @@ const crypto = require('crypto'); assert.strictEqual(secret1, secret4); let wrongBlockLength; - if (common.hasOpenSSL3) { + if (hasOpenSSL3) { wrongBlockLength = { message: 'error:1C80006B:Provider routines::wrong final block length', code: 'ERR_OSSL_WRONG_FINAL_BLOCK_LENGTH', @@ -87,11 +92,11 @@ const crypto = require('crypto'); { // Error message was changed in OpenSSL 3.0.x from 3.0.12, and 3.1.x from 3.1.4. - const hasOpenSSL3WithNewErrorMessage = (common.hasOpenSSL(3, 0, 12) && !common.hasOpenSSL(3, 1, 0)) || - (common.hasOpenSSL(3, 1, 4)); + const hasOpenSSL3WithNewErrorMessage = (hasOpenSSL(3, 0, 12) && !hasOpenSSL(3, 1, 0)) || + (hasOpenSSL(3, 1, 4)); assert.throws(() => { dh3.computeSecret(''); - }, { message: common.hasOpenSSL3 && !hasOpenSSL3WithNewErrorMessage ? + }, { message: hasOpenSSL3 && !hasOpenSSL3WithNewErrorMessage ? 'Unspecified validation error' : 'Supplied key is too small' }); } diff --git a/test/parallel/test-crypto-ecb.js b/test/parallel/test-crypto-ecb.js index aecd858ef3bf1e..6439c9354a059e 100644 --- a/test/parallel/test-crypto-ecb.js +++ b/test/parallel/test-crypto-ecb.js @@ -21,18 +21,23 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} + +const { hasOpenSSL3 } = require('../common/crypto'); +const crypto = require('crypto'); -if (common.hasFipsCrypto) +if (crypto.getFips()) { common.skip('BF-ECB is not FIPS 140-2 compatible'); +} -if (common.hasOpenSSL3) +if (hasOpenSSL3) { common.skip('Blowfish is only available with the legacy provider in ' + 'OpenSSl 3.x'); +} const assert = require('assert'); -const crypto = require('crypto'); // Testing whether EVP_CipherInit_ex is functioning correctly. // Reference: bug#1997 diff --git a/test/parallel/test-crypto-fips.js b/test/parallel/test-crypto-fips.js index 8a8a8089a3cf3b..de004b9a9e8f23 100644 --- a/test/parallel/test-crypto-fips.js +++ b/test/parallel/test-crypto-fips.js @@ -10,6 +10,7 @@ const path = require('path'); const fixtures = require('../common/fixtures'); const { internalBinding } = require('internal/test/binding'); const { testFipsCrypto } = internalBinding('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); const FIPS_ENABLED = 1; const FIPS_DISABLED = 0; @@ -114,7 +115,7 @@ assert.ok(test_result === 1 || test_result === 0); // ("Error: Cannot set FIPS mode in a non-FIPS build."). // Due to this uncertainty the following tests are skipped when configured // with --shared-openssl. -if (!sharedOpenSSL() && !common.hasOpenSSL3) { +if (!sharedOpenSSL() && !hasOpenSSL3) { // OpenSSL config file should be able to turn on FIPS mode testHelper( 'stdout', @@ -144,7 +145,7 @@ if (!sharedOpenSSL() && !common.hasOpenSSL3) { // will not work as expected with that version. // TODO(danbev) Revisit these test once FIPS support is available in // OpenSSL 3.x. -if (!common.hasOpenSSL3) { +if (!hasOpenSSL3) { testHelper( 'stdout', [`--openssl-config=${CNF_FIPS_OFF}`], diff --git a/test/parallel/test-crypto-hash.js b/test/parallel/test-crypto-hash.js index ca8f630b4bb7e7..61145aee0727fb 100644 --- a/test/parallel/test-crypto-hash.js +++ b/test/parallel/test-crypto-hash.js @@ -1,13 +1,14 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} const assert = require('assert'); const crypto = require('crypto'); const fs = require('fs'); -const { hasOpenSSL } = common; +const { hasOpenSSL } = require('../common/crypto'); const fixtures = require('../common/fixtures'); let cryptoType; @@ -40,7 +41,7 @@ a8.write(''); a8.end(); a8 = a8.read(); -if (!common.hasFipsCrypto) { +if (!crypto.getFips()) { cryptoType = 'md5'; digest = 'latin1'; const a0 = crypto.createHash(cryptoType).update('Test123').digest(digest); diff --git a/test/parallel/test-crypto-hkdf.js b/test/parallel/test-crypto-hkdf.js index ff3abdf291efcd..3f7e61e9b2ebc0 100644 --- a/test/parallel/test-crypto-hkdf.js +++ b/test/parallel/test-crypto-hkdf.js @@ -13,6 +13,7 @@ const { hkdfSync, getHashes } = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); { assert.throws(() => hkdf(), { @@ -124,7 +125,7 @@ const algorithms = [ ['sha256', '', 'salt', '', 10], ['sha512', 'secret', 'salt', '', 15], ]; -if (!common.hasOpenSSL3) +if (!hasOpenSSL3) algorithms.push(['whirlpool', 'secret', '', 'info', 20]); algorithms.forEach(([ hash, secret, salt, info, length ]) => { @@ -215,7 +216,7 @@ algorithms.forEach(([ hash, secret, salt, info, length ]) => { }); -if (!common.hasOpenSSL3) { +if (!hasOpenSSL3) { const kKnownUnsupported = ['shake128', 'shake256']; getHashes() .filter((hash) => !kKnownUnsupported.includes(hash)) diff --git a/test/parallel/test-crypto-hmac.js b/test/parallel/test-crypto-hmac.js index 62a6ac18d25265..afb5f74cbf076b 100644 --- a/test/parallel/test-crypto-hmac.js +++ b/test/parallel/test-crypto-hmac.js @@ -1,7 +1,8 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} const assert = require('assert'); const crypto = require('crypto'); @@ -40,7 +41,7 @@ assert.throws( function testHmac(algo, key, data, expected) { // FIPS does not support MD5. - if (common.hasFipsCrypto && algo === 'md5') + if (crypto.getFips() && algo === 'md5') return; if (!Array.isArray(data)) diff --git a/test/parallel/test-crypto-key-objects.js b/test/parallel/test-crypto-key-objects.js index f5271f16d346c0..0c516d80950694 100644 --- a/test/parallel/test-crypto-key-objects.js +++ b/test/parallel/test-crypto-key-objects.js @@ -24,6 +24,8 @@ const { generateKeyPairSync, } = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); + const fixtures = require('../common/fixtures'); const publicPem = fixtures.readKey('rsa_public.pem', 'ascii'); @@ -297,7 +299,7 @@ const privateDsa = fixtures.readKey('dsa_private_encrypted_1025.pem', // This should not cause a crash: https://github.com/nodejs/node/issues/25247 assert.throws(() => { createPrivateKey({ key: '' }); - }, common.hasOpenSSL3 ? { + }, hasOpenSSL3 ? { message: 'error:1E08010C:DECODER routines::unsupported', } : { message: 'error:0909006C:PEM routines:get_name:no start line', @@ -323,7 +325,7 @@ const privateDsa = fixtures.readKey('dsa_private_encrypted_1025.pem', type: 'pkcs1' }); createPrivateKey({ key, format: 'der', type: 'pkcs1' }); - }, common.hasOpenSSL3 ? { + }, hasOpenSSL3 ? { message: /error:1E08010C:DECODER routines::unsupported/, library: 'DECODER routines' } : { @@ -510,7 +512,7 @@ const privateDsa = fixtures.readKey('dsa_private_encrypted_1025.pem', { // Reading an encrypted key without a passphrase should fail. - assert.throws(() => createPrivateKey(privateDsa), common.hasOpenSSL3 ? { + assert.throws(() => createPrivateKey(privateDsa), hasOpenSSL3 ? { name: 'Error', message: 'error:07880109:common libcrypto routines::interrupted or ' + 'cancelled', @@ -526,7 +528,7 @@ const privateDsa = fixtures.readKey('dsa_private_encrypted_1025.pem', key: privateDsa, format: 'pem', passphrase: Buffer.alloc(1025, 'a') - }), common.hasOpenSSL3 ? { name: 'Error' } : { + }), hasOpenSSL3 ? { name: 'Error' } : { code: 'ERR_OSSL_PEM_BAD_PASSWORD_READ', name: 'Error' }); diff --git a/test/parallel/test-crypto-keygen-async-dsa-key-object.js b/test/parallel/test-crypto-keygen-async-dsa-key-object.js index c15807295541e2..a3df136230d0f8 100644 --- a/test/parallel/test-crypto-keygen-async-dsa-key-object.js +++ b/test/parallel/test-crypto-keygen-async-dsa-key-object.js @@ -9,23 +9,25 @@ const { generateKeyPair, } = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); + // Test async DSA key object generation. { generateKeyPair('dsa', { - modulusLength: common.hasOpenSSL3 ? 2048 : 512, + modulusLength: hasOpenSSL3 ? 2048 : 512, divisorLength: 256 }, common.mustSucceed((publicKey, privateKey) => { assert.strictEqual(publicKey.type, 'public'); assert.strictEqual(publicKey.asymmetricKeyType, 'dsa'); assert.deepStrictEqual(publicKey.asymmetricKeyDetails, { - modulusLength: common.hasOpenSSL3 ? 2048 : 512, + modulusLength: hasOpenSSL3 ? 2048 : 512, divisorLength: 256 }); assert.strictEqual(privateKey.type, 'private'); assert.strictEqual(privateKey.asymmetricKeyType, 'dsa'); assert.deepStrictEqual(privateKey.asymmetricKeyDetails, { - modulusLength: common.hasOpenSSL3 ? 2048 : 512, + modulusLength: hasOpenSSL3 ? 2048 : 512, divisorLength: 256 }); })); diff --git a/test/parallel/test-crypto-keygen-async-dsa.js b/test/parallel/test-crypto-keygen-async-dsa.js index 048c0ce6fb92ef..41968d8cc23365 100644 --- a/test/parallel/test-crypto-keygen-async-dsa.js +++ b/test/parallel/test-crypto-keygen-async-dsa.js @@ -14,6 +14,8 @@ const { spkiExp, } = require('../common/crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); + // Test async DSA key generation. { const privateKeyEncoding = { @@ -22,7 +24,7 @@ const { }; generateKeyPair('dsa', { - modulusLength: common.hasOpenSSL3 ? 2048 : 512, + modulusLength: hasOpenSSL3 ? 2048 : 512, divisorLength: 256, publicKeyEncoding: { type: 'spki', @@ -39,8 +41,8 @@ const { // The private key is DER-encoded. assert(Buffer.isBuffer(privateKeyDER)); - assertApproximateSize(publicKey, common.hasOpenSSL3 ? 1194 : 440); - assertApproximateSize(privateKeyDER, common.hasOpenSSL3 ? 721 : 336); + assertApproximateSize(publicKey, hasOpenSSL3 ? 1194 : 440); + assertApproximateSize(privateKeyDER, hasOpenSSL3 ? 721 : 336); // Since the private key is encrypted, signing shouldn't work anymore. assert.throws(() => { diff --git a/test/parallel/test-crypto-keygen-async-explicit-elliptic-curve-encrypted-p256.js b/test/parallel/test-crypto-keygen-async-explicit-elliptic-curve-encrypted-p256.js index 553674774571d3..55aa3831c4233b 100644 --- a/test/parallel/test-crypto-keygen-async-explicit-elliptic-curve-encrypted-p256.js +++ b/test/parallel/test-crypto-keygen-async-explicit-elliptic-curve-encrypted-p256.js @@ -14,6 +14,8 @@ const { pkcs8EncExp, } = require('../common/crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); + // Test async elliptic curve key generation, e.g. for ECDSA, with an encrypted // private key with paramEncoding explicit. { @@ -38,7 +40,7 @@ const { // Since the private key is encrypted, signing shouldn't work anymore. assert.throws(() => testSignVerify(publicKey, privateKey), - common.hasOpenSSL3 ? { + hasOpenSSL3 ? { message: 'error:07880109:common libcrypto ' + 'routines::interrupted or cancelled' } : { diff --git a/test/parallel/test-crypto-keygen-async-explicit-elliptic-curve-encrypted.js.js b/test/parallel/test-crypto-keygen-async-explicit-elliptic-curve-encrypted.js.js index 79a132eed0b854..8a55d4338bc72f 100644 --- a/test/parallel/test-crypto-keygen-async-explicit-elliptic-curve-encrypted.js.js +++ b/test/parallel/test-crypto-keygen-async-explicit-elliptic-curve-encrypted.js.js @@ -12,6 +12,7 @@ const { testSignVerify, spkiExp, sec1EncExp, + hasOpenSSL3, } = require('../common/crypto'); { @@ -38,7 +39,7 @@ const { // Since the private key is encrypted, signing shouldn't work anymore. assert.throws(() => testSignVerify(publicKey, privateKey), - common.hasOpenSSL3 ? { + hasOpenSSL3 ? { message: 'error:07880109:common libcrypto ' + 'routines::interrupted or cancelled' } : { diff --git a/test/parallel/test-crypto-keygen-async-named-elliptic-curve-encrypted-p256.js b/test/parallel/test-crypto-keygen-async-named-elliptic-curve-encrypted-p256.js index 5e7d1a6c9b6611..4c11401d0fc516 100644 --- a/test/parallel/test-crypto-keygen-async-named-elliptic-curve-encrypted-p256.js +++ b/test/parallel/test-crypto-keygen-async-named-elliptic-curve-encrypted-p256.js @@ -12,6 +12,7 @@ const { testSignVerify, spkiExp, pkcs8EncExp, + hasOpenSSL3, } = require('../common/crypto'); // Test async elliptic curve key generation, e.g. for ECDSA, with an encrypted @@ -38,7 +39,7 @@ const { // Since the private key is encrypted, signing shouldn't work anymore. assert.throws(() => testSignVerify(publicKey, privateKey), - common.hasOpenSSL3 ? { + hasOpenSSL3 ? { message: 'error:07880109:common libcrypto ' + 'routines::interrupted or cancelled' } : { diff --git a/test/parallel/test-crypto-keygen-async-named-elliptic-curve-encrypted.js b/test/parallel/test-crypto-keygen-async-named-elliptic-curve-encrypted.js index 1cc93d0a794931..0503ff74787f37 100644 --- a/test/parallel/test-crypto-keygen-async-named-elliptic-curve-encrypted.js +++ b/test/parallel/test-crypto-keygen-async-named-elliptic-curve-encrypted.js @@ -12,6 +12,7 @@ const { testSignVerify, spkiExp, sec1EncExp, + hasOpenSSL3, } = require('../common/crypto'); { @@ -38,7 +39,7 @@ const { // Since the private key is encrypted, signing shouldn't work anymore. assert.throws(() => testSignVerify(publicKey, privateKey), - common.hasOpenSSL3 ? { + hasOpenSSL3 ? { message: 'error:07880109:common libcrypto ' + 'routines::interrupted or cancelled' } : { diff --git a/test/parallel/test-crypto-keygen-async-rsa.js b/test/parallel/test-crypto-keygen-async-rsa.js index f4a83809dc73c7..c80d7d33492923 100644 --- a/test/parallel/test-crypto-keygen-async-rsa.js +++ b/test/parallel/test-crypto-keygen-async-rsa.js @@ -13,6 +13,7 @@ const { testEncryptDecrypt, testSignVerify, pkcs1EncExp, + hasOpenSSL3, } = require('../common/crypto'); // Test async RSA key generation with an encrypted private key. @@ -43,7 +44,7 @@ const { type: 'pkcs1', format: 'der', }; - const expectedError = common.hasOpenSSL3 ? { + const expectedError = hasOpenSSL3 ? { name: 'Error', message: 'error:07880109:common libcrypto routines::interrupted or ' + 'cancelled' diff --git a/test/parallel/test-crypto-keygen-bit-length.js b/test/parallel/test-crypto-keygen-bit-length.js index 08772ba2e496b8..63a80659bb2f53 100644 --- a/test/parallel/test-crypto-keygen-bit-length.js +++ b/test/parallel/test-crypto-keygen-bit-length.js @@ -8,6 +8,7 @@ const assert = require('assert'); const { generateKeyPair, } = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); // This tests check that generateKeyPair returns correct bit length in // KeyObject's asymmetricKeyDetails. @@ -27,7 +28,7 @@ const { assert.strictEqual(publicKey.asymmetricKeyDetails.modulusLength, 513); })); - if (common.hasOpenSSL3) { + if (hasOpenSSL3) { generateKeyPair('dsa', { modulusLength: 2049, divisorLength: 256, diff --git a/test/parallel/test-crypto-keygen-empty-passphrase-no-prompt.js b/test/parallel/test-crypto-keygen-empty-passphrase-no-prompt.js index 7679a492c3194c..cb873ff04748b7 100644 --- a/test/parallel/test-crypto-keygen-empty-passphrase-no-prompt.js +++ b/test/parallel/test-crypto-keygen-empty-passphrase-no-prompt.js @@ -11,6 +11,7 @@ const { } = require('crypto'); const { testSignVerify, + hasOpenSSL3, } = require('../common/crypto'); // Passing an empty passphrase string should not cause OpenSSL's default @@ -40,7 +41,7 @@ for (const type of ['pkcs1', 'pkcs8']) { // the key, and not specifying a passphrase should fail when decoding it. assert.throws(() => { return testSignVerify(publicKey, privateKey); - }, common.hasOpenSSL3 ? { + }, hasOpenSSL3 ? { name: 'Error', code: 'ERR_OSSL_CRYPTO_INTERRUPTED_OR_CANCELLED', message: 'error:07880109:common libcrypto routines::interrupted or cancelled' diff --git a/test/parallel/test-crypto-keygen-missing-oid.js b/test/parallel/test-crypto-keygen-missing-oid.js index f7fefe13848d4b..1e4f309292eb47 100644 --- a/test/parallel/test-crypto-keygen-missing-oid.js +++ b/test/parallel/test-crypto-keygen-missing-oid.js @@ -11,6 +11,8 @@ const { getCurves, } = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); + // This test creates EC key pairs on curves without associated OIDs. // Specifying a key encoding should not crash. { @@ -20,7 +22,7 @@ const { continue; const expectedErrorCode = - common.hasOpenSSL3 ? 'ERR_OSSL_MISSING_OID' : 'ERR_OSSL_EC_MISSING_OID'; + hasOpenSSL3 ? 'ERR_OSSL_MISSING_OID' : 'ERR_OSSL_EC_MISSING_OID'; const params = { namedCurve, publicKeyEncoding: { diff --git a/test/parallel/test-crypto-keygen.js b/test/parallel/test-crypto-keygen.js index b09ca9e7c531ea..edaee845075668 100644 --- a/test/parallel/test-crypto-keygen.js +++ b/test/parallel/test-crypto-keygen.js @@ -14,6 +14,7 @@ const { } = require('crypto'); const { inspect } = require('util'); +const { hasOpenSSL3 } = require('../common/crypto'); // Test invalid parameter encoding. { @@ -351,7 +352,7 @@ const { inspect } = require('util'); publicExponent }, common.mustCall((err) => { assert.strictEqual(err.name, 'Error'); - assert.match(err.message, common.hasOpenSSL3 ? /exponent/ : /bad e value/); + assert.match(err.message, hasOpenSSL3 ? /exponent/ : /bad e value/); })); } } diff --git a/test/parallel/test-crypto-no-algorithm.js b/test/parallel/test-crypto-no-algorithm.js index 37db38cf613b65..06124e3d465e41 100644 --- a/test/parallel/test-crypto-no-algorithm.js +++ b/test/parallel/test-crypto-no-algorithm.js @@ -4,7 +4,9 @@ const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); -if (!common.hasOpenSSL3) +const { hasOpenSSL3 } = require('../common/crypto'); + +if (!hasOpenSSL3) common.skip('this test requires OpenSSL 3.x'); const assert = require('node:assert/strict'); diff --git a/test/parallel/test-crypto-oneshot-hash.js b/test/parallel/test-crypto-oneshot-hash.js index 69051c43d9e882..861aded5dd3f60 100644 --- a/test/parallel/test-crypto-oneshot-hash.js +++ b/test/parallel/test-crypto-oneshot-hash.js @@ -8,6 +8,7 @@ if (!common.hasCrypto) const assert = require('assert'); const crypto = require('crypto'); const fixtures = require('../common/fixtures'); +const { hasOpenSSL } = require('../common/crypto'); const fs = require('fs'); // Test errors for invalid arguments. @@ -32,7 +33,7 @@ const input = fs.readFileSync(fixtures.path('utf8_test_text.txt')); for (const method of methods) { // Skip failing tests on OpenSSL 3.4.0 - if (method.startsWith('shake') && common.hasOpenSSL(3, 4)) + if (method.startsWith('shake') && hasOpenSSL(3, 4)) continue; for (const outputEncoding of ['buffer', 'hex', 'base64', undefined]) { const oldDigest = crypto.createHash(method).update(input).digest(outputEncoding || 'hex'); diff --git a/test/parallel/test-crypto-padding.js b/test/parallel/test-crypto-padding.js index f1f14b472997e7..48cd1ed4df61aa 100644 --- a/test/parallel/test-crypto-padding.js +++ b/test/parallel/test-crypto-padding.js @@ -26,6 +26,7 @@ if (!common.hasCrypto) const assert = require('assert'); const crypto = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); // Input data. const ODD_LENGTH_PLAIN = 'Hello node world!'; @@ -82,7 +83,7 @@ assert.strictEqual(enc(EVEN_LENGTH_PLAIN, true), EVEN_LENGTH_ENCRYPTED); assert.throws(function() { // Input must have block length %. enc(ODD_LENGTH_PLAIN, false); -}, common.hasOpenSSL3 ? { +}, hasOpenSSL3 ? { message: 'error:1C80006B:Provider routines::wrong final block length', code: 'ERR_OSSL_WRONG_FINAL_BLOCK_LENGTH', reason: 'wrong final block length', @@ -109,7 +110,7 @@ assert.strictEqual(dec(EVEN_LENGTH_ENCRYPTED, false).length, 48); assert.throws(function() { // Must have at least 1 byte of padding (PKCS): assert.strictEqual(dec(EVEN_LENGTH_ENCRYPTED_NOPAD, true), EVEN_LENGTH_PLAIN); -}, common.hasOpenSSL3 ? { +}, hasOpenSSL3 ? { message: 'error:1C800064:Provider routines::bad decrypt', reason: 'bad decrypt', code: 'ERR_OSSL_BAD_DECRYPT', diff --git a/test/parallel/test-crypto-pbkdf2.js b/test/parallel/test-crypto-pbkdf2.js index 1f8e6a81f300e7..efd8d6eaf0d640 100644 --- a/test/parallel/test-crypto-pbkdf2.js +++ b/test/parallel/test-crypto-pbkdf2.js @@ -5,6 +5,7 @@ if (!common.hasCrypto) const assert = require('assert'); const crypto = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); function runPBKDF2(password, salt, iterations, keylen, hash) { const syncResult = @@ -219,7 +220,7 @@ assert.throws( } ); -if (!common.hasOpenSSL3) { +if (!hasOpenSSL3) { const kNotPBKDF2Supported = ['shake128', 'shake256']; crypto.getHashes() .filter((hash) => !kNotPBKDF2Supported.includes(hash)) diff --git a/test/parallel/test-crypto-private-decrypt-gh32240.js b/test/parallel/test-crypto-private-decrypt-gh32240.js index e88227a215ba4f..1ff5b565d6d5f4 100644 --- a/test/parallel/test-crypto-private-decrypt-gh32240.js +++ b/test/parallel/test-crypto-private-decrypt-gh32240.js @@ -14,6 +14,8 @@ const { privateDecrypt, } = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); + const pair = generateKeyPairSync('rsa', { modulusLength: 512 }); const expected = Buffer.from('shibboleth'); @@ -34,7 +36,7 @@ function decrypt(key) { } decrypt(pkey); -assert.throws(() => decrypt(pkeyEncrypted), common.hasOpenSSL3 ? +assert.throws(() => decrypt(pkeyEncrypted), hasOpenSSL3 ? { message: 'error:07880109:common libcrypto routines::interrupted or ' + 'cancelled' } : { code: 'ERR_MISSING_PASSPHRASE' }); diff --git a/test/parallel/test-crypto-publicDecrypt-fails-first-time.js b/test/parallel/test-crypto-publicDecrypt-fails-first-time.js index a60b87dbf65229..1d64e08920c63b 100644 --- a/test/parallel/test-crypto-publicDecrypt-fails-first-time.js +++ b/test/parallel/test-crypto-publicDecrypt-fails-first-time.js @@ -3,11 +3,15 @@ const common = require('../common'); // Test for https://github.com/nodejs/node/issues/40814 -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} -if (!common.hasOpenSSL3) +const { hasOpenSSL3 } = require('../common/crypto'); + +if (!hasOpenSSL3) { common.skip('only openssl3'); // https://github.com/nodejs/node/pull/42793#issuecomment-1107491901 +} const assert = require('assert'); const crypto = require('crypto'); diff --git a/test/parallel/test-crypto-rsa-dsa.js b/test/parallel/test-crypto-rsa-dsa.js index 5f4fafdfffbf72..dcd5045daaf58c 100644 --- a/test/parallel/test-crypto-rsa-dsa.js +++ b/test/parallel/test-crypto-rsa-dsa.js @@ -9,6 +9,7 @@ const crypto = require('crypto'); const constants = crypto.constants; const fixtures = require('../common/fixtures'); +const { hasOpenSSL3 } = require('../common/crypto'); // Test certificates const certPem = fixtures.readKey('rsa_cert.crt'); @@ -36,11 +37,11 @@ const openssl1DecryptError = { library: 'digital envelope routines', }; -const decryptError = common.hasOpenSSL3 ? +const decryptError = hasOpenSSL3 ? { message: 'error:1C800064:Provider routines::bad decrypt' } : openssl1DecryptError; -const decryptPrivateKeyError = common.hasOpenSSL3 ? { +const decryptPrivateKeyError = hasOpenSSL3 ? { message: 'error:1C800064:Provider routines::bad decrypt', } : openssl1DecryptError; @@ -146,7 +147,7 @@ function getBufferCopy(buf) { // Now with RSA_NO_PADDING. Plaintext needs to match key size. // OpenSSL 3.x has a rsa_check_padding that will cause an error if // RSA_NO_PADDING is used. - if (!common.hasOpenSSL3) { + if (!hasOpenSSL3) { { const plaintext = 'x'.repeat(rsaKeySize / 8); encryptedBuffer = crypto.privateEncrypt({ diff --git a/test/parallel/test-crypto-secure-heap.js b/test/parallel/test-crypto-secure-heap.js index 0e5788f00e4a44..c20b01a91a9840 100644 --- a/test/parallel/test-crypto-secure-heap.js +++ b/test/parallel/test-crypto-secure-heap.js @@ -1,21 +1,26 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} -if (common.isWindows) +if (common.isWindows) { common.skip('Not supported on Windows'); +} -if (common.isASan) +if (common.isASan) { common.skip('ASan does not play well with secure heap allocations'); +} const assert = require('assert'); const { fork } = require('child_process'); const fixtures = require('../common/fixtures'); +const { hasOpenSSL3 } = require('../common/crypto'); const { secureHeapUsed, createDiffieHellman, + getFips, } = require('crypto'); if (process.argv[2] === 'child') { @@ -29,7 +34,7 @@ if (process.argv[2] === 'child') { assert.strictEqual(a.used, 0); { - const size = common.hasFipsCrypto || common.hasOpenSSL3 ? 1024 : 256; + const size = getFips() || hasOpenSSL3 ? 1024 : 256; const dh1 = createDiffieHellman(size); const p1 = dh1.getPrime('buffer'); const dh2 = createDiffieHellman(p1, 'buffer'); diff --git a/test/parallel/test-crypto-sign-verify.js b/test/parallel/test-crypto-sign-verify.js index 8a263ec3350f55..0589d60736e377 100644 --- a/test/parallel/test-crypto-sign-verify.js +++ b/test/parallel/test-crypto-sign-verify.js @@ -8,6 +8,10 @@ const fs = require('fs'); const exec = require('child_process').exec; const crypto = require('crypto'); const fixtures = require('../common/fixtures'); +const { + hasOpenSSL3, + opensslCli, +} = require('../common/crypto'); // Test certificates const certPem = fixtures.readKey('rsa_cert.crt'); @@ -62,7 +66,7 @@ const keySize = 2048; key: keyPem, padding: crypto.constants.RSA_PKCS1_OAEP_PADDING }); - }, { message: common.hasOpenSSL3 ? + }, { message: hasOpenSSL3 ? 'error:1C8000A5:Provider routines::illegal or unsupported padding mode' : 'bye, bye, error stack' }); @@ -340,7 +344,7 @@ assert.throws( key: keyPem, padding: crypto.constants.RSA_PKCS1_OAEP_PADDING }); - }, common.hasOpenSSL3 ? { + }, hasOpenSSL3 ? { code: 'ERR_OSSL_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE', message: /illegal or unsupported padding mode/, } : { @@ -599,8 +603,9 @@ assert.throws( // Note: this particular test *must* be the last in this file as it will exit // early if no openssl binary is found { - if (!common.opensslCli) + if (!opensslCli) { common.skip('node compiled without OpenSSL CLI.'); + } const pubfile = fixtures.path('keys', 'rsa_public_2048.pem'); const privkey = fixtures.readKey('rsa_private_2048.pem'); @@ -622,7 +627,7 @@ assert.throws( fs.writeFileSync(msgfile, msg); exec(...common.escapePOSIXShell`"${ - common.opensslCli}" dgst -sha256 -verify "${pubfile}" -signature "${ + opensslCli}" dgst -sha256 -verify "${pubfile}" -signature "${ sigfile}" -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-2 "${msgfile }"`, common.mustCall((err, stdout, stderr) => { assert(stdout.includes('Verified OK')); diff --git a/test/parallel/test-crypto-stream.js b/test/parallel/test-crypto-stream.js index 008ab129f0e019..62be4eaf6edfb0 100644 --- a/test/parallel/test-crypto-stream.js +++ b/test/parallel/test-crypto-stream.js @@ -21,14 +21,16 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} const assert = require('assert'); const stream = require('stream'); const crypto = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); -if (!common.hasFipsCrypto) { +if (!crypto.getFips()) { // Small stream to buffer converter class Stream2buffer extends stream.Writable { constructor(callback) { @@ -71,7 +73,7 @@ const cipher = crypto.createCipheriv('aes-128-cbc', key, iv); const decipher = crypto.createDecipheriv('aes-128-cbc', badkey, iv); cipher.pipe(decipher) - .on('error', common.expectsError(common.hasOpenSSL3 ? { + .on('error', common.expectsError(hasOpenSSL3 ? { message: /bad decrypt/, library: 'Provider routines', reason: 'bad decrypt', diff --git a/test/parallel/test-crypto-x509.js b/test/parallel/test-crypto-x509.js index bd906c25b9ee19..f75e1d63470bfb 100644 --- a/test/parallel/test-crypto-x509.js +++ b/test/parallel/test-crypto-x509.js @@ -18,6 +18,7 @@ const { const assert = require('assert'); const fixtures = require('../common/fixtures'); +const { hasOpenSSL3 } = require('../common/crypto'); const { readFileSync } = require('fs'); const cert = readFileSync(fixtures.path('keys', 'agent1-cert.pem')); @@ -50,7 +51,7 @@ emailAddress=ry@tinyclouds.org`; let infoAccessCheck = `OCSP - URI:http://ocsp.nodejs.org/ CA Issuers - URI:http://ca.nodejs.org/ca.cert`; -if (!common.hasOpenSSL3) +if (!hasOpenSSL3) infoAccessCheck += '\n'; const der = Buffer.from( @@ -357,7 +358,7 @@ UcXd/5qu2GhokrKU2cPttU+XAN2Om6a0 const cert = new X509Certificate(certPem); assert.throws(() => cert.publicKey, { - message: common.hasOpenSSL3 ? /decode error/ : /wrong tag/, + message: hasOpenSSL3 ? /decode error/ : /wrong tag/, name: 'Error' }); diff --git a/test/parallel/test-crypto.js b/test/parallel/test-crypto.js index 4271121881379b..93644e016de447 100644 --- a/test/parallel/test-crypto.js +++ b/test/parallel/test-crypto.js @@ -29,6 +29,7 @@ const assert = require('assert'); const crypto = require('crypto'); const tls = require('tls'); const fixtures = require('../common/fixtures'); +const { hasOpenSSL3 } = require('../common/crypto'); // Test Certificates const certPfx = fixtures.readKey('rsa_cert.pfx'); @@ -208,9 +209,9 @@ assert.throws(() => { ].join('\n'); crypto.createSign('SHA256').update('test').sign(priv); }, (err) => { - if (!common.hasOpenSSL3) + if (!hasOpenSSL3) assert.ok(!('opensslErrorStack' in err)); - assert.throws(() => { throw err; }, common.hasOpenSSL3 ? { + assert.throws(() => { throw err; }, hasOpenSSL3 ? { name: 'Error', message: 'error:02000070:rsa routines::digest too big for rsa key', library: 'rsa routines', @@ -225,7 +226,7 @@ assert.throws(() => { return true; }); -if (!common.hasOpenSSL3) { +if (!hasOpenSSL3) { assert.throws(() => { // The correct header inside `rsa_private_pkcs8_bad.pem` should have been // -----BEGIN PRIVATE KEY----- and -----END PRIVATE KEY----- diff --git a/test/parallel/test-dsa-fips-invalid-key.js b/test/parallel/test-dsa-fips-invalid-key.js index 05cc1d143aca6e..3df51bfbed3517 100644 --- a/test/parallel/test-dsa-fips-invalid-key.js +++ b/test/parallel/test-dsa-fips-invalid-key.js @@ -1,12 +1,18 @@ 'use strict'; const common = require('../common'); + +if (!common.hasCrypto) { + common.skip('no crypto'); +} + const fixtures = require('../common/fixtures'); +const crypto = require('crypto'); -if (!common.hasFipsCrypto) +if (!crypto.getFips()) { common.skip('node compiled without FIPS OpenSSL.'); +} const assert = require('assert'); -const crypto = require('crypto'); const input = 'hello'; diff --git a/test/parallel/test-https-agent-session-eviction.js b/test/parallel/test-https-agent-session-eviction.js index e0986e53c1103b..6f88e81e9ff29d 100644 --- a/test/parallel/test-https-agent-session-eviction.js +++ b/test/parallel/test-https-agent-session-eviction.js @@ -2,10 +2,13 @@ 'use strict'; const common = require('../common'); -const { readKey } = require('../common/fixtures'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} + +const { readKey } = require('../common/fixtures'); +const { hasOpenSSL } = require('../common/crypto'); const https = require('https'); const { SSL_OP_NO_TICKET } = require('crypto').constants; @@ -56,7 +59,7 @@ function faultyServer(port) { function second(server, session) { const req = https.request({ port: server.address().port, - ciphers: (common.hasOpenSSL(3, 1) ? 'DEFAULT:@SECLEVEL=0' : 'DEFAULT'), + ciphers: (hasOpenSSL(3, 1) ? 'DEFAULT:@SECLEVEL=0' : 'DEFAULT'), rejectUnauthorized: false }, function(res) { res.resume(); diff --git a/test/parallel/test-https-client-renegotiation-limit.js b/test/parallel/test-https-client-renegotiation-limit.js index 35fcc6bfcc6e43..18a602d738c316 100644 --- a/test/parallel/test-https-client-renegotiation-limit.js +++ b/test/parallel/test-https-client-renegotiation-limit.js @@ -21,11 +21,15 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} + +const { opensslCli } = require('../common/crypto'); -if (!common.opensslCli) +if (!opensslCli) { common.skip('node compiled without OpenSSL CLI.'); +} const assert = require('assert'); const tls = require('tls'); diff --git a/test/parallel/test-https-foafssl.js b/test/parallel/test-https-foafssl.js index d6dde97a41da9c..df375e7d22201e 100644 --- a/test/parallel/test-https-foafssl.js +++ b/test/parallel/test-https-foafssl.js @@ -21,11 +21,15 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} -if (!common.opensslCli) +const { opensslCli } = require('../common/crypto'); + +if (!opensslCli) { common.skip('node compiled without OpenSSL CLI.'); +} const assert = require('assert'); const fixtures = require('../common/fixtures'); @@ -67,7 +71,7 @@ server.listen(0, function() { '-cert', fixtures.path('keys/rsa_cert_foafssl_b.crt'), '-key', fixtures.path('keys/rsa_private_b.pem')]; - const client = spawn(common.opensslCli, args); + const client = spawn(opensslCli, args); client.stdout.on('data', function(data) { console.log('response received'); diff --git a/test/parallel/test-process-env-allowed-flags-are-documented.js b/test/parallel/test-process-env-allowed-flags-are-documented.js index 2a40a821314ff3..070a88bca8c12c 100644 --- a/test/parallel/test-process-env-allowed-flags-are-documented.js +++ b/test/parallel/test-process-env-allowed-flags-are-documented.js @@ -5,6 +5,7 @@ const common = require('../common'); const assert = require('assert'); const fs = require('fs'); const path = require('path'); +const { hasOpenSSL3 } = require('../common/crypto'); const rootDir = path.resolve(__dirname, '..', '..'); const cliMd = path.join(rootDir, 'doc', 'api', 'cli.md'); @@ -43,7 +44,7 @@ for (const line of [...nodeOptionsLines, ...v8OptionsLines]) { } } -if (!common.hasOpenSSL3) { +if (!hasOpenSSL3) { documented.delete('--openssl-legacy-provider'); documented.delete('--openssl-shared-config'); } @@ -55,8 +56,8 @@ const conditionalOpts = [ filter: (opt) => { return [ '--openssl-config', - common.hasOpenSSL3 ? '--openssl-legacy-provider' : '', - common.hasOpenSSL3 ? '--openssl-shared-config' : '', + hasOpenSSL3 ? '--openssl-legacy-provider' : '', + hasOpenSSL3 ? '--openssl-shared-config' : '', '--tls-cipher-list', '--use-bundled-ca', '--use-openssl-ca', diff --git a/test/parallel/test-process-versions.js b/test/parallel/test-process-versions.js index 3b8af4b5b52526..0a2a4014f18d6b 100644 --- a/test/parallel/test-process-versions.js +++ b/test/parallel/test-process-versions.js @@ -85,11 +85,12 @@ assert.match(process.versions.modules, /^\d+$/); assert.match(process.versions.cjs_module_lexer, commonTemplate); if (common.hasCrypto) { + const { hasOpenSSL3 } = require('../common/crypto'); assert.match(process.versions.ncrypto, commonTemplate); if (process.config.variables.node_shared_openssl) { assert.ok(process.versions.openssl); } else { - const versionRegex = common.hasOpenSSL3 ? + const versionRegex = hasOpenSSL3 ? // The following also matches a development version of OpenSSL 3.x which // can be in the format '3.0.0-alpha4-dev'. This can be handy when // building and linking against the main development branch of OpenSSL. diff --git a/test/parallel/test-tls-alert-handling.js b/test/parallel/test-tls-alert-handling.js index b14438bc92d7e6..eec072796063dc 100644 --- a/test/parallel/test-tls-alert-handling.js +++ b/test/parallel/test-tls-alert-handling.js @@ -1,11 +1,19 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} + +const { + hasOpenSSL, + hasOpenSSL3, + opensslCli, +} = require('../common/crypto'); -if (!common.opensslCli) +if (!opensslCli) { common.skip('node compiled without OpenSSL CLI'); +} const assert = require('assert'); const net = require('net'); @@ -33,14 +41,14 @@ let iter = 0; const errorHandler = common.mustCall((err) => { let expectedErrorCode = 'ERR_SSL_WRONG_VERSION_NUMBER'; let expectedErrorReason = 'wrong version number'; - if (common.hasOpenSSL(3, 2)) { + if (hasOpenSSL(3, 2)) { expectedErrorCode = 'ERR_SSL_PACKET_LENGTH_TOO_LONG'; expectedErrorReason = 'packet length too long'; }; assert.strictEqual(err.code, expectedErrorCode); assert.strictEqual(err.library, 'SSL routines'); - if (!common.hasOpenSSL3) assert.strictEqual(err.function, 'ssl3_get_record'); + if (!hasOpenSSL3) assert.strictEqual(err.function, 'ssl3_get_record'); assert.strictEqual(err.reason, expectedErrorReason); errorReceived = true; if (canCloseServer()) @@ -96,13 +104,13 @@ function sendBADTLSRecord() { client.on('error', common.mustCall((err) => { let expectedErrorCode = 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION'; let expectedErrorReason = 'tlsv1 alert protocol version'; - if (common.hasOpenSSL(3, 2)) { + if (hasOpenSSL(3, 2)) { expectedErrorCode = 'ERR_SSL_TLSV1_ALERT_RECORD_OVERFLOW'; expectedErrorReason = 'tlsv1 alert record overflow'; } assert.strictEqual(err.code, expectedErrorCode); assert.strictEqual(err.library, 'SSL routines'); - if (!common.hasOpenSSL3) + if (!hasOpenSSL3) assert.strictEqual(err.function, 'ssl3_read_bytes'); assert.strictEqual(err.reason, expectedErrorReason); })); diff --git a/test/parallel/test-tls-alert.js b/test/parallel/test-tls-alert.js index e6aaaedfe59d72..23c92e7293458f 100644 --- a/test/parallel/test-tls-alert.js +++ b/test/parallel/test-tls-alert.js @@ -21,11 +21,18 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} + +const { + hasOpenSSL, + opensslCli, +} = require('../common/crypto'); -if (!common.opensslCli) +if (!opensslCli) { common.skip('node compiled without OpenSSL CLI.'); +} const assert = require('assert'); const { execFile } = require('child_process'); @@ -42,10 +49,10 @@ const server = tls.Server({ cert: loadPEM('agent2-cert') }, null).listen(0, common.mustCall(() => { const args = ['s_client', '-quiet', '-tls1_1', - '-cipher', (common.hasOpenSSL(3, 1) ? 'DEFAULT:@SECLEVEL=0' : 'DEFAULT'), + '-cipher', (hasOpenSSL(3, 1) ? 'DEFAULT:@SECLEVEL=0' : 'DEFAULT'), '-connect', `127.0.0.1:${server.address().port}`]; - execFile(common.opensslCli, args, common.mustCall((err, _, stderr) => { + execFile(opensslCli, args, common.mustCall((err, _, stderr) => { assert.strictEqual(err.code, 1); assert.match(stderr, /SSL alert number 70/); server.close(); diff --git a/test/parallel/test-tls-alpn-server-client.js b/test/parallel/test-tls-alpn-server-client.js index 8f1a4b8e439aab..b7cd2806471e67 100644 --- a/test/parallel/test-tls-alpn-server-client.js +++ b/test/parallel/test-tls-alpn-server-client.js @@ -1,8 +1,9 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} const assert = require('assert'); const { spawn } = require('child_process'); @@ -198,7 +199,7 @@ function TestFatalAlert() { // OpenSSL's s_client should output the TLS alert number, which is 120 // for the 'no_application_protocol' alert. - const { opensslCli } = common; + const { opensslCli } = require('../common/crypto'); if (opensslCli) { const addr = `${serverIP}:${port}`; let stderr = ''; diff --git a/test/parallel/test-tls-cert-ext-encoding.js b/test/parallel/test-tls-cert-ext-encoding.js index 4556b5791851c5..154e0cdcf02294 100644 --- a/test/parallel/test-tls-cert-ext-encoding.js +++ b/test/parallel/test-tls-cert-ext-encoding.js @@ -3,7 +3,9 @@ const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); -if (common.hasOpenSSL3) +const { hasOpenSSL3 } = require('../common/crypto'); + +if (hasOpenSSL3) // TODO(danbev) This test fails with the following error: // error:0D00008F:asn1 encoding routines::no matching choice type // diff --git a/test/parallel/test-tls-client-auth.js b/test/parallel/test-tls-client-auth.js index de4c8f038ec073..b347c0a88df571 100644 --- a/test/parallel/test-tls-client-auth.js +++ b/test/parallel/test-tls-client-auth.js @@ -3,6 +3,11 @@ const common = require('../common'); const fixtures = require('../common/fixtures'); +if (!common.hasCrypto) { + common.skip('missing crypto'); +} +const { hasOpenSSL } = require('../common/crypto'); + const { assert, connect, keys, tls } = require(fixtures.path('tls-connect')); @@ -79,7 +84,7 @@ connect({ }, function(err, pair, cleanup) { assert.strictEqual(pair.server.err.code, 'ERR_SSL_PEER_DID_NOT_RETURN_A_CERTIFICATE'); - const expectedErr = common.hasOpenSSL(3, 2) ? + const expectedErr = hasOpenSSL(3, 2) ? 'ERR_SSL_SSL/TLS_ALERT_HANDSHAKE_FAILURE' : 'ERR_SSL_SSLV3_ALERT_HANDSHAKE_FAILURE'; assert.strictEqual(pair.client.err.code, expectedErr); diff --git a/test/parallel/test-tls-client-getephemeralkeyinfo.js b/test/parallel/test-tls-client-getephemeralkeyinfo.js index 0bacd8702fc650..0f132c565e4400 100644 --- a/test/parallel/test-tls-client-getephemeralkeyinfo.js +++ b/test/parallel/test-tls-client-getephemeralkeyinfo.js @@ -3,6 +3,7 @@ const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); const fixtures = require('../common/fixtures'); +const { hasOpenSSL } = require('../common/crypto'); const assert = require('assert'); const { X509Certificate } = require('crypto'); @@ -69,7 +70,7 @@ function test(size, type, name, cipher) { test(undefined, undefined, undefined, 'AES256-SHA256'); test('auto', 'DH', undefined, 'DHE-RSA-AES256-GCM-SHA384'); -if (!common.hasOpenSSL(3, 2)) { +if (!hasOpenSSL(3, 2)) { test(1024, 'DH', undefined, 'DHE-RSA-AES256-GCM-SHA384'); } else { test(3072, 'DH', undefined, 'DHE-RSA-AES256-GCM-SHA384'); diff --git a/test/parallel/test-tls-client-mindhsize.js b/test/parallel/test-tls-client-mindhsize.js index 15c086842e1e4a..1ab5b5fe1bffd7 100644 --- a/test/parallel/test-tls-client-mindhsize.js +++ b/test/parallel/test-tls-client-mindhsize.js @@ -3,6 +3,7 @@ const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); +const { hasOpenSSL } = require('../common/crypto'); const assert = require('assert'); const tls = require('tls'); const fixtures = require('../common/fixtures'); @@ -38,7 +39,7 @@ function test(size, err, next) { // Client set minimum DH parameter size to 2048 or 3072 bits // so that it fails when it makes a connection to the tls // server where is too small - const minDHSize = common.hasOpenSSL(3, 2) ? 3072 : 2048; + const minDHSize = hasOpenSSL(3, 2) ? 3072 : 2048; const client = tls.connect({ minDHSize: minDHSize, port: this.address().port, @@ -76,7 +77,7 @@ function testDHE3072() { test(3072, false, null); } -if (common.hasOpenSSL(3, 2)) { +if (hasOpenSSL(3, 2)) { // Minimum size for OpenSSL 3.2 is 2048 by default testDHE2048(true, testDHE3072); } else { diff --git a/test/parallel/test-tls-client-renegotiation-13.js b/test/parallel/test-tls-client-renegotiation-13.js index a32baed0249a0a..38a72fb525b430 100644 --- a/test/parallel/test-tls-client-renegotiation-13.js +++ b/test/parallel/test-tls-client-renegotiation-13.js @@ -1,6 +1,12 @@ 'use strict'; const common = require('../common'); + +if (!common.hasCrypto) { + common.skip('missing crypto'); +} +const { hasOpenSSL3 } = require('../common/crypto'); + const fixtures = require('../common/fixtures'); // Confirm that for TLSv1.3, renegotiate() is disallowed. @@ -29,7 +35,7 @@ connect({ const ok = client.renegotiate({}, common.mustCall((err) => { assert.throws(() => { throw err; }, { - message: common.hasOpenSSL3 ? + message: hasOpenSSL3 ? 'error:0A00010A:SSL routines::wrong ssl version' : 'error:1420410A:SSL routines:SSL_renegotiate:wrong ssl version', code: 'ERR_SSL_WRONG_SSL_VERSION', diff --git a/test/parallel/test-tls-client-renegotiation-limit.js b/test/parallel/test-tls-client-renegotiation-limit.js index 71d7a85bae468b..b35140e8964ac1 100644 --- a/test/parallel/test-tls-client-renegotiation-limit.js +++ b/test/parallel/test-tls-client-renegotiation-limit.js @@ -21,11 +21,15 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} + +const { opensslCli } = require('../common/crypto'); -if (!common.opensslCli) +if (!opensslCli) { common.skip('node compiled without OpenSSL CLI.'); +} const assert = require('assert'); const tls = require('tls'); diff --git a/test/parallel/test-tls-dhe.js b/test/parallel/test-tls-dhe.js index 21739ce42428eb..25b58191e1d413 100644 --- a/test/parallel/test-tls-dhe.js +++ b/test/parallel/test-tls-dhe.js @@ -22,11 +22,18 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} + +const { + hasOpenSSL, + opensslCli, +} = require('../common/crypto'); -if (!common.opensslCli) +if (!opensslCli) { common.skip('missing openssl-cli'); +} const assert = require('assert'); const { X509Certificate } = require('crypto'); @@ -43,7 +50,7 @@ const dheCipher = 'DHE-RSA-AES128-SHA256'; const ecdheCipher = 'ECDHE-RSA-AES128-SHA256'; const ciphers = `${dheCipher}:${ecdheCipher}`; -if (!common.hasOpenSSL(3, 2)) { +if (!hasOpenSSL(3, 2)) { // Test will emit a warning because the DH parameter size is < 2048 bits // when the test is run on versions lower than OpenSSL32 common.expectWarning('SecurityWarning', @@ -70,7 +77,7 @@ function test(dhparam, keylen, expectedCipher) { const args = ['s_client', '-connect', `127.0.0.1:${server.address().port}`, '-cipher', `${ciphers}:@SECLEVEL=1`]; - execFile(common.opensslCli, args, common.mustSucceed((stdout) => { + execFile(opensslCli, args, common.mustSucceed((stdout) => { assert(keylen === null || stdout.includes(`Server Temp Key: DH, ${keylen} bits`)); assert(stdout.includes(`Cipher : ${expectedCipher}`)); @@ -107,7 +114,7 @@ function testCustomParam(keylen, expectedCipher) { }, /DH parameter is less than 1024 bits/); // Custom DHE parameters are supported (but discouraged). - if (!common.hasOpenSSL(3, 2)) { + if (!hasOpenSSL(3, 2)) { await testCustomParam(1024, dheCipher); } else { await testCustomParam(3072, dheCipher); diff --git a/test/parallel/test-tls-ecdh-auto.js b/test/parallel/test-tls-ecdh-auto.js index 11c588d8ac8ce1..adc7817b729aa8 100644 --- a/test/parallel/test-tls-ecdh-auto.js +++ b/test/parallel/test-tls-ecdh-auto.js @@ -4,11 +4,15 @@ const common = require('../common'); // This test ensures that the value "auto" on ecdhCurve option is // supported to enable automatic curve selection in TLS server. -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} + +const { opensslCli } = require('../common/crypto'); -if (!common.opensslCli) +if (!opensslCli) { common.skip('missing openssl-cli'); +} const assert = require('assert'); const tls = require('tls'); @@ -36,7 +40,7 @@ const server = tls.createServer(options, (conn) => { '-cipher', `${options.ciphers}`, '-connect', `127.0.0.1:${server.address().port}`]; - execFile(common.opensslCli, args, common.mustSucceed((stdout) => { + execFile(opensslCli, args, common.mustSucceed((stdout) => { assert(stdout.includes(reply)); server.close(); })); diff --git a/test/parallel/test-tls-ecdh-multiple.js b/test/parallel/test-tls-ecdh-multiple.js index 5bf119f48bacad..957f8e0407a6de 100644 --- a/test/parallel/test-tls-ecdh-multiple.js +++ b/test/parallel/test-tls-ecdh-multiple.js @@ -4,11 +4,16 @@ const common = require('../common'); // This test ensures that ecdhCurve option of TLS server supports colon // separated ECDH curve names as value. -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} + +const { opensslCli } = require('../common/crypto'); +const crypto = require('crypto'); -if (!common.opensslCli) +if (!opensslCli) { common.skip('missing openssl-cli'); +} const assert = require('assert'); const tls = require('tls'); @@ -36,7 +41,7 @@ const server = tls.createServer(options, (conn) => { '-cipher', `${options.ciphers}`, '-connect', `127.0.0.1:${server.address().port}`]; - execFile(common.opensslCli, args, common.mustSucceed((stdout) => { + execFile(opensslCli, args, common.mustSucceed((stdout) => { assert(stdout.includes(reply)); server.close(); })); @@ -51,8 +56,9 @@ const server = tls.createServer(options, (conn) => { ]; // Brainpool is not supported in FIPS mode. - if (common.hasFipsCrypto) + if (crypto.getFips()) { unsupportedCurves.push('brainpoolP256r1'); + } unsupportedCurves.forEach((ecdhCurve) => { assert.throws(() => tls.createServer({ ecdhCurve }), diff --git a/test/parallel/test-tls-ecdh.js b/test/parallel/test-tls-ecdh.js index 276b713f5ecf70..4d45e7f024586e 100644 --- a/test/parallel/test-tls-ecdh.js +++ b/test/parallel/test-tls-ecdh.js @@ -23,11 +23,15 @@ const common = require('../common'); const fixtures = require('../common/fixtures'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} -if (!common.opensslCli) +const { opensslCli } = require('../common/crypto'); + +if (!opensslCli) { common.skip('missing openssl-cli'); +} const assert = require('assert'); const tls = require('tls'); @@ -49,7 +53,7 @@ const server = tls.createServer(options, common.mustCall(function(conn) { })); server.listen(0, '127.0.0.1', common.mustCall(function() { - const cmd = common.escapePOSIXShell`"${common.opensslCli}" s_client -cipher ${ + const cmd = common.escapePOSIXShell`"${opensslCli}" s_client -cipher ${ options.ciphers} -connect 127.0.0.1:${this.address().port}`; exec(...cmd, common.mustSucceed((stdout, stderr) => { diff --git a/test/parallel/test-tls-empty-sni-context.js b/test/parallel/test-tls-empty-sni-context.js index 093e5cca712d2c..79f1ddd341d938 100644 --- a/test/parallel/test-tls-empty-sni-context.js +++ b/test/parallel/test-tls-empty-sni-context.js @@ -3,7 +3,7 @@ const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); - +const { hasOpenSSL } = require('../common/crypto'); const assert = require('assert'); const tls = require('tls'); @@ -26,7 +26,7 @@ const server = tls.createServer(options, (c) => { }, common.mustNotCall()); c.on('error', common.mustCall((err) => { - const expectedErr = common.hasOpenSSL(3, 2) ? + const expectedErr = hasOpenSSL(3, 2) ? 'ERR_SSL_SSL/TLS_ALERT_HANDSHAKE_FAILURE' : 'ERR_SSL_SSLV3_ALERT_HANDSHAKE_FAILURE'; assert.strictEqual(err.code, expectedErr); })); diff --git a/test/parallel/test-tls-getprotocol.js b/test/parallel/test-tls-getprotocol.js index a9c8775e2f112f..b1eab88fd6517e 100644 --- a/test/parallel/test-tls-getprotocol.js +++ b/test/parallel/test-tls-getprotocol.js @@ -3,6 +3,8 @@ const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); +const { hasOpenSSL } = require('../common/crypto'); + // This test ensures that `getProtocol` returns the right protocol // from a TLS connection @@ -14,11 +16,11 @@ const clientConfigs = [ { secureProtocol: 'TLSv1_method', version: 'TLSv1', - ciphers: (common.hasOpenSSL(3, 1) ? 'DEFAULT:@SECLEVEL=0' : 'DEFAULT') + ciphers: (hasOpenSSL(3, 1) ? 'DEFAULT:@SECLEVEL=0' : 'DEFAULT') }, { secureProtocol: 'TLSv1_1_method', version: 'TLSv1.1', - ciphers: (common.hasOpenSSL(3, 1) ? 'DEFAULT:@SECLEVEL=0' : 'DEFAULT') + ciphers: (hasOpenSSL(3, 1) ? 'DEFAULT:@SECLEVEL=0' : 'DEFAULT') }, { secureProtocol: 'TLSv1_2_method', version: 'TLSv1.2' diff --git a/test/parallel/test-tls-junk-server.js b/test/parallel/test-tls-junk-server.js index cc520383ede45f..0e536a66884e94 100644 --- a/test/parallel/test-tls-junk-server.js +++ b/test/parallel/test-tls-junk-server.js @@ -1,8 +1,11 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} + +const { hasOpenSSL } = require('../common/crypto'); const assert = require('assert'); const https = require('https'); @@ -21,7 +24,7 @@ server.listen(0, function() { req.end(); let expectedErrorMessage = new RegExp('wrong version number'); - if (common.hasOpenSSL(3, 2)) { + if (hasOpenSSL(3, 2)) { expectedErrorMessage = new RegExp('packet length too long'); }; req.once('error', common.mustCall(function(err) { diff --git a/test/parallel/test-tls-key-mismatch.js b/test/parallel/test-tls-key-mismatch.js index fdbb3676267a9d..df8848a03de4a9 100644 --- a/test/parallel/test-tls-key-mismatch.js +++ b/test/parallel/test-tls-key-mismatch.js @@ -22,14 +22,16 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} const fixtures = require('../common/fixtures'); +const { hasOpenSSL3 } = require('../common/crypto'); const assert = require('assert'); const tls = require('tls'); -const errorMessageRegex = common.hasOpenSSL3 ? +const errorMessageRegex = hasOpenSSL3 ? /^Error: error:05800074:x509 certificate routines::key values mismatch$/ : /^Error: error:0B080074:x509 certificate routines:X509_check_private_key:key values mismatch$/; diff --git a/test/parallel/test-tls-legacy-pfx.js b/test/parallel/test-tls-legacy-pfx.js index 33b4c58fc6ccc3..5106217718dbdc 100644 --- a/test/parallel/test-tls-legacy-pfx.js +++ b/test/parallel/test-tls-legacy-pfx.js @@ -1,9 +1,14 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); -if (!common.hasOpenSSL3) +} + +const { hasOpenSSL3 } = require('../common/crypto'); + +if (!hasOpenSSL3) { common.skip('OpenSSL legacy failures are only testable with OpenSSL 3+'); +} const fixtures = require('../common/fixtures'); diff --git a/test/parallel/test-tls-min-max-version.js b/test/parallel/test-tls-min-max-version.js index af32468eea6a68..4903d92f5c5700 100644 --- a/test/parallel/test-tls-min-max-version.js +++ b/test/parallel/test-tls-min-max-version.js @@ -1,5 +1,13 @@ 'use strict'; const common = require('../common'); + +if (!common.hasCrypto) { + common.skip('missing crypto'); +} +const { + hasOpenSSL, + hasOpenSSL3, +} = require('../common/crypto'); const fixtures = require('../common/fixtures'); const { inspect } = require('util'); @@ -16,13 +24,13 @@ function test(cmin, cmax, cprot, smin, smax, sprot, proto, cerr, serr) { assert(proto || cerr || serr, 'test missing any expectations'); let ciphers; - if (common.hasOpenSSL3 && (proto === 'TLSv1' || proto === 'TLSv1.1' || + if (hasOpenSSL3 && (proto === 'TLSv1' || proto === 'TLSv1.1' || proto === 'TLSv1_1_method' || proto === 'TLSv1_method' || sprot === 'TLSv1_1_method' || sprot === 'TLSv1_method')) { if (serr !== 'ERR_SSL_UNSUPPORTED_PROTOCOL') ciphers = 'ALL@SECLEVEL=0'; } - if (common.hasOpenSSL(3, 1) && cerr === 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION') { + if (hasOpenSSL(3, 1) && cerr === 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION') { ciphers = 'DEFAULT@SECLEVEL=0'; } // Report where test was called from. Strip leading garbage from @@ -125,9 +133,9 @@ test(U, U, 'TLS_method', U, U, 'TLSv1_method', 'TLSv1'); // OpenSSL 1.1.1 and 3.0 use a different error code and alert (sent to the // client) when no protocols are enabled on the server. -const NO_PROTOCOLS_AVAILABLE_SERVER = common.hasOpenSSL3 ? +const NO_PROTOCOLS_AVAILABLE_SERVER = hasOpenSSL3 ? 'ERR_SSL_NO_PROTOCOLS_AVAILABLE' : 'ERR_SSL_INTERNAL_ERROR'; -const NO_PROTOCOLS_AVAILABLE_SERVER_ALERT = common.hasOpenSSL3 ? +const NO_PROTOCOLS_AVAILABLE_SERVER_ALERT = hasOpenSSL3 ? 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION' : 'ERR_SSL_TLSV1_ALERT_INTERNAL_ERROR'; // SSLv23 also means "any supported protocol" greater than the default diff --git a/test/parallel/test-tls-no-sslv3.js b/test/parallel/test-tls-no-sslv3.js index 9282beb4bdac2c..cd5f4ad944a6c5 100644 --- a/test/parallel/test-tls-no-sslv3.js +++ b/test/parallel/test-tls-no-sslv3.js @@ -1,10 +1,14 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} -if (common.opensslCli === false) +const { opensslCli } = require('../common/crypto'); + +if (opensslCli === false) { common.skip('node compiled without OpenSSL CLI.'); +} const assert = require('assert'); const tls = require('tls'); @@ -23,7 +27,7 @@ server.listen(0, '127.0.0.1', function() { '-ssl3', '-connect', address]; - const client = spawn(common.opensslCli, args, { stdio: 'pipe' }); + const client = spawn(opensslCli, args, { stdio: 'pipe' }); client.stdout.pipe(process.stdout); client.stderr.pipe(process.stderr); client.stderr.setEncoding('utf8'); diff --git a/test/parallel/test-tls-ocsp-callback.js b/test/parallel/test-tls-ocsp-callback.js index 04a60a0890c506..bdf622d4686ec1 100644 --- a/test/parallel/test-tls-ocsp-callback.js +++ b/test/parallel/test-tls-ocsp-callback.js @@ -22,12 +22,17 @@ 'use strict'; const common = require('../common'); -if (!common.opensslCli) - common.skip('node compiled without OpenSSL CLI.'); - -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} + +const { opensslCli } = require('../common/crypto'); + +if (!opensslCli) { + common.skip('node compiled without OpenSSL CLI.'); +} +const crypto = require('crypto'); const tls = require('tls'); const fixtures = require('../common/fixtures'); @@ -108,6 +113,6 @@ test({ ocsp: true, response: false }); test({ ocsp: true, response: 'hello world' }); test({ ocsp: false }); -if (!common.hasFipsCrypto) { +if (!crypto.getFips()) { test({ ocsp: true, response: 'hello pfx', pfx: pfx, passphrase: 'sample' }); } diff --git a/test/parallel/test-tls-psk-circuit.js b/test/parallel/test-tls-psk-circuit.js index c06e61c321ef67..61861ecf4dafa6 100644 --- a/test/parallel/test-tls-psk-circuit.js +++ b/test/parallel/test-tls-psk-circuit.js @@ -1,9 +1,11 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} +const { hasOpenSSL } = require('../common/crypto'); const assert = require('assert'); const tls = require('tls'); @@ -62,12 +64,12 @@ test({ psk: USERS.UserA, identity: 'UserA' }, { minVersion: 'TLSv1.3' }); test({ psk: USERS.UserB, identity: 'UserB' }); test({ psk: USERS.UserB, identity: 'UserB' }, { minVersion: 'TLSv1.3' }); // Unrecognized user should fail handshake -const expectedHandshakeErr = common.hasOpenSSL(3, 2) ? +const expectedHandshakeErr = hasOpenSSL(3, 2) ? 'ERR_SSL_SSL/TLS_ALERT_HANDSHAKE_FAILURE' : 'ERR_SSL_SSLV3_ALERT_HANDSHAKE_FAILURE'; test({ psk: USERS.UserB, identity: 'UserC' }, {}, expectedHandshakeErr); // Recognized user but incorrect secret should fail handshake -const expectedIllegalParameterErr = common.hasOpenSSL(3, 4) ? 'ERR_SSL_TLSV1_ALERT_DECRYPT_ERROR' : - common.hasOpenSSL(3, 2) ? +const expectedIllegalParameterErr = hasOpenSSL(3, 4) ? 'ERR_SSL_TLSV1_ALERT_DECRYPT_ERROR' : + hasOpenSSL(3, 2) ? 'ERR_SSL_SSL/TLS_ALERT_ILLEGAL_PARAMETER' : 'ERR_SSL_SSLV3_ALERT_ILLEGAL_PARAMETER'; test({ psk: USERS.UserA, identity: 'UserB' }, {}, expectedIllegalParameterErr); test({ psk: USERS.UserB, identity: 'UserB' }); diff --git a/test/parallel/test-tls-psk-server.js b/test/parallel/test-tls-psk-server.js index b9260958401522..87fad86083e1ab 100644 --- a/test/parallel/test-tls-psk-server.js +++ b/test/parallel/test-tls-psk-server.js @@ -1,10 +1,15 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); -if (!common.opensslCli) +} + +const { opensslCli } = require('../common/crypto'); + +if (!opensslCli) { common.skip('missing openssl cli'); +} const assert = require('assert'); @@ -41,7 +46,7 @@ let sentWorld = false; let gotWorld = false; server.listen(0, () => { - const client = spawn(common.opensslCli, [ + const client = spawn(opensslCli, [ 's_client', '-connect', `127.0.0.1:${server.address().port}`, '-cipher', CIPHERS, diff --git a/test/parallel/test-tls-securepair-server.js b/test/parallel/test-tls-securepair-server.js index 78cd9f725401ed..fb4ebe6a2511cf 100644 --- a/test/parallel/test-tls-securepair-server.js +++ b/test/parallel/test-tls-securepair-server.js @@ -21,11 +21,15 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} + +const { opensslCli } = require('../common/crypto'); -if (!common.opensslCli) +if (!opensslCli) { common.skip('missing openssl-cli'); +} const assert = require('assert'); const tls = require('tls'); @@ -109,7 +113,7 @@ server.listen(0, common.mustCall(function() { const args = ['s_client', '-connect', `127.0.0.1:${this.address().port}`]; - const client = spawn(common.opensslCli, args); + const client = spawn(opensslCli, args); let out = ''; diff --git a/test/parallel/test-tls-server-verify.js b/test/parallel/test-tls-server-verify.js index 51ccd0d747fdf5..2517c7c8dbbb1f 100644 --- a/test/parallel/test-tls-server-verify.js +++ b/test/parallel/test-tls-server-verify.js @@ -22,11 +22,15 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} + +const { opensslCli } = require('../common/crypto'); -if (!common.opensslCli) +if (!opensslCli) { common.skip('node compiled without OpenSSL CLI.'); +} // This is a rather complex test which sets up various TLS servers with node // and connects to them using the 'openssl s_client' command line utility @@ -188,7 +192,7 @@ function runClient(prefix, port, options, cb) { } // To test use: openssl s_client -connect localhost:8000 - const client = spawn(common.opensslCli, args); + const client = spawn(opensslCli, args); let out = ''; diff --git a/test/parallel/test-tls-session-cache.js b/test/parallel/test-tls-session-cache.js index b55e150401d8a2..9524764aa609ee 100644 --- a/test/parallel/test-tls-session-cache.js +++ b/test/parallel/test-tls-session-cache.js @@ -21,17 +21,23 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} +const { + hasOpenSSL, + opensslCli, +} = require('../common/crypto'); + +if (!opensslCli) { + common.skip('node compiled without OpenSSL CLI.'); +} + const fixtures = require('../common/fixtures'); const assert = require('assert'); const tls = require('tls'); const { spawn } = require('child_process'); -if (!common.opensslCli) - common.skip('node compiled without OpenSSL CLI.'); - - doTest({ tickets: false }, function() { doTest({ tickets: true }, function() { doTest({ tickets: false, invalidSession: true }, function() { @@ -100,7 +106,7 @@ function doTest(testOptions, callback) { const args = [ 's_client', '-tls1', - '-cipher', (common.hasOpenSSL(3, 1) ? 'DEFAULT:@SECLEVEL=0' : 'DEFAULT'), + '-cipher', (hasOpenSSL(3, 1) ? 'DEFAULT:@SECLEVEL=0' : 'DEFAULT'), '-connect', `localhost:${this.address().port}`, '-servername', 'ohgod', '-key', fixtures.path('keys/rsa_private.pem'), @@ -109,7 +115,7 @@ function doTest(testOptions, callback) { ].concat(testOptions.tickets ? [] : '-no_ticket'); function spawnClient() { - const client = spawn(common.opensslCli, args, { + const client = spawn(opensslCli, args, { stdio: [ 0, 1, 'pipe' ] }); let err = ''; diff --git a/test/parallel/test-tls-set-ciphers.js b/test/parallel/test-tls-set-ciphers.js index f7062e73c9403c..1e63e9376e134b 100644 --- a/test/parallel/test-tls-set-ciphers.js +++ b/test/parallel/test-tls-set-ciphers.js @@ -1,7 +1,17 @@ 'use strict'; const common = require('../common'); -if (!common.hasOpenSSL3) +if (!common.hasCrypto) { common.skip('missing crypto, or OpenSSL version lower than 3'); +} + +const { + hasOpenSSL, + hasOpenSSL3, +} = require('../common/crypto'); + +if (!hasOpenSSL3) { + common.skip('missing crypto, or OpenSSL version lower than 3'); +} const fixtures = require('../common/fixtures'); const { inspect } = require('util'); @@ -80,7 +90,7 @@ function test(cciphers, sciphers, cipher, cerr, serr, options) { const U = undefined; let expectedTLSAlertError = 'ERR_SSL_SSLV3_ALERT_HANDSHAKE_FAILURE'; -if (common.hasOpenSSL(3, 2)) { +if (hasOpenSSL(3, 2)) { expectedTLSAlertError = 'ERR_SSL_SSL/TLS_ALERT_HANDSHAKE_FAILURE'; } @@ -117,7 +127,7 @@ test(U, 'AES256-SHA', 'TLS_AES_256_GCM_SHA384', U, U, { maxVersion: 'TLSv1.3' }) // default, but work. // However, for OpenSSL32 AES_128 is not enabled due to the // default security level -if (!common.hasOpenSSL(3, 2)) { +if (!hasOpenSSL(3, 2)) { test('TLS_AES_128_CCM_8_SHA256', U, U, 'ERR_SSL_SSLV3_ALERT_HANDSHAKE_FAILURE', 'ERR_SSL_NO_SHARED_CIPHER'); diff --git a/test/parallel/test-tls-set-secure-context.js b/test/parallel/test-tls-set-secure-context.js index c056875e14ddfb..3d2de6b3321414 100644 --- a/test/parallel/test-tls-set-secure-context.js +++ b/test/parallel/test-tls-set-secure-context.js @@ -1,8 +1,9 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} // This test verifies the behavior of the tls setSecureContext() method. // It also verifies that existing connections are not disrupted when the @@ -12,6 +13,7 @@ const assert = require('assert'); const events = require('events'); const https = require('https'); const timers = require('timers/promises'); +const { hasOpenSSL3 } = require('../common/crypto'); const fixtures = require('../common/fixtures'); const credentialOptions = [ { @@ -55,7 +57,7 @@ server.listen(0, common.mustCall(() => { server.setSecureContext(credentialOptions[1]); firstResponse.write('request-'); - const errorMessageRegex = common.hasOpenSSL3 ? + const errorMessageRegex = hasOpenSSL3 ? /^Error: self-signed certificate$/ : /^Error: self signed certificate$/; await assert.rejects(makeRequest(port, 3), errorMessageRegex); diff --git a/test/parallel/test-tls-set-sigalgs.js b/test/parallel/test-tls-set-sigalgs.js index 3f3d152f4d877e..985ca13ba2ac7d 100644 --- a/test/parallel/test-tls-set-sigalgs.js +++ b/test/parallel/test-tls-set-sigalgs.js @@ -1,6 +1,9 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) common.skip('missing crypto'); +if (!common.hasCrypto) { + common.skip('missing crypto'); +} +const { hasOpenSSL } = require('../common/crypto'); const fixtures = require('../common/fixtures'); // Test sigalgs: option for TLS. @@ -63,7 +66,7 @@ test('RSA-PSS+SHA256:RSA-PSS+SHA512:ECDSA+SHA256', ['RSA-PSS+SHA256', 'ECDSA+SHA256']); // Do not have shared sigalgs. -const handshakeErr = common.hasOpenSSL(3, 2) ? +const handshakeErr = hasOpenSSL(3, 2) ? 'ERR_SSL_SSL/TLS_ALERT_HANDSHAKE_FAILURE' : 'ERR_SSL_SSLV3_ALERT_HANDSHAKE_FAILURE'; test('RSA-PSS+SHA384', 'ECDSA+SHA256', undefined, handshakeErr, diff --git a/test/parallel/test-trace-env.js b/test/parallel/test-trace-env.js index ba08e0af2aad1d..7a7b80fa4c1094 100644 --- a/test/parallel/test-trace-env.js +++ b/test/parallel/test-trace-env.js @@ -18,9 +18,11 @@ spawnSyncAndAssert(process.execPath, ['--trace-env', fixtures.path('empty.js')], } if (common.hasCrypto) { assert.match(output, /get "NODE_EXTRA_CA_CERTS"/); - } - if (common.hasOpenSSL3) { - assert.match(output, /get "OPENSSL_CONF"/); + + const { hasOpenSSL3 } = require('../common/crypto'); + if (hasOpenSSL3) { + assert.match(output, /get "OPENSSL_CONF"/); + } } assert.match(output, /get "NODE_DEBUG_NATIVE"/); assert.match(output, /get "NODE_COMPILE_CACHE"/); diff --git a/test/parallel/test-x509-escaping.js b/test/parallel/test-x509-escaping.js index e6ae4d886908cb..b507af88e1f7f3 100644 --- a/test/parallel/test-x509-escaping.js +++ b/test/parallel/test-x509-escaping.js @@ -1,15 +1,16 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} const assert = require('assert'); const { X509Certificate } = require('crypto'); const tls = require('tls'); const fixtures = require('../common/fixtures'); -const { hasOpenSSL3 } = common; +const { hasOpenSSL3 } = require('../common/crypto'); // Test that all certificate chains provided by the reporter are rejected. { diff --git a/test/pummel/test-crypto-dh-hash.js b/test/pummel/test-crypto-dh-hash.js index ef5a640688c9bb..b59f556a2042b9 100644 --- a/test/pummel/test-crypto-dh-hash.js +++ b/test/pummel/test-crypto-dh-hash.js @@ -30,7 +30,9 @@ if (common.isPi) { common.skip('Too slow for Raspberry Pi devices'); } -if (!common.hasOpenSSL3) { +const { hasOpenSSL3 } = require('../common/crypto'); + +if (!hasOpenSSL3) { common.skip('Too slow when dynamically linked against OpenSSL 1.1.1'); } diff --git a/test/pummel/test-crypto-dh-keys.js b/test/pummel/test-crypto-dh-keys.js index 2caa4e244a9859..abce6a07acf4ac 100644 --- a/test/pummel/test-crypto-dh-keys.js +++ b/test/pummel/test-crypto-dh-keys.js @@ -36,8 +36,9 @@ const crypto = require('crypto'); [ 'modp1', 'modp2', 'modp5', 'modp14', 'modp15', 'modp16', 'modp17' ] .forEach((name) => { // modp1 is 768 bits, FIPS requires >= 1024 - if (name === 'modp1' && common.hasFipsCrypto) + if (name === 'modp1' && crypto.getFips()) { return; + } const group1 = crypto.getDiffieHellman(name); const group2 = crypto.getDiffieHellman(name); group1.generateKeys(); diff --git a/test/pummel/test-dh-regr.js b/test/pummel/test-dh-regr.js index 41d5bf872f97ec..cfae57d0728bdb 100644 --- a/test/pummel/test-dh-regr.js +++ b/test/pummel/test-dh-regr.js @@ -32,10 +32,11 @@ if (common.isPi) { const assert = require('assert'); const crypto = require('crypto'); +const { hasOpenSSL3 } = require('../common/crypto'); // FIPS requires length >= 1024 but we use 512/256 in this test to keep it from // taking too long and timing out in CI. -const length = (common.hasFipsCrypto) ? 1024 : common.hasOpenSSL3 ? 512 : 256; +const length = crypto.getFips() ? 1024 : hasOpenSSL3 ? 512 : 256; const p = crypto.createDiffieHellman(length).getPrime(); diff --git a/test/sequential/test-tls-psk-client.js b/test/sequential/test-tls-psk-client.js index ddebc8f8cc9807..c07b1f92d98376 100644 --- a/test/sequential/test-tls-psk-client.js +++ b/test/sequential/test-tls-psk-client.js @@ -1,10 +1,15 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); -if (!common.opensslCli) +} + +const { opensslCli } = require('../common/crypto'); + +if (!opensslCli) { common.skip('missing openssl cli'); +} const assert = require('assert'); const tls = require('tls'); @@ -16,7 +21,7 @@ const KEY = 'd731ef57be09e5204f0b205b60627028'; const IDENTITY = 'Client_identity'; // Hardcoded by `openssl s_server` const useIPv4 = !common.hasIPv6; -const server = spawn(common.opensslCli, [ +const server = spawn(opensslCli, [ 's_server', '-accept', common.PORT, '-cipher', CIPHERS, diff --git a/test/sequential/test-tls-securepair-client.js b/test/sequential/test-tls-securepair-client.js index f3ca42ad6edfb0..262518621b5f3f 100644 --- a/test/sequential/test-tls-securepair-client.js +++ b/test/sequential/test-tls-securepair-client.js @@ -23,14 +23,19 @@ const common = require('../common'); -if (!common.opensslCli) - common.skip('node compiled without OpenSSL CLI.'); - -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} + +const { opensslCli } = require('../common/crypto'); -if (common.isWindows) +if (!opensslCli) { + common.skip('node compiled without OpenSSL CLI.'); +} + +if (common.isWindows) { common.skip('test does not work on Windows'); // ...but it should! +} const net = require('net'); const assert = require('assert'); @@ -63,11 +68,11 @@ function test(keyPath, certPath, check, next) { const key = fixtures.readSync(keyPath).toString(); const cert = fixtures.readSync(certPath).toString(); - const server = spawn(common.opensslCli, ['s_server', - '-accept', 0, - '-cert', fixtures.path(certPath), - '-key', fixtures.path(keyPath), - ...(useIPv4 ? ['-4'] : []), + const server = spawn(opensslCli, ['s_server', + '-accept', 0, + '-cert', fixtures.path(certPath), + '-key', fixtures.path(keyPath), + ...(useIPv4 ? ['-4'] : []), ]); server.stdout.pipe(process.stdout); server.stderr.pipe(process.stdout); diff --git a/test/sequential/test-tls-session-timeout.js b/test/sequential/test-tls-session-timeout.js index 09107011aeda52..a93cdc793a2337 100644 --- a/test/sequential/test-tls-session-timeout.js +++ b/test/sequential/test-tls-session-timeout.js @@ -22,8 +22,11 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} + +const { opensslCli } = require('../common/crypto'); const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); @@ -56,8 +59,9 @@ const cert = fixtures.readKey('rsa_cert.crt'); } } -if (!common.opensslCli) +if (!opensslCli) { common.skip('node compiled without OpenSSL CLI.'); +} doTest(); @@ -105,7 +109,7 @@ function doTest() { '-sess_in', sessionFileName, '-sess_out', sessionFileName, ]; - const client = spawn(common.opensslCli, flags, { + const client = spawn(opensslCli, flags, { stdio: ['ignore', 'pipe', 'ignore'] }); From f07300cfa32c197684fe44d39965770b2fcfc23d Mon Sep 17 00:00:00 2001 From: James M Snell Date: Fri, 24 Jan 2025 17:35:59 -0800 Subject: [PATCH 08/38] test: move hasMultiLocalhost to common/net Given that `common/net` already exists and hasMultiLocalhost is net specific, let's move it out of common/index to better encapsulate and simplify common/index more PR-URL: https://github.com/nodejs/node/pull/56716 Reviewed-By: Yagiz Nizipli Reviewed-By: Richard Lau Reviewed-By: Luigi Pinca --- test/common/README.md | 6 ------ test/common/index.js | 10 ---------- test/common/index.mjs | 2 -- test/common/net.js | 10 ++++++++++ test/parallel/test-http-localaddress.js | 4 +++- test/parallel/test-http2-connect-options.js | 4 +++- test/parallel/test-https-localaddress.js | 4 +++- 7 files changed, 19 insertions(+), 21 deletions(-) diff --git a/test/common/README.md b/test/common/README.md index ee36503f920001..9ecee39b64a3df 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -238,12 +238,6 @@ Indicates if [internationalization][] is supported. Indicates whether `IPv6` is supported on this platform. -### `hasMultiLocalhost` - -* [\][] - -Indicates if there are multiple localhosts available. - ### `inFreeBSDJail` * [\][] diff --git a/test/common/index.js b/test/common/index.js index d2c39578324600..e8bf65d0a6edb4 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -489,15 +489,6 @@ function _mustCallInner(fn, criteria = 1, field) { return _return; } -function hasMultiLocalhost() { - const { internalBinding } = require('internal/test/binding'); - const { TCP, constants: TCPConstants } = internalBinding('tcp_wrap'); - const t = new TCP(TCPConstants.SOCKET); - const ret = t.bind('127.0.0.2', 0); - t.close(); - return ret === 0; -} - function skipIfEslintMissing() { if (!fs.existsSync( path.join(__dirname, '..', '..', 'tools', 'eslint', 'node_modules', 'eslint'), @@ -965,7 +956,6 @@ const common = { hasIntl, hasCrypto, hasQuic, - hasMultiLocalhost, invalidArgTypeHelper, isAlive, isASan, diff --git a/test/common/index.mjs b/test/common/index.mjs index 23328ac90ea3c9..090659f93be8ef 100644 --- a/test/common/index.mjs +++ b/test/common/index.mjs @@ -20,7 +20,6 @@ const { hasCrypto, hasIntl, hasIPv6, - hasMultiLocalhost, isAIX, isAlive, isDumbTerminal, @@ -75,7 +74,6 @@ export { hasCrypto, hasIntl, hasIPv6, - hasMultiLocalhost, isAIX, isAlive, isDumbTerminal, diff --git a/test/common/net.js b/test/common/net.js index 84eddd0966ed56..3886c542421005 100644 --- a/test/common/net.js +++ b/test/common/net.js @@ -17,7 +17,17 @@ function checkSupportReusePort() { }); } +function hasMultiLocalhost() { + const { internalBinding } = require('internal/test/binding'); + const { TCP, constants: TCPConstants } = internalBinding('tcp_wrap'); + const t = new TCP(TCPConstants.SOCKET); + const ret = t.bind('127.0.0.2', 0); + t.close(); + return ret === 0; +} + module.exports = { checkSupportReusePort, + hasMultiLocalhost, options, }; diff --git a/test/parallel/test-http-localaddress.js b/test/parallel/test-http-localaddress.js index a0e4bb80a3f8c2..da25ab3047613f 100644 --- a/test/parallel/test-http-localaddress.js +++ b/test/parallel/test-http-localaddress.js @@ -22,8 +22,10 @@ // Flags: --expose-internals 'use strict'; const common = require('../common'); -if (!common.hasMultiLocalhost()) +const { hasMultiLocalhost } = require('../common/net'); +if (!hasMultiLocalhost()) { common.skip('platform-specific test.'); +} const http = require('http'); const assert = require('assert'); diff --git a/test/parallel/test-http2-connect-options.js b/test/parallel/test-http2-connect-options.js index 233ced016974e2..1abcee99e06433 100644 --- a/test/parallel/test-http2-connect-options.js +++ b/test/parallel/test-http2-connect-options.js @@ -4,8 +4,10 @@ const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); -if (!common.hasMultiLocalhost()) +const { hasMultiLocalhost } = require('../common/net'); +if (!hasMultiLocalhost()) { common.skip('platform-specific test.'); +} const http2 = require('http2'); const assert = require('assert'); diff --git a/test/parallel/test-https-localaddress.js b/test/parallel/test-https-localaddress.js index 0de0974dc69b04..2a4629b34e4105 100644 --- a/test/parallel/test-https-localaddress.js +++ b/test/parallel/test-https-localaddress.js @@ -25,8 +25,10 @@ const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); -if (!common.hasMultiLocalhost()) +const { hasMultiLocalhost } = require('../common/net'); +if (!hasMultiLocalhost()) { common.skip('platform-specific test.'); +} const fixtures = require('../common/fixtures'); const assert = require('assert'); From 4a5d2c7538b412eea84a0f41544784b1b8ed7c8c Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Sat, 25 Jan 2025 03:30:27 +0100 Subject: [PATCH 09/38] module: integrate TypeScript into compile cache This integrates TypeScript into the compile cache by caching the transpilation (either type-stripping or transforming) output in addition to the V8 code cache that's generated from the transpilation output. Locally this speeds up loading with type stripping of `benchmark/fixtures/strip-types-benchmark.ts` by ~65% and loading with type transforms of `fixtures/transform-types-benchmark.ts` by ~128%. When comparing loading .ts and loading pre-transpiled .js on-disk with the compile cache enabled, previously .ts loaded 46% slower with type-stripping and 66% slower with transforms compared to loading .js files directly. After this patch, .ts loads 12% slower with type-stripping and 22% slower with transforms compared to .js. (Note that the numbers are based on microbenchmark fixtures and do not necessarily represent real-world workloads, though with bigger real-world files, the speed up should be more significant). PR-URL: https://github.com/nodejs/node/pull/56629 Fixes: https://github.com/nodejs/node/issues/54741 Reviewed-By: Geoffrey Booth Reviewed-By: Marco Ippolito Reviewed-By: James M Snell --- lib/internal/modules/typescript.js | 63 ++++++- src/compile_cache.cc | 65 ++++++- src/compile_cache.h | 15 +- src/node_modules.cc | 96 ++++++++++ .../test-compile-cache-typescript-commonjs.js | 166 +++++++++++++++++ .../test-compile-cache-typescript-esm.js | 167 ++++++++++++++++++ ...est-compile-cache-typescript-strip-miss.js | 104 +++++++++++ ...mpile-cache-typescript-strip-sourcemaps.js | 59 +++++++ ...test-compile-cache-typescript-transform.js | 127 +++++++++++++ 9 files changed, 846 insertions(+), 16 deletions(-) create mode 100644 test/parallel/test-compile-cache-typescript-commonjs.js create mode 100644 test/parallel/test-compile-cache-typescript-esm.js create mode 100644 test/parallel/test-compile-cache-typescript-strip-miss.js create mode 100644 test/parallel/test-compile-cache-typescript-strip-sourcemaps.js create mode 100644 test/parallel/test-compile-cache-typescript-transform.js diff --git a/lib/internal/modules/typescript.js b/lib/internal/modules/typescript.js index 6abfc707657b92..17bbc6ba944432 100644 --- a/lib/internal/modules/typescript.js +++ b/lib/internal/modules/typescript.js @@ -22,6 +22,11 @@ const { const { getOptionValue } = require('internal/options'); const assert = require('internal/assert'); const { Buffer } = require('buffer'); +const { + getCompileCacheEntry, + saveCompileCacheEntry, + cachedCodeTypes: { kStrippedTypeScript, kTransformedTypeScript, kTransformedTypeScriptWithSourceMaps }, +} = internalBinding('modules'); /** * The TypeScript parsing mode, either 'strip-only' or 'transform'. @@ -105,11 +110,19 @@ function stripTypeScriptTypes(code, options = kEmptyObject) { }); } +/** + * @typedef {'strip-only' | 'transform'} TypeScriptMode + * @typedef {object} TypeScriptOptions + * @property {TypeScriptMode} mode Mode. + * @property {boolean} sourceMap Whether to generate source maps. + * @property {string|undefined} filename Filename. + */ + /** * Processes TypeScript code by stripping types or transforming. * Handles source maps if needed. * @param {string} code TypeScript code to process. - * @param {object} options The configuration object. + * @param {TypeScriptOptions} options The configuration object. * @returns {string} The processed code. */ function processTypeScriptCode(code, options) { @@ -126,6 +139,20 @@ function processTypeScriptCode(code, options) { return transformedCode; } +/** + * Get the type enum used for compile cache. + * @param {TypeScriptMode} mode Mode of transpilation. + * @param {boolean} sourceMap Whether source maps are enabled. + * @returns {number} + */ +function getCachedCodeType(mode, sourceMap) { + if (mode === 'transform') { + if (sourceMap) { return kTransformedTypeScriptWithSourceMaps; } + return kTransformedTypeScript; + } + return kStrippedTypeScript; +} + /** * Performs type-stripping to TypeScript source code internally. * It is used by internal loaders. @@ -142,12 +169,40 @@ function stripTypeScriptModuleTypes(source, filename, emitWarning = true) { if (isUnderNodeModules(filename)) { throw new ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING(filename); } + const sourceMap = getOptionValue('--enable-source-maps'); + + const mode = getTypeScriptParsingMode(); + + // Instead of caching the compile cache status, just go into C++ to fetch it, + // as checking process.env equally involves calling into C++ anyway, and + // the compile cache can be enabled dynamically. + const type = getCachedCodeType(mode, sourceMap); + // Get a compile cache entry into the native compile cache store, + // keyed by the filename. If the cache can already be loaded on disk, + // cached.transpiled contains the cached string. Otherwise we should do + // the transpilation and save it in the native store later using + // saveCompileCacheEntry(). + const cached = (filename ? getCompileCacheEntry(source, filename, type) : undefined); + if (cached?.transpiled) { // TODO(joyeecheung): return Buffer here. + return cached.transpiled; + } + const options = { - mode: getTypeScriptParsingMode(), - sourceMap: getOptionValue('--enable-source-maps'), + mode, + sourceMap, filename, }; - return processTypeScriptCode(source, options); + + const transpiled = processTypeScriptCode(source, options); + if (cached) { + // cached.external contains a pointer to the native cache entry. + // The cached object would be unreachable once it's out of scope, + // but the pointer inside cached.external would stay around for reuse until + // environment shutdown or when the cache is manually flushed + // to disk. Unwrap it in JS before passing into C++ since it's faster. + saveCompileCacheEntry(cached.external, transpiled); + } + return transpiled; } /** diff --git a/src/compile_cache.cc b/src/compile_cache.cc index 50697bcfe1671d..f13797e5f50288 100644 --- a/src/compile_cache.cc +++ b/src/compile_cache.cc @@ -77,10 +77,27 @@ v8::ScriptCompiler::CachedData* CompileCacheEntry::CopyCache() const { // See comments in CompileCacheHandler::Persist(). constexpr uint32_t kCacheMagicNumber = 0x8adfdbb2; +const char* CompileCacheEntry::type_name() const { + switch (type) { + case CachedCodeType::kCommonJS: + return "CommonJS"; + case CachedCodeType::kESM: + return "ESM"; + case CachedCodeType::kStrippedTypeScript: + return "StrippedTypeScript"; + case CachedCodeType::kTransformedTypeScript: + return "TransformedTypeScript"; + case CachedCodeType::kTransformedTypeScriptWithSourceMaps: + return "TransformedTypeScriptWithSourceMaps"; + default: + UNREACHABLE(); + } +} + void CompileCacheHandler::ReadCacheFile(CompileCacheEntry* entry) { Debug("[compile cache] reading cache from %s for %s %s...", entry->cache_filename, - entry->type == CachedCodeType::kCommonJS ? "CommonJS" : "ESM", + entry->type_name(), entry->source_filename); uv_fs_t req; @@ -256,7 +273,8 @@ void CompileCacheHandler::MaybeSaveImpl(CompileCacheEntry* entry, v8::Local func_or_mod, bool rejected) { DCHECK_NOT_NULL(entry); - Debug("[compile cache] cache for %s was %s, ", + Debug("[compile cache] V8 code cache for %s %s was %s, ", + entry->type_name(), entry->source_filename, rejected ? "rejected" : (entry->cache == nullptr) ? "not initialized" @@ -287,6 +305,25 @@ void CompileCacheHandler::MaybeSave(CompileCacheEntry* entry, MaybeSaveImpl(entry, func, rejected); } +void CompileCacheHandler::MaybeSave(CompileCacheEntry* entry, + std::string_view transpiled) { + CHECK(entry->type == CachedCodeType::kStrippedTypeScript || + entry->type == CachedCodeType::kTransformedTypeScript || + entry->type == CachedCodeType::kTransformedTypeScriptWithSourceMaps); + Debug("[compile cache] saving transpilation cache for %s %s\n", + entry->type_name(), + entry->source_filename); + + // TODO(joyeecheung): it's weird to copy it again here. Convert the v8::String + // directly into buffer held by v8::ScriptCompiler::CachedData here. + int cache_size = static_cast(transpiled.size()); + uint8_t* data = new uint8_t[cache_size]; + memcpy(data, transpiled.data(), cache_size); + entry->cache.reset(new v8::ScriptCompiler::CachedData( + data, cache_size, v8::ScriptCompiler::CachedData::BufferOwned)); + entry->refreshed = true; +} + /** * Persist the compile cache accumulated in memory to disk. * @@ -316,18 +353,25 @@ void CompileCacheHandler::Persist() { // incur a negligible overhead from thread synchronization. for (auto& pair : compiler_cache_store_) { auto* entry = pair.second.get(); + const char* type_name = entry->type_name(); if (entry->cache == nullptr) { - Debug("[compile cache] skip %s because the cache was not initialized\n", + Debug("[compile cache] skip persisting %s %s because the cache was not " + "initialized\n", + type_name, entry->source_filename); continue; } if (entry->refreshed == false) { - Debug("[compile cache] skip %s because cache was the same\n", - entry->source_filename); + Debug( + "[compile cache] skip persisting %s %s because cache was the same\n", + type_name, + entry->source_filename); continue; } if (entry->persisted == true) { - Debug("[compile cache] skip %s because cache was already persisted\n", + Debug("[compile cache] skip persisting %s %s because cache was already " + "persisted\n", + type_name, entry->source_filename); continue; } @@ -363,8 +407,9 @@ void CompileCacheHandler::Persist() { auto cleanup_mkstemp = OnScopeLeave([&mkstemp_req]() { uv_fs_req_cleanup(&mkstemp_req); }); std::string cache_filename_tmp = entry->cache_filename + ".XXXXXX"; - Debug("[compile cache] Creating temporary file for cache of %s...", - entry->source_filename); + Debug("[compile cache] Creating temporary file for cache of %s (%s)...", + entry->source_filename, + type_name); int err = uv_fs_mkstemp( nullptr, &mkstemp_req, cache_filename_tmp.c_str(), nullptr); if (err < 0) { @@ -372,8 +417,10 @@ void CompileCacheHandler::Persist() { continue; } Debug(" -> %s\n", mkstemp_req.path); - Debug("[compile cache] writing cache for %s to temporary file %s [%d %d %d " + Debug("[compile cache] writing cache for %s %s to temporary file %s [%d " + "%d %d " "%d %d]...", + type_name, entry->source_filename, mkstemp_req.path, headers[kMagicNumberOffset], diff --git a/src/compile_cache.h b/src/compile_cache.h index a7bb58c4a0be95..72910084e18bca 100644 --- a/src/compile_cache.h +++ b/src/compile_cache.h @@ -13,10 +13,17 @@ namespace node { class Environment; -// TODO(joyeecheung): move it into a CacheHandler class. +#define CACHED_CODE_TYPES(V) \ + V(kCommonJS, 0) \ + V(kESM, 1) \ + V(kStrippedTypeScript, 2) \ + V(kTransformedTypeScript, 3) \ + V(kTransformedTypeScriptWithSourceMaps, 4) + enum class CachedCodeType : uint8_t { - kCommonJS = 0, - kESM, +#define V(type, value) type = value, + CACHED_CODE_TYPES(V) +#undef V }; struct CompileCacheEntry { @@ -34,6 +41,7 @@ struct CompileCacheEntry { // Copy the cache into a new store for V8 to consume. Caller takes // ownership. v8::ScriptCompiler::CachedData* CopyCache() const; + const char* type_name() const; }; #define COMPILE_CACHE_STATUS(V) \ @@ -70,6 +78,7 @@ class CompileCacheHandler { void MaybeSave(CompileCacheEntry* entry, v8::Local mod, bool rejected); + void MaybeSave(CompileCacheEntry* entry, std::string_view transpiled); std::string_view cache_dir() { return compile_cache_dir_; } private: diff --git a/src/node_modules.cc b/src/node_modules.cc index 4b522a91323c9f..85c8e21cf026ff 100644 --- a/src/node_modules.cc +++ b/src/node_modules.cc @@ -1,6 +1,7 @@ #include "node_modules.h" #include #include "base_object-inl.h" +#include "compile_cache.h" #include "node_errors.h" #include "node_external_reference.h" #include "node_url.h" @@ -21,12 +22,16 @@ namespace modules { using v8::Array; using v8::Context; +using v8::External; using v8::FunctionCallbackInfo; using v8::HandleScope; +using v8::Integer; using v8::Isolate; using v8::Local; using v8::LocalVector; +using v8::Name; using v8::NewStringType; +using v8::Null; using v8::Object; using v8::ObjectTemplate; using v8::Primitive; @@ -498,6 +503,74 @@ void GetCompileCacheDir(const FunctionCallbackInfo& args) { .ToLocalChecked()); } +void GetCompileCacheEntry(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + CHECK(args[0]->IsString()); // TODO(joyeecheung): accept buffer. + CHECK(args[1]->IsString()); + CHECK(args[2]->IsUint32()); + Local context = isolate->GetCurrentContext(); + Environment* env = Environment::GetCurrent(context); + if (!env->use_compile_cache()) { + return; + } + Local source = args[0].As(); + Local filename = args[1].As(); + CachedCodeType type = + static_cast(args[2].As()->Value()); + auto* cache_entry = + env->compile_cache_handler()->GetOrInsert(source, filename, type); + if (cache_entry == nullptr) { + return; + } + + v8::LocalVector names(isolate, + {FIXED_ONE_BYTE_STRING(isolate, "external")}); + v8::LocalVector values(isolate, + {v8::External::New(isolate, cache_entry)}); + if (cache_entry->cache != nullptr) { + Debug(env, + DebugCategory::COMPILE_CACHE, + "[compile cache] retrieving transpile cache for %s %s...", + cache_entry->type_name(), + cache_entry->source_filename); + + std::string_view cache( + reinterpret_cast(cache_entry->cache->data), + cache_entry->cache->length); + Local transpiled; + // TODO(joyeecheung): convert with simdutf and into external strings + if (!ToV8Value(context, cache).ToLocal(&transpiled)) { + Debug(env, DebugCategory::COMPILE_CACHE, "failed\n"); + return; + } else { + Debug(env, DebugCategory::COMPILE_CACHE, "success\n"); + } + names.push_back(FIXED_ONE_BYTE_STRING(isolate, "transpiled")); + values.push_back(transpiled); + } else { + Debug(env, + DebugCategory::COMPILE_CACHE, + "[compile cache] no transpile cache for %s %s\n", + cache_entry->type_name(), + cache_entry->source_filename); + } + args.GetReturnValue().Set(Object::New( + isolate, v8::Null(isolate), names.data(), values.data(), names.size())); +} + +void SaveCompileCacheEntry(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + Local context = isolate->GetCurrentContext(); + Environment* env = Environment::GetCurrent(context); + DCHECK(env->use_compile_cache()); + CHECK(args[0]->IsExternal()); + CHECK(args[1]->IsString()); // TODO(joyeecheung): accept buffer. + auto* cache_entry = + static_cast(args[0].As()->Value()); + Utf8Value utf8(isolate, args[1].As()); + env->compile_cache_handler()->MaybeSave(cache_entry, utf8.ToStringView()); +} + void BindingData::CreatePerIsolateProperties(IsolateData* isolate_data, Local target) { Isolate* isolate = isolate_data->isolate(); @@ -514,6 +587,8 @@ void BindingData::CreatePerIsolateProperties(IsolateData* isolate_data, SetMethod(isolate, target, "enableCompileCache", EnableCompileCache); SetMethod(isolate, target, "getCompileCacheDir", GetCompileCacheDir); SetMethod(isolate, target, "flushCompileCache", FlushCompileCache); + SetMethod(isolate, target, "getCompileCacheEntry", GetCompileCacheEntry); + SetMethod(isolate, target, "saveCompileCacheEntry", SaveCompileCacheEntry); } void BindingData::CreatePerContextProperties(Local target, @@ -530,12 +605,31 @@ void BindingData::CreatePerContextProperties(Local target, compile_cache_status_values.push_back( \ FIXED_ONE_BYTE_STRING(isolate, #status)); COMPILE_CACHE_STATUS(V) +#undef V USE(target->Set(context, FIXED_ONE_BYTE_STRING(isolate, "compileCacheStatus"), Array::New(isolate, compile_cache_status_values.data(), compile_cache_status_values.size()))); + + LocalVector cached_code_type_keys(isolate); + LocalVector cached_code_type_values(isolate); + +#define V(type, value) \ + cached_code_type_keys.push_back(FIXED_ONE_BYTE_STRING(isolate, #type)); \ + cached_code_type_values.push_back(Integer::New(isolate, value)); \ + DCHECK_EQ(value, cached_code_type_values.size() - 1); + CACHED_CODE_TYPES(V) +#undef V + + USE(target->Set(context, + FIXED_ONE_BYTE_STRING(isolate, "cachedCodeTypes"), + Object::New(isolate, + Null(isolate), + cached_code_type_keys.data(), + cached_code_type_values.data(), + cached_code_type_keys.size()))); } void BindingData::RegisterExternalReferences( @@ -547,6 +641,8 @@ void BindingData::RegisterExternalReferences( registry->Register(EnableCompileCache); registry->Register(GetCompileCacheDir); registry->Register(FlushCompileCache); + registry->Register(GetCompileCacheEntry); + registry->Register(SaveCompileCacheEntry); } } // namespace modules diff --git a/test/parallel/test-compile-cache-typescript-commonjs.js b/test/parallel/test-compile-cache-typescript-commonjs.js new file mode 100644 index 00000000000000..b6c4581ed47be3 --- /dev/null +++ b/test/parallel/test-compile-cache-typescript-commonjs.js @@ -0,0 +1,166 @@ +'use strict'; + +// This tests NODE_COMPILE_CACHE works for CommonJS with types. + +require('../common'); +const { spawnSyncAndAssert } = require('../common/child_process'); +const assert = require('assert'); +const tmpdir = require('../common/tmpdir'); +const fixtures = require('../common/fixtures'); + +// Check cache for .ts files that would be run as CommonJS. +{ + tmpdir.refresh(); + const dir = tmpdir.resolve('.compile_cache_dir'); + const script = fixtures.path('typescript', 'ts', 'test-commonjs-parsing.ts'); + + spawnSyncAndAssert( + process.execPath, + [script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /saving transpilation cache for StrippedTypeScript .*test-commonjs-parsing\.ts/); + assert.match(output, /writing cache for StrippedTypeScript .*test-commonjs-parsing\.ts.*success/); + assert.match(output, /writing cache for CommonJS .*test-commonjs-parsing\.ts.*success/); + return true; + } + }); + + spawnSyncAndAssert( + process.execPath, + [script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /retrieving transpile cache for StrippedTypeScript .*test-commonjs-parsing\.ts.*success/); + assert.match(output, /reading cache from .* for CommonJS .*test-commonjs-parsing\.ts.*success/); + assert.match(output, /skip persisting StrippedTypeScript .*test-commonjs-parsing\.ts because cache was the same/); + assert.match(output, /V8 code cache for CommonJS .*test-commonjs-parsing\.ts was accepted, keeping the in-memory entry/); + assert.match(output, /skip persisting CommonJS .*test-commonjs-parsing\.ts because cache was the same/); + return true; + } + }); +} + +// Check cache for .cts files that require .cts files. +{ + tmpdir.refresh(); + const dir = tmpdir.resolve('.compile_cache_dir'); + const script = fixtures.path('typescript', 'cts', 'test-require-commonjs.cts'); + + spawnSyncAndAssert( + process.execPath, + [script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /writing cache for StrippedTypeScript .*test-require-commonjs\.cts.*success/); + assert.match(output, /writing cache for StrippedTypeScript .*test-cts-export-foo\.cts.*success/); + assert.match(output, /writing cache for CommonJS .*test-require-commonjs\.cts.*success/); + assert.match(output, /writing cache for CommonJS .*test-cts-export-foo\.cts.*success/); + return true; + } + }); + + spawnSyncAndAssert( + process.execPath, + [script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /retrieving transpile cache for StrippedTypeScript .*test-require-commonjs\.cts.*success/); + assert.match(output, /skip persisting StrippedTypeScript .*test-require-commonjs\.cts because cache was the same/); + assert.match(output, /retrieving transpile cache for StrippedTypeScript .*test-cts-export-foo\.cts.*success/); + assert.match(output, /skip persisting StrippedTypeScript .*test-cts-export-foo\.cts because cache was the same/); + + assert.match(output, /V8 code cache for CommonJS .*test-require-commonjs\.cts was accepted, keeping the in-memory entry/); + assert.match(output, /skip persisting CommonJS .*test-require-commonjs\.cts because cache was the same/); + assert.match(output, /V8 code cache for CommonJS .*test-cts-export-foo\.cts was accepted, keeping the in-memory entry/); + assert.match(output, /skip persisting CommonJS .*test-cts-export-foo\.cts because cache was the same/); + return true; + } + }); +} + +// Check cache for .cts files that require .mts files. +{ + tmpdir.refresh(); + const dir = tmpdir.resolve('.compile_cache_dir'); + const script = fixtures.path('typescript', 'cts', 'test-require-mts-module.cts'); + + spawnSyncAndAssert( + process.execPath, + [script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /writing cache for StrippedTypeScript .*test-require-mts-module\.cts.*success/); + assert.match(output, /writing cache for StrippedTypeScript .*test-mts-export-foo\.mts.*success/); + assert.match(output, /writing cache for CommonJS .*test-require-mts-module\.cts.*success/); + assert.match(output, /writing cache for ESM .*test-mts-export-foo\.mts.*success/); + return true; + } + }); + + spawnSyncAndAssert( + process.execPath, + [script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /retrieving transpile cache for StrippedTypeScript .*test-require-mts-module\.cts.*success/); + assert.match(output, /skip persisting StrippedTypeScript .*test-require-mts-module\.cts because cache was the same/); + assert.match(output, /retrieving transpile cache for StrippedTypeScript .*test-mts-export-foo\.mts.*success/); + assert.match(output, /skip persisting StrippedTypeScript .*test-mts-export-foo\.mts because cache was the same/); + + assert.match(output, /V8 code cache for CommonJS .*test-require-mts-module\.cts was accepted, keeping the in-memory entry/); + assert.match(output, /skip persisting CommonJS .*test-require-mts-module\.cts because cache was the same/); + assert.match(output, /V8 code cache for ESM .*test-mts-export-foo\.mts was accepted, keeping the in-memory entry/); + assert.match(output, /skip persisting ESM .*test-mts-export-foo\.mts because cache was the same/); + return true; + } + }); +} diff --git a/test/parallel/test-compile-cache-typescript-esm.js b/test/parallel/test-compile-cache-typescript-esm.js new file mode 100644 index 00000000000000..cec7b814da6679 --- /dev/null +++ b/test/parallel/test-compile-cache-typescript-esm.js @@ -0,0 +1,167 @@ +'use strict'; + +// This tests NODE_COMPILE_CACHE works for ESM with types. + +require('../common'); +const { spawnSyncAndAssert } = require('../common/child_process'); +const assert = require('assert'); +const tmpdir = require('../common/tmpdir'); +const fixtures = require('../common/fixtures'); + +// Check cache for .ts files that would be run as ESM. +{ + tmpdir.refresh(); + const dir = tmpdir.resolve('.compile_cache_dir'); + const script = fixtures.path('typescript', 'ts', 'test-module-typescript.ts'); + + spawnSyncAndAssert( + process.execPath, + [script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /saving transpilation cache for StrippedTypeScript .*test-module-typescript\.ts/); + assert.match(output, /writing cache for StrippedTypeScript .*test-module-typescript\.ts.*success/); + assert.match(output, /writing cache for ESM .*test-module-typescript\.ts.*success/); + return true; + } + }); + + spawnSyncAndAssert( + process.execPath, + [script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /retrieving transpile cache for StrippedTypeScript .*test-module-typescript\.ts.*success/); + assert.match(output, /reading cache from .* for ESM .*test-module-typescript\.ts.*success/); + assert.match(output, /skip persisting StrippedTypeScript .*test-module-typescript\.ts because cache was the same/); + assert.match(output, /V8 code cache for ESM .*test-module-typescript\.ts was accepted, keeping the in-memory entry/); + assert.match(output, /skip persisting ESM .*test-module-typescript\.ts because cache was the same/); + return true; + } + }); +} + +// Check cache for .mts files that import .mts files. +{ + tmpdir.refresh(); + const dir = tmpdir.resolve('.compile_cache_dir'); + const script = fixtures.path('typescript', 'mts', 'test-import-module.mts'); + + spawnSyncAndAssert( + process.execPath, + [script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /writing cache for StrippedTypeScript .*test-import-module\.mts.*success/); + assert.match(output, /writing cache for StrippedTypeScript .*test-mts-export-foo\.mts.*success/); + assert.match(output, /writing cache for ESM .*test-import-module\.mts.*success/); + assert.match(output, /writing cache for ESM .*test-mts-export-foo\.mts.*success/); + return true; + } + }); + + spawnSyncAndAssert( + process.execPath, + [script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /retrieving transpile cache for StrippedTypeScript .*test-import-module\.mts.*success/); + assert.match(output, /skip persisting StrippedTypeScript .*test-import-module\.mts because cache was the same/); + assert.match(output, /retrieving transpile cache for StrippedTypeScript .*test-mts-export-foo\.mts.*success/); + assert.match(output, /skip persisting StrippedTypeScript .*test-mts-export-foo\.mts because cache was the same/); + + assert.match(output, /V8 code cache for ESM .*test-import-module\.mts was accepted, keeping the in-memory entry/); + assert.match(output, /skip persisting ESM .*test-import-module\.mts because cache was the same/); + assert.match(output, /V8 code cache for ESM .*test-mts-export-foo\.mts was accepted, keeping the in-memory entry/); + assert.match(output, /skip persisting ESM .*test-mts-export-foo\.mts because cache was the same/); + return true; + } + }); +} + + +// Check cache for .mts files that import .cts files. +{ + tmpdir.refresh(); + const dir = tmpdir.resolve('.compile_cache_dir'); + const script = fixtures.path('typescript', 'mts', 'test-import-commonjs.mts'); + + spawnSyncAndAssert( + process.execPath, + [script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /writing cache for StrippedTypeScript .*test-import-commonjs\.mts.*success/); + assert.match(output, /writing cache for StrippedTypeScript .*test-cts-export-foo\.cts.*success/); + assert.match(output, /writing cache for ESM .*test-import-commonjs\.mts.*success/); + assert.match(output, /writing cache for CommonJS .*test-cts-export-foo\.cts.*success/); + return true; + } + }); + + spawnSyncAndAssert( + process.execPath, + [script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /retrieving transpile cache for StrippedTypeScript .*test-import-commonjs\.mts.*success/); + assert.match(output, /skip persisting StrippedTypeScript .*test-import-commonjs\.mts because cache was the same/); + assert.match(output, /retrieving transpile cache for StrippedTypeScript .*test-cts-export-foo\.cts.*success/); + assert.match(output, /skip persisting StrippedTypeScript .*test-cts-export-foo\.cts because cache was the same/); + + assert.match(output, /V8 code cache for ESM .*test-import-commonjs\.mts was accepted, keeping the in-memory entry/); + assert.match(output, /skip persisting ESM .*test-import-commonjs\.mts because cache was the same/); + assert.match(output, /V8 code cache for CommonJS .*test-cts-export-foo\.cts was accepted, keeping the in-memory entry/); + assert.match(output, /skip persisting CommonJS .*test-cts-export-foo\.cts because cache was the same/); + return true; + } + }); +} diff --git a/test/parallel/test-compile-cache-typescript-strip-miss.js b/test/parallel/test-compile-cache-typescript-strip-miss.js new file mode 100644 index 00000000000000..5d37a377f002e4 --- /dev/null +++ b/test/parallel/test-compile-cache-typescript-strip-miss.js @@ -0,0 +1,104 @@ +'use strict'; + +// This tests NODE_COMPILE_CACHE can handle cache invalidation +// between strip-only TypeScript and transformed TypeScript. + +require('../common'); +const { spawnSyncAndAssert } = require('../common/child_process'); +const assert = require('assert'); +const tmpdir = require('../common/tmpdir'); +const fixtures = require('../common/fixtures'); + +tmpdir.refresh(); +const dir = tmpdir.resolve('.compile_cache_dir'); +const script = fixtures.path('typescript', 'ts', 'test-typescript.ts'); + +spawnSyncAndAssert( + process.execPath, + [script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /saving transpilation cache for StrippedTypeScript .*test-typescript\.ts/); + assert.match(output, /writing cache for StrippedTypeScript .*test-typescript\.ts.*success/); + assert.match(output, /writing cache for CommonJS .*test-typescript\.ts.*success/); + return true; + } + }); + +// Reloading with transform should miss the cache generated without transform. +spawnSyncAndAssert( + process.execPath, + ['--experimental-transform-types', script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + // Both the transpile cache and the code cache should be missed. + assert.match(output, /no transpile cache for TransformedTypeScriptWithSourceMaps .*test-typescript\.ts/); + assert.match(output, /reading cache from .* for CommonJS .*test-typescript\.ts.*mismatch/); + // New cache with source map should be generated. + assert.match(output, /writing cache for TransformedTypeScriptWithSourceMaps .*test-typescript\.ts.*success/); + assert.match(output, /writing cache for CommonJS .*test-typescript\.ts.*success/); + return true; + } + }); + +// Reloading with transform should hit the cache generated with transform. +spawnSyncAndAssert( + process.execPath, + ['--experimental-transform-types', script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /retrieving transpile cache for TransformedTypeScriptWithSourceMaps .*test-typescript\.ts.*success/); + assert.match(output, /reading cache from .* for CommonJS .*test-typescript\.ts.*success/); + assert.match(output, /skip persisting TransformedTypeScriptWithSourceMaps .*test-typescript\.ts because cache was the same/); + assert.match(output, /V8 code cache for CommonJS .*test-typescript\.ts was accepted, keeping the in-memory entry/); + assert.match(output, /skip persisting CommonJS .*test-typescript\.ts because cache was the same/); + return true; + } + }); + +// Reloading without transform should hit the co-existing transpile cache generated without transform, +// but miss the code cache generated with transform. +spawnSyncAndAssert( + process.execPath, + [script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /retrieving transpile cache for StrippedTypeScript .*test-typescript\.ts.*success/); + assert.match(output, /reading cache from .* for CommonJS .*test-typescript\.ts.*mismatch/); + assert.match(output, /skip persisting StrippedTypeScript .*test-typescript\.ts because cache was the same/); + assert.match(output, /writing cache for CommonJS .*test-typescript\.ts.*success/); + return true; + } + }); diff --git a/test/parallel/test-compile-cache-typescript-strip-sourcemaps.js b/test/parallel/test-compile-cache-typescript-strip-sourcemaps.js new file mode 100644 index 00000000000000..da5e350496f005 --- /dev/null +++ b/test/parallel/test-compile-cache-typescript-strip-sourcemaps.js @@ -0,0 +1,59 @@ +'use strict'; + +// This tests NODE_COMPILE_CACHE can be used for type stripping and ignores +// --enable-source-maps as there's no difference in the code generated. + +require('../common'); +const { spawnSyncAndAssert } = require('../common/child_process'); +const assert = require('assert'); +const tmpdir = require('../common/tmpdir'); +const fixtures = require('../common/fixtures'); + +tmpdir.refresh(); +const dir = tmpdir.resolve('.compile_cache_dir'); +const script = fixtures.path('typescript', 'ts', 'test-typescript.ts'); + +spawnSyncAndAssert( + process.execPath, + [script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /saving transpilation cache for StrippedTypeScript .*test-typescript\.ts/); + assert.match(output, /writing cache for StrippedTypeScript .*test-typescript\.ts.*success/); + assert.match(output, /writing cache for CommonJS .*test-typescript\.ts.*success/); + return true; + } + }); + +// Reloading with source maps should hit the cache generated without source maps, because for +// type stripping, only sourceURL is added regardless of whether source map is enabled. +spawnSyncAndAssert( + process.execPath, + ['--enable-source-maps', script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + // Both the transpile cache and the code cache should be missed. + assert.match(output, /retrieving transpile cache for StrippedTypeScript .*test-typescript\.ts.*success/); + assert.match(output, /reading cache from .* for CommonJS .*test-typescript\.ts.*success/); + assert.match(output, /skip persisting StrippedTypeScript .*test-typescript\.ts because cache was the same/); + assert.match(output, /V8 code cache for CommonJS .*test-typescript\.ts was accepted, keeping the in-memory entry/); + assert.match(output, /skip persisting CommonJS .*test-typescript\.ts because cache was the same/); + return true; + } + }); diff --git a/test/parallel/test-compile-cache-typescript-transform.js b/test/parallel/test-compile-cache-typescript-transform.js new file mode 100644 index 00000000000000..41eb67b203baa1 --- /dev/null +++ b/test/parallel/test-compile-cache-typescript-transform.js @@ -0,0 +1,127 @@ +'use strict'; + +// This tests NODE_COMPILE_CACHE works with --experimental-transform-types. + +require('../common'); +const { spawnSyncAndAssert } = require('../common/child_process'); +const assert = require('assert'); +const tmpdir = require('../common/tmpdir'); +const fixtures = require('../common/fixtures'); + + +tmpdir.refresh(); +const dir = tmpdir.resolve('.compile_cache_dir'); +const script = fixtures.path('typescript', 'ts', 'transformation', 'test-enum.ts'); + +// Check --experimental-transform-types which enables source maps by default. +spawnSyncAndAssert( + process.execPath, + ['--experimental-transform-types', script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /saving transpilation cache for TransformedTypeScriptWithSourceMaps .*test-enum\.ts/); + assert.match(output, /writing cache for TransformedTypeScriptWithSourceMaps .*test-enum\.ts.*success/); + assert.match(output, /writing cache for CommonJS .*test-enum\.ts.*success/); + return true; + } + }); + +spawnSyncAndAssert( + process.execPath, + ['--experimental-transform-types', script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /retrieving transpile cache for TransformedTypeScriptWithSourceMaps .*test-enum\.ts.*success/); + assert.match(output, /reading cache from .* for CommonJS .*test-enum\.ts.*success/); + assert.match(output, /skip persisting TransformedTypeScriptWithSourceMaps .*test-enum\.ts because cache was the same/); + assert.match(output, /V8 code cache for CommonJS .*test-enum\.ts was accepted, keeping the in-memory entry/); + assert.match(output, /skip persisting CommonJS .*test-enum\.ts because cache was the same/); + return true; + } + }); + +// Reloading without source maps should miss the cache generated with source maps. +spawnSyncAndAssert( + process.execPath, + ['--experimental-transform-types', '--no-enable-source-maps', script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + // Both the transpile cache and the code cache should be missed. + assert.match(output, /no transpile cache for TransformedTypeScript .*test-enum\.ts/); + assert.match(output, /reading cache from .* for CommonJS .*test-enum\.ts.*mismatch/); + // New cache without source map should be generated. + assert.match(output, /writing cache for TransformedTypeScript .*test-enum\.ts.*success/); + assert.match(output, /writing cache for CommonJS .*test-enum\.ts.*success/); + return true; + } + }); + +// Reloading without source maps again should hit the cache generated without source maps. +spawnSyncAndAssert( + process.execPath, + ['--experimental-transform-types', '--no-enable-source-maps', script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /retrieving transpile cache for TransformedTypeScript .*test-enum\.ts.*success/); + assert.match(output, /reading cache from .* for CommonJS .*test-enum\.ts.*success/); + assert.match(output, /skip persisting TransformedTypeScript .*test-enum\.ts because cache was the same/); + assert.match(output, /V8 code cache for CommonJS .*test-enum\.ts was accepted, keeping the in-memory entry/); + assert.match(output, /skip persisting CommonJS .*test-enum\.ts because cache was the same/); + return true; + } + }); + +// Reloading with source maps again should hit the co-existing transpile cache with source +// maps, but miss the code cache generated without source maps. +spawnSyncAndAssert( + process.execPath, + ['--experimental-transform-types', script], + { + env: { + ...process.env, + NODE_DEBUG_NATIVE: 'COMPILE_CACHE', + NODE_COMPILE_CACHE: dir + }, + cwd: tmpdir.path + }, + { + stderr(output) { + assert.match(output, /retrieving transpile cache for TransformedTypeScriptWithSourceMaps .*test-enum\.ts.*success/); + assert.match(output, /reading cache from .* for CommonJS .*test-enum\.ts.*mismatch/); + assert.match(output, /skip persisting TransformedTypeScriptWithSourceMaps .*test-enum\.ts because cache was the same/); + assert.match(output, /writing cache for CommonJS .*test-enum\.ts.*success/); + return true; + } + }); From 0713ee3a17b06654fa80321bf53eb4f19a49413c Mon Sep 17 00:00:00 2001 From: James M Snell Date: Wed, 22 Jan 2025 13:05:54 -0800 Subject: [PATCH 10/38] test: simplify common/index.js Move single or trivial and limited use things out of common/index.js for the purpose of simplifying and reducing common/index.js PR-URL: https://github.com/nodejs/node/pull/56712 Reviewed-By: Yagiz Nizipli Reviewed-By: Matteo Collina --- test/common/README.md | 6 ------ test/common/index.js | 12 ------------ test/common/index.mjs | 4 ---- test/parallel/test-source-map-enable.js | 3 ++- test/tick-processor/util.js | 11 +++++++++-- test/wasi/test-wasi-io.js | 11 ++++++++--- 6 files changed, 19 insertions(+), 28 deletions(-) diff --git a/test/common/README.md b/test/common/README.md index 9ecee39b64a3df..c3c44e32b3788c 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -279,12 +279,6 @@ Platform check for IBMi. Platform check for Linux. -### `isLinuxPPCBE` - -* [\][] - -Platform check for Linux on PowerPC. - ### `isMacOS` * [\][] diff --git a/test/common/index.js b/test/common/index.js index e8bf65d0a6edb4..238d66e96fe257 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -1034,11 +1034,6 @@ const common = { return require('os').type() === 'OS400'; }, - get isLinuxPPCBE() { - return (process.platform === 'linux') && (process.arch === 'ppc64') && - (require('os').endianness() === 'BE'); - }, - get localhostIPv4() { if (localhostIPv4 !== null) return localhostIPv4; @@ -1067,13 +1062,6 @@ const common = { return +process.env.NODE_COMMON_PORT || 12346; }, - /** - * Returns the EOL character used by this Git checkout. - */ - get checkoutEOL() { - return fs.readFileSync(__filename).includes('\r\n') ? '\r\n' : '\n'; - }, - get isInsideDirWithUnusualChars() { return __dirname.includes('%') || (!isWindows && __dirname.includes('\\')) || diff --git a/test/common/index.mjs b/test/common/index.mjs index 090659f93be8ef..aafef1453bd78a 100644 --- a/test/common/index.mjs +++ b/test/common/index.mjs @@ -7,7 +7,6 @@ const { allowGlobals, buildType, canCreateSymLink, - checkoutEOL, childShouldThrowAndAbort, createZeroFilledFile, enoughTestMem, @@ -27,7 +26,6 @@ const { isIBMi, isInsideDirWithUnusualChars, isLinux, - isLinuxPPCBE, isMainThread, isOpenBSD, isMacOS, @@ -59,7 +57,6 @@ export { allowGlobals, buildType, canCreateSymLink, - checkoutEOL, childShouldThrowAndAbort, createRequire, createZeroFilledFile, @@ -81,7 +78,6 @@ export { isIBMi, isInsideDirWithUnusualChars, isLinux, - isLinuxPPCBE, isMainThread, isOpenBSD, isMacOS, diff --git a/test/parallel/test-source-map-enable.js b/test/parallel/test-source-map-enable.js index 46c25d26cfa8e7..64f4254fcddbc6 100644 --- a/test/parallel/test-source-map-enable.js +++ b/test/parallel/test-source-map-enable.js @@ -242,6 +242,7 @@ function nextdir() { // Persists line lengths for in-memory representation of source file. { + const checkoutEOL = fs.readFileSync(__filename).includes('\r\n') ? '\r\n' : '\n'; const coverageDirectory = nextdir(); spawnSync(process.execPath, [ require.resolve('../fixtures/source-map/istanbul-throw.js'), @@ -250,7 +251,7 @@ function nextdir() { 'istanbul-throw.js', coverageDirectory ); - if (common.checkoutEOL === '\r\n') { + if (checkoutEOL === '\r\n') { assert.deepStrictEqual(sourceMap.lineLengths, [1086, 31, 185, 649, 0]); } else { assert.deepStrictEqual(sourceMap.lineLengths, [1085, 30, 184, 648, 0]); diff --git a/test/tick-processor/util.js b/test/tick-processor/util.js index 6d118b7c38bc66..9586a81c276a65 100644 --- a/test/tick-processor/util.js +++ b/test/tick-processor/util.js @@ -5,14 +5,21 @@ const { isWindows, isSunOS, isAIX, - isLinuxPPCBE, isFreeBSD, } = require('../common'); +const { endianness } = require('os'); + +function isLinuxPPCBE() { + return (process.platform === 'linux') && + (process.arch === 'ppc64') && + (endianness() === 'BE'); +} + module.exports = { isCPPSymbolsNotMapped: isWindows || isSunOS || isAIX || - isLinuxPPCBE || + isLinuxPPCBE() || isFreeBSD, }; diff --git a/test/wasi/test-wasi-io.js b/test/wasi/test-wasi-io.js index 061ac88a73ece4..f5348644f1cfbf 100644 --- a/test/wasi/test-wasi-io.js +++ b/test/wasi/test-wasi-io.js @@ -1,14 +1,19 @@ 'use strict'; -const common = require('../common'); -const { checkoutEOL } = common; +require('../common'); +const { readFileSync } = require('fs'); const { testWasiPreview1 } = require('../common/wasi'); +const checkoutEOL = readFileSync(__filename).includes('\r\n') ? '\r\n' : '\n'; + +// TODO(@jasnell): It's not entirely clear what this test is asserting. +// More comments would be helpful. + testWasiPreview1(['freopen'], {}, { stdout: `hello from input2.txt${checkoutEOL}` }); testWasiPreview1(['read_file'], {}, { stdout: `hello from input.txt${checkoutEOL}` }); testWasiPreview1(['read_file_twice'], {}, { stdout: `hello from input.txt${checkoutEOL}hello from input.txt${checkoutEOL}`, }); // Tests that are currently unsupported on Windows. -if (!common.isWindows) { +if (process.platform !== 'win32') { testWasiPreview1(['stdin'], { input: 'hello world' }, { stdout: 'hello world' }); } From 8caa1dcee63b2c6fd7a9edf9b9a6222b38a2cf62 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Wed, 22 Jan 2025 14:19:38 -0800 Subject: [PATCH 11/38] test: rely less on duplicative common test harness utilities There are several cleanups here that are not just style nits... 1. The `common.isMainThread` was just a passthrough to the `isMainThread` export on the worker_thread module. It's use was inconsistent and just obfuscated the fact that the test file depend on the `worker_threads` built-in. By eliminating it we simplify the test harness a bit and make it clearer which tests depend on the worker_threads check. 2. The `common.isDumbTerminal` is fairly unnecesary since that just wraps a public API check. 3. Several of the `common.skipIf....` checks were inconsistently used and really don't need to be separate utility functions. A key part of the motivation here is to work towards making more of the tests more self-contained and less reliant on the common test harness where possible. PR-URL: https://github.com/nodejs/node/pull/56712 Reviewed-By: Yagiz Nizipli Reviewed-By: Matteo Collina --- test/abort/test-abort-backtrace.js | 45 ++++++++++++- test/async-hooks/init-hooks.js | 5 +- test/async-hooks/test-crypto-pbkdf2.js | 7 +- test/async-hooks/test-crypto-randomBytes.js | 7 +- test/async-hooks/test-enable-disable.js | 3 +- test/async-hooks/test-fseventwrap.js | 7 +- .../test-fsreqcallback-readFile.js | 4 +- test/async-hooks/test-getaddrinforeqwrap.js | 4 +- test/async-hooks/test-getnameinforeqwrap.js | 4 +- test/async-hooks/test-graph.signal.js | 7 +- .../test-no-assert-when-disabled.js | 5 +- test/async-hooks/test-pipewrap.js | 4 +- ...promise.chain-promise-before-init-hooks.js | 4 +- test/async-hooks/test-promise.js | 4 +- test/async-hooks/test-signalwrap.js | 7 +- test/async-hooks/test-statwatcher.js | 5 +- .../test-unhandled-rejection-context.js | 4 +- test/benchmark/test-benchmark-napi.js | 4 +- test/common/README.md | 17 ----- test/common/index.js | 66 ------------------- test/common/index.mjs | 8 --- test/es-module/test-esm-resolve-type.mjs | 4 +- .../test-vm-main-context-default-loader.js | 6 +- test/fixtures/permission/fs-write.js | 8 ++- test/fixtures/permission/processbinding.js | 6 +- test/internet/test-trace-events-dns.js | 4 +- ...test-async-hooks-disable-during-promise.js | 4 +- .../test-async-hooks-promise-triggerid.js | 4 +- test/parallel/test-async-hooks-promise.js | 4 +- ...st-async-hooks-top-level-clearimmediate.js | 4 +- .../test-async-wrap-promise-after-enabled.js | 4 +- test/parallel/test-bootstrap-modules.js | 10 +-- .../test-child-process-validate-stdio.js | 4 +- .../test-cluster-net-listen-relative-path.js | 9 ++- test/parallel/test-code-cache.js | 3 +- test/parallel/test-console-clear.js | 4 +- test/parallel/test-console.js | 4 +- test/parallel/test-crypto-no-algorithm.js | 3 +- test/parallel/test-cwd-enoent-preload.js | 9 ++- test/parallel/test-cwd-enoent-repl.js | 9 ++- test/parallel/test-cwd-enoent.js | 9 ++- test/parallel/test-fs-mkdir.js | 3 +- test/parallel/test-fs-realpath.js | 4 +- test/parallel/test-fs-whatwg-url.js | 4 +- test/parallel/test-fs-write-file-sync.js | 4 +- test/parallel/test-http-chunk-problem.js | 11 +++- test/parallel/test-icu-env.js | 4 +- .../test-inspector-already-activated-cli.js | 7 +- .../test-inspector-async-hook-after-done.js | 7 +- ...st-inspector-async-hook-setup-at-signal.js | 6 +- .../test-inspector-connect-main-thread.js | 4 +- .../test-inspector-connect-to-main-thread.js | 4 +- test/parallel/test-inspector-contexts.js | 4 +- ...ctor-exit-worker-in-wait-for-connection.js | 12 +++- ...tor-exit-worker-in-wait-for-connection2.js | 6 +- test/parallel/test-inspector-open-coverage.js | 7 +- ...st-inspector-open-port-integer-overflow.js | 7 +- .../test-inspector-overwrite-config.js | 4 +- .../test-inspector-port-zero-cluster.js | 7 +- .../parallel/test-inspector-tracing-domain.js | 8 ++- .../test-inspector-workers-flat-list.js | 4 +- test/parallel/test-internal-module-require.js | 3 +- ...st-performance-nodetiming-uvmetricsinfo.js | 6 +- .../test-permission-allow-addons-cli.js | 6 +- ...test-permission-allow-child-process-cli.js | 8 ++- .../test-permission-allow-wasi-cli.js | 6 +- .../test-permission-child-process-cli.js | 7 +- .../test-permission-fs-absolute-path.js | 6 +- ...test-permission-fs-internal-module-stat.js | 6 +- test/parallel/test-permission-fs-read.js | 6 +- .../test-permission-fs-relative-path.js | 6 +- .../test-permission-fs-repeat-path.js | 6 +- test/parallel/test-permission-fs-require.js | 8 ++- .../test-permission-fs-symlink-relative.js | 7 +- ...test-permission-fs-symlink-target-write.js | 18 +++-- test/parallel/test-permission-fs-symlink.js | 12 +++- .../test-permission-fs-traversal-path.js | 13 +++- test/parallel/test-permission-fs-wildcard.js | 6 +- .../test-permission-fs-windows-path.js | 6 +- .../test-permission-fs-write-report.js | 10 ++- test/parallel/test-permission-fs-write-v8.js | 10 ++- test/parallel/test-permission-fs-write.js | 10 ++- .../parallel/test-permission-inspector-brk.js | 6 +- test/parallel/test-permission-inspector.js | 7 +- test/parallel/test-permission-no-addons.js | 6 +- .../test-permission-processbinding.js | 6 +- .../test-permission-worker-threads-cli.js | 8 ++- test/parallel/test-pipe-file-to-http.js | 7 +- .../parallel/test-preload-self-referential.js | 4 +- test/parallel/test-process-abort.js | 4 +- .../test-process-beforeexit-throw-exit.js | 6 +- .../test-process-chdir-errormessage.js | 5 +- test/parallel/test-process-chdir.js | 4 +- test/parallel/test-process-env-tz.js | 7 +- test/parallel/test-process-euid-egid.js | 5 +- test/parallel/test-process-exit-handler.js | 4 +- test/parallel/test-process-get-builtin.mjs | 3 +- test/parallel/test-process-initgroups.js | 5 +- test/parallel/test-process-load-env-file.js | 5 +- test/parallel/test-process-setgroups.js | 4 +- test/parallel/test-process-uid-gid.js | 4 +- test/parallel/test-process-umask-mask.js | 3 +- test/parallel/test-process-umask.js | 3 +- ...-readline-interface-no-trailing-newline.js | 4 +- ...est-readline-interface-recursive-writes.js | 4 +- test/parallel/test-readline-interface.js | 5 +- test/parallel/test-readline-position.js | 4 +- .../test-readline-promises-interface.js | 5 +- .../test-readline-promises-tab-complete.js | 4 +- test/parallel/test-readline-tab-complete.js | 4 +- .../test-readline-undefined-columns.js | 4 +- test/parallel/test-readline.js | 4 +- test/parallel/test-repl-autocomplete.js | 4 +- test/parallel/test-repl-editor.js | 4 +- test/parallel/test-repl-history-navigation.js | 4 +- ...repl-load-multiline-no-trailing-newline.js | 4 +- test/parallel/test-repl-load-multiline.js | 4 +- test/parallel/test-repl-mode.js | 4 +- test/parallel/test-repl-permission-model.js | 4 +- test/parallel/test-repl-persistent-history.js | 4 +- .../test-repl-programmatic-history.js | 4 +- .../test-repl-require-self-referential.js | 4 +- test/parallel/test-repl-require.js | 4 +- test/parallel/test-repl-reverse-search.js | 5 +- test/parallel/test-repl-sigint-nested-eval.js | 6 +- test/parallel/test-repl-sigint.js | 6 +- .../test-repl-strict-mode-previews.js | 5 +- .../parallel/test-repl-tab-complete-import.js | 5 +- test/parallel/test-repl-tab-complete.js | 4 +- test/parallel/test-require-symlink.js | 8 ++- test/parallel/test-runner-module-mocking.js | 3 +- test/parallel/test-set-process-debug-port.js | 6 +- test/parallel/test-setproctitle.js | 14 ++-- .../test-shadow-realm-import-value-resolve.js | 5 +- test/parallel/test-signal-args.js | 9 ++- test/parallel/test-signal-handler.js | 9 ++- test/parallel/test-stdio-pipe-access.js | 5 +- test/parallel/test-stdio-pipe-redirect.js | 5 +- .../test-timers-immediate-unref-simple.js | 3 +- test/parallel/test-trace-events-api.js | 7 +- .../test-trace-events-dynamic-enable.js | 8 ++- test/parallel/test-warn-sigprof.js | 9 ++- test/parallel/test-worker-name.js | 11 +++- test/report/test-report-signal.js | 8 ++- test/sequential/test-fs-watch.js | 4 +- test/sequential/test-heapdump.js | 4 +- test/sequential/test-init.js | 4 +- test/sequential/test-perf-hooks.js | 5 +- 148 files changed, 672 insertions(+), 290 deletions(-) diff --git a/test/abort/test-abort-backtrace.js b/test/abort/test-abort-backtrace.js index ce9ed39196eb1f..455bbf2361cf51 100644 --- a/test/abort/test-abort-backtrace.js +++ b/test/abort/test-abort-backtrace.js @@ -1,8 +1,47 @@ 'use strict'; -const common = require('../common'); +require('../common'); const assert = require('assert'); const cp = require('child_process'); +function getPrintedStackTrace(stderr) { + const lines = stderr.split('\n'); + + let state = 'initial'; + const result = { + message: [], + nativeStack: [], + jsStack: [], + }; + for (let i = 0; i < lines.length; ++i) { + const line = lines[i].trim(); + if (line.length === 0) { + continue; // Skip empty lines. + } + + switch (state) { + case 'initial': + result.message.push(line); + if (line.includes('Native stack trace')) { + state = 'native-stack'; + } else { + result.message.push(line); + } + break; + case 'native-stack': + if (line.includes('JavaScript stack trace')) { + state = 'js-stack'; + } else { + result.nativeStack.push(line); + } + break; + case 'js-stack': + result.jsStack.push(line); + break; + } + } + return result; +} + if (process.argv[2] === 'child') { process.abort(); } else { @@ -10,7 +49,7 @@ if (process.argv[2] === 'child') { const stderr = child.stderr.toString(); assert.strictEqual(child.stdout.toString(), ''); - const { nativeStack, jsStack } = common.getPrintedStackTrace(stderr); + const { nativeStack, jsStack } = getPrintedStackTrace(stderr); if (!nativeStack.every((frame, index) => frame.startsWith(`${index + 1}:`))) { assert.fail(`Each frame should start with a frame number:\n${stderr}`); @@ -18,7 +57,7 @@ if (process.argv[2] === 'child') { // For systems that don't support backtraces, the native stack is // going to be empty. - if (!common.isWindows && nativeStack.length > 0) { + if (process.platform !== 'win32' && nativeStack.length > 0) { const { getBinaryPath } = require('../common/shared-lib-util'); if (!nativeStack.some((frame) => frame.includes(`[${getBinaryPath()}]`))) { assert.fail(`Some native stack frame include the binary name:\n${stderr}`); diff --git a/test/async-hooks/init-hooks.js b/test/async-hooks/init-hooks.js index 2206ab31eba75f..8fc44994fbc497 100644 --- a/test/async-hooks/init-hooks.js +++ b/test/async-hooks/init-hooks.js @@ -1,9 +1,10 @@ 'use strict'; // Flags: --expose-gc -const common = require('../common'); +require('../common'); const assert = require('assert'); const async_hooks = require('async_hooks'); +const { isMainThread } = require('worker_threads'); const util = require('util'); const print = process._rawDebug; @@ -161,7 +162,7 @@ class ActivityCollector { const stub = { uid, type: 'Unknown', handleIsObject: true, handle: {} }; this._activities.set(uid, stub); return stub; - } else if (!common.isMainThread) { + } else if (!isMainThread) { // Worker threads start main script execution inside of an AsyncWrap // callback, so we don't yield errors for these. return null; diff --git a/test/async-hooks/test-crypto-pbkdf2.js b/test/async-hooks/test-crypto-pbkdf2.js index 4788ce4a580656..c607adf7258760 100644 --- a/test/async-hooks/test-crypto-pbkdf2.js +++ b/test/async-hooks/test-crypto-pbkdf2.js @@ -1,10 +1,13 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); -if (!common.isMainThread) +} +const { isMainThread } = require('worker_threads'); +if (!isMainThread) { common.skip('Worker bootstrapping works differently -> different async IDs'); +} const assert = require('assert'); const tick = require('../common/tick'); diff --git a/test/async-hooks/test-crypto-randomBytes.js b/test/async-hooks/test-crypto-randomBytes.js index 88cd4643ab6638..8ecc1c45a9a524 100644 --- a/test/async-hooks/test-crypto-randomBytes.js +++ b/test/async-hooks/test-crypto-randomBytes.js @@ -1,10 +1,13 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); -if (!common.isMainThread) +} +const { isMainThread } = require('worker_threads'); +if (!isMainThread) { common.skip('Worker bootstrapping works differently -> different async IDs'); +} const assert = require('assert'); const tick = require('../common/tick'); diff --git a/test/async-hooks/test-enable-disable.js b/test/async-hooks/test-enable-disable.js index 64139408a48209..d408338e892c32 100644 --- a/test/async-hooks/test-enable-disable.js +++ b/test/async-hooks/test-enable-disable.js @@ -87,8 +87,9 @@ const assert = require('assert'); const tick = require('../common/tick'); const initHooks = require('./init-hooks'); const { checkInvocations } = require('./hook-checks'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) common.skip('Worker bootstrapping works differently -> different timing'); // Include "Unknown"s because hook2 will not be able to identify diff --git a/test/async-hooks/test-fseventwrap.js b/test/async-hooks/test-fseventwrap.js index 12a439f8033cbc..a5e1a3b9d2f232 100644 --- a/test/async-hooks/test-fseventwrap.js +++ b/test/async-hooks/test-fseventwrap.js @@ -6,12 +6,15 @@ const initHooks = require('./init-hooks'); const tick = require('../common/tick'); const { checkInvocations } = require('./hook-checks'); const fs = require('fs'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('Worker bootstrapping works differently -> different async IDs'); +} -if (common.isIBMi) +if (common.isIBMi) { common.skip('IBMi does not support fs.watch()'); +} const hooks = initHooks(); diff --git a/test/async-hooks/test-fsreqcallback-readFile.js b/test/async-hooks/test-fsreqcallback-readFile.js index 01ccce9b4cc694..65f3652f12f988 100644 --- a/test/async-hooks/test-fsreqcallback-readFile.js +++ b/test/async-hooks/test-fsreqcallback-readFile.js @@ -6,9 +6,11 @@ const tick = require('../common/tick'); const initHooks = require('./init-hooks'); const { checkInvocations } = require('./hook-checks'); const fs = require('fs'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('Worker bootstrapping works differently -> different async IDs'); +} const hooks = initHooks(); diff --git a/test/async-hooks/test-getaddrinforeqwrap.js b/test/async-hooks/test-getaddrinforeqwrap.js index 7291ea8a301954..a21557bcd56e7a 100644 --- a/test/async-hooks/test-getaddrinforeqwrap.js +++ b/test/async-hooks/test-getaddrinforeqwrap.js @@ -6,9 +6,11 @@ const tick = require('../common/tick'); const initHooks = require('./init-hooks'); const { checkInvocations } = require('./hook-checks'); const dns = require('dns'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('Worker bootstrapping works differently -> different async IDs'); +} const hooks = initHooks(); diff --git a/test/async-hooks/test-getnameinforeqwrap.js b/test/async-hooks/test-getnameinforeqwrap.js index c7a3937ff3ceef..b00fa0d4d9dd54 100644 --- a/test/async-hooks/test-getnameinforeqwrap.js +++ b/test/async-hooks/test-getnameinforeqwrap.js @@ -6,9 +6,11 @@ const tick = require('../common/tick'); const initHooks = require('./init-hooks'); const { checkInvocations } = require('./hook-checks'); const dns = require('dns'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('Worker bootstrapping works differently -> different async IDs'); +} const hooks = initHooks(); diff --git a/test/async-hooks/test-graph.signal.js b/test/async-hooks/test-graph.signal.js index f87b1215b523b9..351fb7550af431 100644 --- a/test/async-hooks/test-graph.signal.js +++ b/test/async-hooks/test-graph.signal.js @@ -1,10 +1,13 @@ 'use strict'; const common = require('../common'); -if (common.isWindows) +if (common.isWindows) { common.skip('no signals on Windows'); -if (!common.isMainThread) +} +const { isMainThread } = require('worker_threads'); +if (!isMainThread) { common.skip('No signal handling available in Workers'); +} const initHooks = require('./init-hooks'); const verifyGraph = require('./verify-graph'); diff --git a/test/async-hooks/test-no-assert-when-disabled.js b/test/async-hooks/test-no-assert-when-disabled.js index 70114d1e1140f8..0e7c0568cc09fa 100644 --- a/test/async-hooks/test-no-assert-when-disabled.js +++ b/test/async-hooks/test-no-assert-when-disabled.js @@ -1,9 +1,10 @@ 'use strict'; // Flags: --no-force-async-hooks-checks --expose-internals const common = require('../common'); - -if (!common.isMainThread) +const { isMainThread } = require('worker_threads'); +if (!isMainThread) { common.skip('Workers don\'t inherit per-env state like the check flag'); +} const async_hooks = require('internal/async_hooks'); diff --git a/test/async-hooks/test-pipewrap.js b/test/async-hooks/test-pipewrap.js index 2d42e769cfd1f3..7ea5f38adc85e2 100644 --- a/test/async-hooks/test-pipewrap.js +++ b/test/async-hooks/test-pipewrap.js @@ -9,9 +9,11 @@ const tick = require('../common/tick'); const initHooks = require('./init-hooks'); const { checkInvocations } = require('./hook-checks'); const { spawn } = require('child_process'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('Worker bootstrapping works differently -> different async IDs'); +} const hooks = initHooks(); diff --git a/test/async-hooks/test-promise.chain-promise-before-init-hooks.js b/test/async-hooks/test-promise.chain-promise-before-init-hooks.js index 52a312dbdfe196..c5e67b6f94ca68 100644 --- a/test/async-hooks/test-promise.chain-promise-before-init-hooks.js +++ b/test/async-hooks/test-promise.chain-promise-before-init-hooks.js @@ -4,9 +4,11 @@ const common = require('../common'); const assert = require('assert'); const initHooks = require('./init-hooks'); const { checkInvocations } = require('./hook-checks'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('Worker bootstrapping works differently -> different async IDs'); +} const p = new Promise(common.mustCall(function executor(resolve) { resolve(5); diff --git a/test/async-hooks/test-promise.js b/test/async-hooks/test-promise.js index 417cb3c80d6298..554c3ae7dd711e 100644 --- a/test/async-hooks/test-promise.js +++ b/test/async-hooks/test-promise.js @@ -5,9 +5,11 @@ const common = require('../common'); const assert = require('assert'); const initHooks = require('./init-hooks'); const { checkInvocations } = require('./hook-checks'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('Worker bootstrapping works differently -> different async IDs'); +} const hooks = initHooks(); diff --git a/test/async-hooks/test-signalwrap.js b/test/async-hooks/test-signalwrap.js index 4584d140ce1d0f..60adaedd476f27 100644 --- a/test/async-hooks/test-signalwrap.js +++ b/test/async-hooks/test-signalwrap.js @@ -1,10 +1,13 @@ 'use strict'; const common = require('../common'); -if (common.isWindows) +if (common.isWindows) { common.skip('no signals in Windows'); -if (!common.isMainThread) +} +const { isMainThread } = require('worker_threads'); +if (!isMainThread) { common.skip('No signal handling available in Workers'); +} const assert = require('assert'); const initHooks = require('./init-hooks'); diff --git a/test/async-hooks/test-statwatcher.js b/test/async-hooks/test-statwatcher.js index f3c0e74355eeba..8f4fb2175885f3 100644 --- a/test/async-hooks/test-statwatcher.js +++ b/test/async-hooks/test-statwatcher.js @@ -7,8 +7,11 @@ const initHooks = require('./init-hooks'); const { checkInvocations } = require('./hook-checks'); const fs = require('fs'); -if (!common.isMainThread) +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { common.skip('Worker bootstrapping works differently -> different async IDs'); +} tmpdir.refresh(); diff --git a/test/async-hooks/test-unhandled-rejection-context.js b/test/async-hooks/test-unhandled-rejection-context.js index 8404cf71f0db6f..168b51a3331f7f 100644 --- a/test/async-hooks/test-unhandled-rejection-context.js +++ b/test/async-hooks/test-unhandled-rejection-context.js @@ -5,9 +5,11 @@ const common = require('../common'); const assert = require('assert'); const initHooks = require('./init-hooks'); const async_hooks = require('async_hooks'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('Worker bootstrapping works differently -> different async IDs'); +} const promiseAsyncIds = []; const hooks = initHooks({ diff --git a/test/benchmark/test-benchmark-napi.js b/test/benchmark/test-benchmark-napi.js index 7164efe3d4e718..518e10a5111a5b 100644 --- a/test/benchmark/test-benchmark-napi.js +++ b/test/benchmark/test-benchmark-napi.js @@ -6,7 +6,9 @@ if (common.isWindows) { common.skip('vcbuild.bat doesn\'t build the n-api benchmarks yet'); } -if (!common.isMainThread) { +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { common.skip('addons are not supported in workers'); } diff --git a/test/common/README.md b/test/common/README.md index c3c44e32b3788c..887dee2783ad72 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -102,10 +102,6 @@ symlinks ([SeCreateSymbolicLinkPrivilege](https://msdn.microsoft.com/en-us/library/windows/desktop/bb530716\(v=vs.85\).aspx)). On non-Windows platforms, this always returns `true`. -### `createZeroFilledFile(filename)` - -Creates a 10 MiB file of all null characters. - ### `enoughTestMem` * [\][] @@ -257,10 +253,6 @@ Platform check for Advanced Interactive eXecutive (AIX). Attempts to 'kill' `pid` -### `isDumbTerminal` - -* [\][] - ### `isFreeBSD` * [\][] @@ -456,10 +448,6 @@ will not be run. Logs '1..0 # Skipped: ' + `msg` and exits with exit code `0`. -### `skipIfDumbTerminal()` - -Skip the rest of the tests if the current terminal is a dumb terminal - ### `skipIfEslintMissing()` Skip the rest of the tests in the current file when `ESLint` is not available @@ -475,11 +463,6 @@ was disabled at compile time. Skip the rest of the tests in the current file when the Node.js executable was compiled with a pointer size smaller than 64 bits. -### `skipIfWorker()` - -Skip the rest of the tests in the current file when not running on a main -thread. - ## ArrayStream module The `ArrayStream` module provides a simple `Stream` that pushes elements from diff --git a/test/common/index.js b/test/common/index.js index 238d66e96fe257..6086d584f0b595 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -139,8 +139,6 @@ const isPi = (() => { } })(); -const isDumbTerminal = process.env.TERM === 'dumb'; - // When using high concurrency or in the CI we need much more time for each connection attempt net.setDefaultAutoSelectFamilyAttemptTimeout(platformTimeout(net.getDefaultAutoSelectFamilyAttemptTimeout() * 10)); const defaultAutoSelectFamilyAttemptTimeout = net.getDefaultAutoSelectFamilyAttemptTimeout(); @@ -243,13 +241,6 @@ function childShouldThrowAndAbort() { }); } -function createZeroFilledFile(filename) { - const fd = fs.openSync(filename, 'w'); - fs.ftruncateSync(fd, 10 * 1024 * 1024); - fs.closeSync(fd); -} - - const pwdCommand = isWindows ? ['cmd.exe', ['/d', '/c', 'cd']] : ['pwd', []]; @@ -716,12 +707,6 @@ function skipIf32Bits() { } } -function skipIfWorker() { - if (!isMainThread) { - skip('This test only works on a main thread'); - } -} - function getArrayBufferViews(buf) { const { buffer, byteOffset, byteLength } = buf; @@ -806,12 +791,6 @@ function invalidArgTypeHelper(input) { return ` Received type ${typeof input} (${inspected})`; } -function skipIfDumbTerminal() { - if (isDumbTerminal) { - skip('skipping - dumb terminal'); - } -} - function requireNoPackageJSONAbove(dir = __dirname) { let possiblePackage = path.join(dir, '..', 'package.json'); let lastPackage = null; @@ -882,45 +861,6 @@ function escapePOSIXShell(cmdParts, ...args) { return [cmd, { env }]; }; -function getPrintedStackTrace(stderr) { - const lines = stderr.split('\n'); - - let state = 'initial'; - const result = { - message: [], - nativeStack: [], - jsStack: [], - }; - for (let i = 0; i < lines.length; ++i) { - const line = lines[i].trim(); - if (line.length === 0) { - continue; // Skip empty lines. - } - - switch (state) { - case 'initial': - result.message.push(line); - if (line.includes('Native stack trace')) { - state = 'native-stack'; - } else { - result.message.push(line); - } - break; - case 'native-stack': - if (line.includes('JavaScript stack trace')) { - state = 'js-stack'; - } else { - result.nativeStack.push(line); - } - break; - case 'js-stack': - result.jsStack.push(line); - break; - } - } - return result; -} - /** * Check the exports of require(esm). * TODO(joyeecheung): use it in all the test-require-module-* tests to minimize changes @@ -943,7 +883,6 @@ const common = { buildType, canCreateSymLink, childShouldThrowAndAbort, - createZeroFilledFile, defaultAutoSelectFamilyAttemptTimeout, escapePOSIXShell, expectsError, @@ -951,7 +890,6 @@ const common = { expectWarning, getArrayBufferViews, getBufferSources, - getPrintedStackTrace, getTTYfd, hasIntl, hasCrypto, @@ -960,10 +898,8 @@ const common = { isAlive, isASan, isDebug, - isDumbTerminal, isFreeBSD, isLinux, - isMainThread, isOpenBSD, isMacOS, isPi, @@ -985,10 +921,8 @@ const common = { runWithInvalidFD, skip, skipIf32Bits, - skipIfDumbTerminal, skipIfEslintMissing, skipIfInspectorDisabled, - skipIfWorker, spawnPromisified, get enoughTestMem() { diff --git a/test/common/index.mjs b/test/common/index.mjs index aafef1453bd78a..dd0adadcb28d38 100644 --- a/test/common/index.mjs +++ b/test/common/index.mjs @@ -8,7 +8,6 @@ const { buildType, canCreateSymLink, childShouldThrowAndAbort, - createZeroFilledFile, enoughTestMem, escapePOSIXShell, expectsError, @@ -21,12 +20,10 @@ const { hasIPv6, isAIX, isAlive, - isDumbTerminal, isFreeBSD, isIBMi, isInsideDirWithUnusualChars, isLinux, - isMainThread, isOpenBSD, isMacOS, isSunOS, @@ -45,7 +42,6 @@ const { runWithInvalidFD, skip, skipIf32Bits, - skipIfDumbTerminal, skipIfEslintMissing, skipIfInspectorDisabled, spawnPromisified, @@ -59,7 +55,6 @@ export { canCreateSymLink, childShouldThrowAndAbort, createRequire, - createZeroFilledFile, enoughTestMem, escapePOSIXShell, expectsError, @@ -73,12 +68,10 @@ export { hasIPv6, isAIX, isAlive, - isDumbTerminal, isFreeBSD, isIBMi, isInsideDirWithUnusualChars, isLinux, - isMainThread, isOpenBSD, isMacOS, isSunOS, @@ -97,7 +90,6 @@ export { runWithInvalidFD, skip, skipIf32Bits, - skipIfDumbTerminal, skipIfEslintMissing, skipIfInspectorDisabled, spawnPromisified, diff --git a/test/es-module/test-esm-resolve-type.mjs b/test/es-module/test-esm-resolve-type.mjs index 22163bbd5defb8..9d97413379ad3c 100644 --- a/test/es-module/test-esm-resolve-type.mjs +++ b/test/es-module/test-esm-resolve-type.mjs @@ -13,8 +13,10 @@ import path from 'path'; import fs from 'fs'; import url from 'url'; import process from 'process'; +import { isMainThread } from 'worker_threads'; -if (!common.isMainThread) { + +if (!isMainThread) { common.skip( 'test-esm-resolve-type.mjs: process.chdir is not available in Workers' ); diff --git a/test/es-module/test-vm-main-context-default-loader.js b/test/es-module/test-vm-main-context-default-loader.js index f9edc761465d96..bda954be6ebf97 100644 --- a/test/es-module/test-vm-main-context-default-loader.js +++ b/test/es-module/test-vm-main-context-default-loader.js @@ -3,7 +3,11 @@ const common = require('../common'); // Can't process.chdir() in worker. -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const tmpdir = require('../common/tmpdir'); const fixtures = require('../common/fixtures'); diff --git a/test/fixtures/permission/fs-write.js b/test/fixtures/permission/fs-write.js index 0c0ec72602041a..83fe3d234db290 100644 --- a/test/fixtures/permission/fs-write.js +++ b/test/fixtures/permission/fs-write.js @@ -1,7 +1,11 @@ 'use strict'; const common = require('../../common'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const assert = require('assert'); const fs = require('fs'); @@ -553,4 +557,4 @@ const relativeProtectedFolder = process.env.RELATIVEBLOCKEDFOLDER; }, { code: 'ERR_ACCESS_DENIED', }); -} \ No newline at end of file +} diff --git a/test/fixtures/permission/processbinding.js b/test/fixtures/permission/processbinding.js index bdb958fb01b5ca..69e2fac5d7f151 100644 --- a/test/fixtures/permission/processbinding.js +++ b/test/fixtures/permission/processbinding.js @@ -1,5 +1,9 @@ const common = require('../../common'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const assert = require('assert'); diff --git a/test/internet/test-trace-events-dns.js b/test/internet/test-trace-events-dns.js index c18a49bc9496c8..c5df4751374399 100644 --- a/test/internet/test-trace-events-dns.js +++ b/test/internet/test-trace-events-dns.js @@ -5,9 +5,11 @@ const cp = require('child_process'); const tmpdir = require('../common/tmpdir'); const fs = require('fs'); const util = require('util'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('process.chdir is not available in Workers'); +} const traceFile = 'node_trace.1.log'; diff --git a/test/parallel/test-async-hooks-disable-during-promise.js b/test/parallel/test-async-hooks-disable-during-promise.js index 6b9b53bd30f0f5..a25dae51e1f82d 100644 --- a/test/parallel/test-async-hooks-disable-during-promise.js +++ b/test/parallel/test-async-hooks-disable-during-promise.js @@ -1,9 +1,11 @@ 'use strict'; const common = require('../common'); const async_hooks = require('async_hooks'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('Worker bootstrapping works differently -> different AsyncWraps'); +} const hook = async_hooks.createHook({ init: common.mustCall(2), diff --git a/test/parallel/test-async-hooks-promise-triggerid.js b/test/parallel/test-async-hooks-promise-triggerid.js index b860d60999e1ef..89e5bc1464f8d5 100644 --- a/test/parallel/test-async-hooks-promise-triggerid.js +++ b/test/parallel/test-async-hooks-promise-triggerid.js @@ -2,9 +2,11 @@ const common = require('../common'); const assert = require('assert'); const async_hooks = require('async_hooks'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('Worker bootstrapping works differently -> different async IDs'); +} const promiseAsyncIds = []; diff --git a/test/parallel/test-async-hooks-promise.js b/test/parallel/test-async-hooks-promise.js index 9db510e329ffad..74f72a188240a0 100644 --- a/test/parallel/test-async-hooks-promise.js +++ b/test/parallel/test-async-hooks-promise.js @@ -2,9 +2,11 @@ const common = require('../common'); const assert = require('assert'); const async_hooks = require('async_hooks'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('Worker bootstrapping works differently -> different async IDs'); +} const initCalls = []; const resolveCalls = []; diff --git a/test/parallel/test-async-hooks-top-level-clearimmediate.js b/test/parallel/test-async-hooks-top-level-clearimmediate.js index cc5fcf48eb50b3..fd91fefa9c4bce 100644 --- a/test/parallel/test-async-hooks-top-level-clearimmediate.js +++ b/test/parallel/test-async-hooks-top-level-clearimmediate.js @@ -5,9 +5,11 @@ const common = require('../common'); const assert = require('assert'); const async_hooks = require('async_hooks'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('Worker bootstrapping works differently -> different async IDs'); +} let seenId, seenResource; diff --git a/test/parallel/test-async-wrap-promise-after-enabled.js b/test/parallel/test-async-wrap-promise-after-enabled.js index 0d58cbd653868b..cbca873574c1f8 100644 --- a/test/parallel/test-async-wrap-promise-after-enabled.js +++ b/test/parallel/test-async-wrap-promise-after-enabled.js @@ -4,9 +4,11 @@ const common = require('../common'); const assert = require('assert'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('Worker bootstrapping works differently -> different timing'); +} const async_hooks = require('async_hooks'); diff --git a/test/parallel/test-bootstrap-modules.js b/test/parallel/test-bootstrap-modules.js index c75ee390dcd195..ebcd2a6d6c12b2 100644 --- a/test/parallel/test-bootstrap-modules.js +++ b/test/parallel/test-bootstrap-modules.js @@ -115,7 +115,9 @@ expected.atRunTime = new Set([ 'NativeModule internal/modules/esm/utils', ]); -if (common.isMainThread) { +const { isMainThread } = require('worker_threads'); + +if (isMainThread) { [ 'NativeModule url', ].forEach(expected.beforePreExec.add.bind(expected.beforePreExec)); @@ -186,7 +188,7 @@ function err(message) { } } -if (common.isMainThread) { +if (isMainThread) { const missing = expected.beforePreExec.difference(actual.beforePreExec); const extra = actual.beforePreExec.difference(expected.beforePreExec); if (missing.size !== 0) { @@ -212,10 +214,10 @@ if (common.isMainThread) { } } -if (!common.isMainThread) { +if (!isMainThread) { // For workers, just merge beforePreExec into atRunTime for now. // When we start adding modules to the worker snapshot, this branch - // can be removed and we can just remove the common.isMainThread + // can be removed and we can just remove the isMainThread // conditions. expected.beforePreExec.forEach(expected.atRunTime.add.bind(expected.atRunTime)); actual.beforePreExec.forEach(actual.atRunTime.add.bind(actual.atRunTime)); diff --git a/test/parallel/test-child-process-validate-stdio.js b/test/parallel/test-child-process-validate-stdio.js index d5958c694ff6ff..5ba6f0fd123cc1 100644 --- a/test/parallel/test-child-process-validate-stdio.js +++ b/test/parallel/test-child-process-validate-stdio.js @@ -43,7 +43,9 @@ assert.throws(() => getValidStdio(stdio2, true), assert.throws(() => getValidStdio(stdio), expectedError); } -if (common.isMainThread) { +const { isMainThread } = require('worker_threads'); + +if (isMainThread) { const stdio3 = [process.stdin, process.stdout, process.stderr]; const result = getValidStdio(stdio3, false); assert.deepStrictEqual(result, { diff --git a/test/parallel/test-cluster-net-listen-relative-path.js b/test/parallel/test-cluster-net-listen-relative-path.js index bb4d0b90f203e6..16d2bf5c836b53 100644 --- a/test/parallel/test-cluster-net-listen-relative-path.js +++ b/test/parallel/test-cluster-net-listen-relative-path.js @@ -1,11 +1,16 @@ 'use strict'; const common = require('../common'); -if (common.isWindows) +if (common.isWindows) { common.skip('On Windows named pipes live in their own ' + 'filesystem and don\'t have a ~100 byte limit'); -if (!common.isMainThread) +} + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { common.skip('process.chdir is not available in Workers'); +} const assert = require('assert'); const cluster = require('cluster'); diff --git a/test/parallel/test-code-cache.js b/test/parallel/test-code-cache.js index 1c768d664c8a18..576f713af1b02a 100644 --- a/test/parallel/test-code-cache.js +++ b/test/parallel/test-code-cache.js @@ -5,7 +5,8 @@ // and the cache is used when built in modules are compiled. // Otherwise, verifies that no cache is used when compiling builtins. -const { isMainThread } = require('../common'); +require('../common'); +const { isMainThread } = require('worker_threads'); const assert = require('assert'); const { internalBinding diff --git a/test/parallel/test-console-clear.js b/test/parallel/test-console-clear.js index 5975602547922a..8ded51595f654e 100644 --- a/test/parallel/test-console-clear.js +++ b/test/parallel/test-console-clear.js @@ -1,6 +1,6 @@ 'use strict'; -const common = require('../common'); +require('../common'); const assert = require('assert'); const stdoutWrite = process.stdout.write; @@ -18,7 +18,7 @@ function doTest(isTTY, check) { } // Fake TTY -if (!common.isDumbTerminal) { +if (process.env.TERM !== 'dumb') { doTest(true, check); } doTest(false, ''); diff --git a/test/parallel/test-console.js b/test/parallel/test-console.js index 5dd029a6e904cc..bc08ba395e787e 100644 --- a/test/parallel/test-console.js +++ b/test/parallel/test-console.js @@ -31,10 +31,12 @@ const { restoreStderr } = require('../common/hijackstdio'); +const { isMainThread } = require('worker_threads'); + assert.ok(process.stdout.writable); assert.ok(process.stderr.writable); // Support legacy API -if (common.isMainThread) { +if (isMainThread) { assert.strictEqual(typeof process.stdout.fd, 'number'); assert.strictEqual(typeof process.stderr.fd, 'number'); } diff --git a/test/parallel/test-crypto-no-algorithm.js b/test/parallel/test-crypto-no-algorithm.js index 06124e3d465e41..bb5b81e119c87d 100644 --- a/test/parallel/test-crypto-no-algorithm.js +++ b/test/parallel/test-crypto-no-algorithm.js @@ -11,8 +11,9 @@ if (!hasOpenSSL3) const assert = require('node:assert/strict'); const crypto = require('node:crypto'); +const { isMainThread } = require('worker_threads'); -if (common.isMainThread) { +if (isMainThread) { // TODO(richardlau): Decide if `crypto.setFips` should error if the // provider named "fips" is not available. crypto.setFips(1); diff --git a/test/parallel/test-cwd-enoent-preload.js b/test/parallel/test-cwd-enoent-preload.js index 21b20d6d035672..a7841e984d0eab 100644 --- a/test/parallel/test-cwd-enoent-preload.js +++ b/test/parallel/test-cwd-enoent-preload.js @@ -1,10 +1,15 @@ 'use strict'; const common = require('../common'); // Fails with EINVAL on SmartOS, EBUSY on Windows, EBUSY on AIX. -if (common.isSunOS || common.isWindows || common.isAIX || common.isIBMi) +if (common.isSunOS || common.isWindows || common.isAIX || common.isIBMi) { common.skip('cannot rmdir current working directory'); -if (!common.isMainThread) +} + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { common.skip('process.chdir is not available in Workers'); +} const assert = require('assert'); const fs = require('fs'); diff --git a/test/parallel/test-cwd-enoent-repl.js b/test/parallel/test-cwd-enoent-repl.js index 0a61cbfbced9b4..fcb08c004f345c 100644 --- a/test/parallel/test-cwd-enoent-repl.js +++ b/test/parallel/test-cwd-enoent-repl.js @@ -1,10 +1,15 @@ 'use strict'; const common = require('../common'); // Fails with EINVAL on SmartOS, EBUSY on Windows, EBUSY on AIX. -if (common.isSunOS || common.isWindows || common.isAIX || common.isIBMi) +if (common.isSunOS || common.isWindows || common.isAIX || common.isIBMi) { common.skip('cannot rmdir current working directory'); -if (!common.isMainThread) +} + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { common.skip('process.chdir is not available in Workers'); +} const assert = require('assert'); const fs = require('fs'); diff --git a/test/parallel/test-cwd-enoent.js b/test/parallel/test-cwd-enoent.js index 876888bc2be518..ca8b460835d45a 100644 --- a/test/parallel/test-cwd-enoent.js +++ b/test/parallel/test-cwd-enoent.js @@ -1,10 +1,15 @@ 'use strict'; const common = require('../common'); // Fails with EINVAL on SmartOS, EBUSY on Windows, EBUSY on AIX. -if (common.isSunOS || common.isWindows || common.isAIX || common.isIBMi) +if (common.isSunOS || common.isWindows || common.isAIX || common.isIBMi) { common.skip('cannot rmdir current working directory'); -if (!common.isMainThread) +} + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { common.skip('process.chdir is not available in Workers'); +} const assert = require('assert'); const fs = require('fs'); diff --git a/test/parallel/test-fs-mkdir.js b/test/parallel/test-fs-mkdir.js index 89b8b436d5c9f4..f7685c7de0a962 100644 --- a/test/parallel/test-fs-mkdir.js +++ b/test/parallel/test-fs-mkdir.js @@ -24,6 +24,7 @@ const common = require('../common'); const assert = require('assert'); const fs = require('fs'); const path = require('path'); +const { isMainThread } = require('worker_threads'); const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); @@ -217,7 +218,7 @@ function nextdir() { // mkdirpSync dirname loop // XXX: windows and smartos have issues removing a directory that you're in. -if (common.isMainThread && (common.isLinux || common.isMacOS)) { +if (isMainThread && (common.isLinux || common.isMacOS)) { const pathname = tmpdir.resolve(nextdir()); fs.mkdirSync(pathname); process.chdir(pathname); diff --git a/test/parallel/test-fs-realpath.js b/test/parallel/test-fs-realpath.js index d944195de3de0c..69237e3974e5b0 100644 --- a/test/parallel/test-fs-realpath.js +++ b/test/parallel/test-fs-realpath.js @@ -23,9 +23,11 @@ const common = require('../common'); const fixtures = require('../common/fixtures'); const tmpdir = require('../common/tmpdir'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('process.chdir is not available in Workers'); +} const assert = require('assert'); const fs = require('fs'); diff --git a/test/parallel/test-fs-whatwg-url.js b/test/parallel/test-fs-whatwg-url.js index 7401ed7e76ecd1..2d5664cd12015c 100644 --- a/test/parallel/test-fs-whatwg-url.js +++ b/test/parallel/test-fs-whatwg-url.js @@ -5,6 +5,8 @@ const fixtures = require('../common/fixtures'); const assert = require('assert'); const fs = require('fs'); const tmpdir = require('../common/tmpdir'); +const { isMainThread } = require('worker_threads'); + tmpdir.refresh(); const url = fixtures.fileURL('a.js'); @@ -86,7 +88,7 @@ if (common.isWindows) { // Test that strings are interpreted as paths and not as URL // Can't use process.chdir in Workers // Please avoid testing fs.rmdir('file:') or using it as cleanup -if (common.isMainThread && !common.isWindows) { +if (isMainThread && !common.isWindows) { const oldCwd = process.cwd(); process.chdir(tmpdir.path); diff --git a/test/parallel/test-fs-write-file-sync.js b/test/parallel/test-fs-write-file-sync.js index 4ead91530bb748..e5fbe32eab6d14 100644 --- a/test/parallel/test-fs-write-file-sync.js +++ b/test/parallel/test-fs-write-file-sync.js @@ -21,9 +21,11 @@ 'use strict'; const common = require('../common'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('Setting process.umask is not supported in Workers'); +} const assert = require('assert'); const fs = require('fs'); diff --git a/test/parallel/test-http-chunk-problem.js b/test/parallel/test-http-chunk-problem.js index 3629b7576600e8..90c54b8f5c7dcb 100644 --- a/test/parallel/test-http-chunk-problem.js +++ b/test/parallel/test-http-chunk-problem.js @@ -1,9 +1,12 @@ 'use strict'; // http://groups.google.com/group/nodejs/browse_thread/thread/f66cd3c960406919 const common = require('../common'); -if (!common.hasCrypto) + +if (!common.hasCrypto) { common.skip('missing crypto'); +} +const fs = require('fs'); const assert = require('assert'); if (process.argv[2] === 'request') { @@ -73,7 +76,11 @@ function executeRequest(cb) { tmpdir.refresh(); -common.createZeroFilledFile(filename); + +// Create a zero-filled file. +const fd = fs.openSync(filename, 'w'); +fs.ftruncateSync(fd, 10 * 1024 * 1024); +fs.closeSync(fd); server = http.createServer(function(req, res) { res.writeHead(200); diff --git a/test/parallel/test-icu-env.js b/test/parallel/test-icu-env.js index afa36132f60e8d..26075a3d0acec2 100644 --- a/test/parallel/test-icu-env.js +++ b/test/parallel/test-icu-env.js @@ -4,7 +4,7 @@ const assert = require('assert'); const { execFileSync } = require('child_process'); const { readFileSync, globSync } = require('fs'); const { path } = require('../common/fixtures'); - +const { isMainThread } = require('worker_threads'); // This test checks for regressions in environment variable handling and // caching, but the localization data originated from ICU might change @@ -169,7 +169,7 @@ if (isMockable) { // Tests with process.env mutated inside { // process.env.TZ is not intercepted in Workers - if (common.isMainThread) { + if (isMainThread) { assert.strictEqual( isSet(zones.map((TZ) => runEnvInside({ TZ }, () => new Date(333333333333).toString()))), true diff --git a/test/parallel/test-inspector-already-activated-cli.js b/test/parallel/test-inspector-already-activated-cli.js index ba76d5168c14b9..9de226cedca60c 100644 --- a/test/parallel/test-inspector-already-activated-cli.js +++ b/test/parallel/test-inspector-already-activated-cli.js @@ -3,7 +3,12 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -common.skipIfWorker(); + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const assert = require('assert'); const inspector = require('inspector'); diff --git a/test/parallel/test-inspector-async-hook-after-done.js b/test/parallel/test-inspector-async-hook-after-done.js index 9f96fdb7b0da84..b49fe32982e132 100644 --- a/test/parallel/test-inspector-async-hook-after-done.js +++ b/test/parallel/test-inspector-async-hook-after-done.js @@ -3,7 +3,12 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -common.skipIfWorker(); + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const assert = require('assert'); const { Worker } = require('worker_threads'); diff --git a/test/parallel/test-inspector-async-hook-setup-at-signal.js b/test/parallel/test-inspector-async-hook-setup-at-signal.js index 43f50d00615723..64a3835e415746 100644 --- a/test/parallel/test-inspector-async-hook-setup-at-signal.js +++ b/test/parallel/test-inspector-async-hook-setup-at-signal.js @@ -6,7 +6,11 @@ common.skipIf32Bits(); const { NodeInstance } = require('../common/inspector-helper.js'); const assert = require('assert'); -common.skipIfWorker(); // Signal starts a server for a main thread inspector +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const script = ` process._rawDebug('Waiting until a signal enables the inspector...'); diff --git a/test/parallel/test-inspector-connect-main-thread.js b/test/parallel/test-inspector-connect-main-thread.js index b724bf3cd9d62f..2281b5efcf3ed8 100644 --- a/test/parallel/test-inspector-connect-main-thread.js +++ b/test/parallel/test-inspector-connect-main-thread.js @@ -10,8 +10,8 @@ const { pathToFileURL } = require('url'); const { isMainThread, parentPort, Worker, workerData } = require('worker_threads'); -if (!workerData) { - common.skipIfWorker(); +if (!workerData && !isMainThread) { + common.skip('This test only works on a main thread'); } function toDebug() { diff --git a/test/parallel/test-inspector-connect-to-main-thread.js b/test/parallel/test-inspector-connect-to-main-thread.js index 7254145a2733f0..9244a85f21b15a 100644 --- a/test/parallel/test-inspector-connect-to-main-thread.js +++ b/test/parallel/test-inspector-connect-to-main-thread.js @@ -6,8 +6,8 @@ common.skipIfInspectorDisabled(); const { Session } = require('inspector'); const { Worker, isMainThread, workerData } = require('worker_threads'); -if (!workerData) { - common.skipIfWorker(); +if (!workerData && !isMainThread) { + common.skip('This test only works on a main thread'); } if (isMainThread) { diff --git a/test/parallel/test-inspector-contexts.js b/test/parallel/test-inspector-contexts.js index 3d6ee4d460e863..9ab2c515b4a9de 100644 --- a/test/parallel/test-inspector-contexts.js +++ b/test/parallel/test-inspector-contexts.js @@ -9,6 +9,8 @@ const assert = require('assert'); const vm = require('vm'); const { Session } = require('inspector'); const { gcUntil } = require('../common/gc'); +const { isMainThread } = require('worker_threads'); + const session = new Session(); session.connect(); @@ -34,7 +36,7 @@ async function testContextCreatedAndDestroyed() { assert.strictEqual(name.includes(`[${process.pid}]`), true); } else { let expects = `${process.argv0}[${process.pid}]`; - if (!common.isMainThread) { + if (!isMainThread) { expects = `Worker[${require('worker_threads').threadId}]`; } assert.strictEqual(expects, name); diff --git a/test/parallel/test-inspector-exit-worker-in-wait-for-connection.js b/test/parallel/test-inspector-exit-worker-in-wait-for-connection.js index 4fcbb092fd23cf..9215d4969fb92f 100644 --- a/test/parallel/test-inspector-exit-worker-in-wait-for-connection.js +++ b/test/parallel/test-inspector-exit-worker-in-wait-for-connection.js @@ -3,9 +3,15 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -const { parentPort, workerData, Worker } = require('node:worker_threads'); -if (!workerData) { - common.skipIfWorker(); +const { + isMainThread, + parentPort, + workerData, + Worker, +} = require('node:worker_threads'); + +if (!workerData && !isMainThread) { + common.skip('This test only works on a main thread'); } const inspector = require('node:inspector'); diff --git a/test/parallel/test-inspector-exit-worker-in-wait-for-connection2.js b/test/parallel/test-inspector-exit-worker-in-wait-for-connection2.js index fb13fc3f969304..cf485ae3a4318f 100644 --- a/test/parallel/test-inspector-exit-worker-in-wait-for-connection2.js +++ b/test/parallel/test-inspector-exit-worker-in-wait-for-connection2.js @@ -3,9 +3,9 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -const { workerData, Worker } = require('node:worker_threads'); -if (!workerData) { - common.skipIfWorker(); +const { isMainThread, workerData, Worker } = require('node:worker_threads'); +if (!workerData && !isMainThread) { + common.skip('This test only works on a main thread'); } const assert = require('node:assert'); diff --git a/test/parallel/test-inspector-open-coverage.js b/test/parallel/test-inspector-open-coverage.js index 259049c36822ab..33f50bfc3f53c4 100644 --- a/test/parallel/test-inspector-open-coverage.js +++ b/test/parallel/test-inspector-open-coverage.js @@ -7,7 +7,12 @@ const fixtures = require('../common/fixtures'); const tmpdir = require('../common/tmpdir'); common.skipIfInspectorDisabled(); -common.skipIfWorker(); + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} tmpdir.refresh(); diff --git a/test/parallel/test-inspector-open-port-integer-overflow.js b/test/parallel/test-inspector-open-port-integer-overflow.js index 0f9a4799d0642a..a1b5c640c4c18d 100644 --- a/test/parallel/test-inspector-open-port-integer-overflow.js +++ b/test/parallel/test-inspector-open-port-integer-overflow.js @@ -5,7 +5,12 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -common.skipIfWorker(); + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const assert = require('assert'); const inspector = require('inspector'); diff --git a/test/parallel/test-inspector-overwrite-config.js b/test/parallel/test-inspector-overwrite-config.js index c20df083256120..53599b31df8acc 100644 --- a/test/parallel/test-inspector-overwrite-config.js +++ b/test/parallel/test-inspector-overwrite-config.js @@ -13,9 +13,11 @@ const common = require('../common'); const assert = require('assert'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('--require does not work with Workers'); +} const inspector = require('inspector'); const msg = 'Test inspector logging'; diff --git a/test/parallel/test-inspector-port-zero-cluster.js b/test/parallel/test-inspector-port-zero-cluster.js index 8e2db0b69d5ca0..5ee7bcf7417345 100644 --- a/test/parallel/test-inspector-port-zero-cluster.js +++ b/test/parallel/test-inspector-port-zero-cluster.js @@ -3,7 +3,12 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -common.skipIfWorker(); + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} // Assert that even when started with `--inspect=0` workers are assigned // consecutive (i.e. deterministically predictable) debug ports diff --git a/test/parallel/test-inspector-tracing-domain.js b/test/parallel/test-inspector-tracing-domain.js index f5ac6875a0f643..aa31d63a01577d 100644 --- a/test/parallel/test-inspector-tracing-domain.js +++ b/test/parallel/test-inspector-tracing-domain.js @@ -3,7 +3,13 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -common.skipIfWorker(); // https://github.com/nodejs/node/issues/22767 + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + // https://github.com/nodejs/node/issues/22767 + common.skip('This test only works on a main thread'); +} const assert = require('assert'); const { Session } = require('inspector'); diff --git a/test/parallel/test-inspector-workers-flat-list.js b/test/parallel/test-inspector-workers-flat-list.js index 9f6495d10fb147..a7b57fbb0a353b 100644 --- a/test/parallel/test-inspector-workers-flat-list.js +++ b/test/parallel/test-inspector-workers-flat-list.js @@ -6,8 +6,8 @@ common.skipIfInspectorDisabled(); const { Worker, isMainThread, parentPort, workerData } = require('worker_threads'); -if (isMainThread || workerData !== 'launched by test') { - common.skipIfWorker(); +if (!isMainThread || workerData !== 'launched by test') { + common.skip('This test only works on a main thread'); } const { Session } = require('inspector'); diff --git a/test/parallel/test-internal-module-require.js b/test/parallel/test-internal-module-require.js index 058273c7ea4304..213838150b96d9 100644 --- a/test/parallel/test-internal-module-require.js +++ b/test/parallel/test-internal-module-require.js @@ -8,8 +8,9 @@ // 3. Deprecated modules are properly deprecated. const common = require('../common'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) { +if (!isMainThread) { common.skip('Cannot test the existence of --expose-internals from worker'); } diff --git a/test/parallel/test-performance-nodetiming-uvmetricsinfo.js b/test/parallel/test-performance-nodetiming-uvmetricsinfo.js index 3d32e0deb72e94..b67682b0ff3559 100644 --- a/test/parallel/test-performance-nodetiming-uvmetricsinfo.js +++ b/test/parallel/test-performance-nodetiming-uvmetricsinfo.js @@ -1,7 +1,11 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const { spawnSync } = require('node:child_process'); const assert = require('node:assert'); diff --git a/test/parallel/test-permission-allow-addons-cli.js b/test/parallel/test-permission-allow-addons-cli.js index 484f16e0acb3b5..342bdb6bc01e35 100644 --- a/test/parallel/test-permission-allow-addons-cli.js +++ b/test/parallel/test-permission-allow-addons-cli.js @@ -2,7 +2,11 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const { createRequire } = require('node:module'); const assert = require('node:assert'); diff --git a/test/parallel/test-permission-allow-child-process-cli.js b/test/parallel/test-permission-allow-child-process-cli.js index 794f55ecf9a68c..cf7e79e208d389 100644 --- a/test/parallel/test-permission-allow-child-process-cli.js +++ b/test/parallel/test-permission-allow-child-process-cli.js @@ -2,7 +2,13 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} + const assert = require('assert'); const childProcess = require('child_process'); const fs = require('fs'); diff --git a/test/parallel/test-permission-allow-wasi-cli.js b/test/parallel/test-permission-allow-wasi-cli.js index c6bea9fb39cf0a..20aca9292533d5 100644 --- a/test/parallel/test-permission-allow-wasi-cli.js +++ b/test/parallel/test-permission-allow-wasi-cli.js @@ -2,7 +2,11 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const assert = require('assert'); const { WASI } = require('wasi'); diff --git a/test/parallel/test-permission-child-process-cli.js b/test/parallel/test-permission-child-process-cli.js index dfea008a60407b..7d8fbf0564d5ef 100644 --- a/test/parallel/test-permission-child-process-cli.js +++ b/test/parallel/test-permission-child-process-cli.js @@ -2,7 +2,12 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} + const assert = require('assert'); const childProcess = require('child_process'); diff --git a/test/parallel/test-permission-fs-absolute-path.js b/test/parallel/test-permission-fs-absolute-path.js index 2c2257052c8b02..c3bf9ef5cfb2d1 100644 --- a/test/parallel/test-permission-fs-absolute-path.js +++ b/test/parallel/test-permission-fs-absolute-path.js @@ -3,7 +3,11 @@ const common = require('../common'); const path = require('path'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const assert = require('assert'); const { spawnSync } = require('child_process'); diff --git a/test/parallel/test-permission-fs-internal-module-stat.js b/test/parallel/test-permission-fs-internal-module-stat.js index fd0222cc34fa2e..ef99e4cca73a4f 100644 --- a/test/parallel/test-permission-fs-internal-module-stat.js +++ b/test/parallel/test-permission-fs-internal-module-stat.js @@ -2,7 +2,11 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} if (!common.hasCrypto) { common.skip('no crypto'); diff --git a/test/parallel/test-permission-fs-read.js b/test/parallel/test-permission-fs-read.js index ed8e866a6a4c10..b719207bdbd820 100644 --- a/test/parallel/test-permission-fs-read.js +++ b/test/parallel/test-permission-fs-read.js @@ -2,7 +2,11 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} if (!common.hasCrypto) { common.skip('no crypto'); diff --git a/test/parallel/test-permission-fs-relative-path.js b/test/parallel/test-permission-fs-relative-path.js index 3b115ee35d1227..9f4ce25f0f7d37 100644 --- a/test/parallel/test-permission-fs-relative-path.js +++ b/test/parallel/test-permission-fs-relative-path.js @@ -2,7 +2,11 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const assert = require('assert'); const { spawnSync } = require('child_process'); diff --git a/test/parallel/test-permission-fs-repeat-path.js b/test/parallel/test-permission-fs-repeat-path.js index 764c7d91497248..d24197e905063d 100644 --- a/test/parallel/test-permission-fs-repeat-path.js +++ b/test/parallel/test-permission-fs-repeat-path.js @@ -3,7 +3,11 @@ const common = require('../common'); const path = require('path'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const assert = require('assert'); const { spawnSync } = require('child_process'); diff --git a/test/parallel/test-permission-fs-require.js b/test/parallel/test-permission-fs-require.js index 5d3a407708371e..8406f9ec052eae 100644 --- a/test/parallel/test-permission-fs-require.js +++ b/test/parallel/test-permission-fs-require.js @@ -2,7 +2,13 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} + const fixtures = require('../common/fixtures'); const assert = require('node:assert'); diff --git a/test/parallel/test-permission-fs-symlink-relative.js b/test/parallel/test-permission-fs-symlink-relative.js index cf9b37ea79b059..e1fe5d064a8756 100644 --- a/test/parallel/test-permission-fs-symlink-relative.js +++ b/test/parallel/test-permission-fs-symlink-relative.js @@ -2,7 +2,12 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const assert = require('assert'); const path = require('path'); diff --git a/test/parallel/test-permission-fs-symlink-target-write.js b/test/parallel/test-permission-fs-symlink-target-write.js index f55b19fa764a89..1cffead4dd7e71 100644 --- a/test/parallel/test-permission-fs-symlink-target-write.js +++ b/test/parallel/test-permission-fs-symlink-target-write.js @@ -2,11 +2,19 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); -if (!common.canCreateSymLink()) +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} + +if (!common.canCreateSymLink()) { common.skip('insufficient privileges'); -if (!common.hasCrypto) +} + +if (!common.hasCrypto) { common.skip('no crypto'); +} const assert = require('assert'); const fs = require('fs'); @@ -15,9 +23,7 @@ const tmpdir = require('../common/tmpdir'); const fixtures = require('../common/fixtures'); const { spawnSync } = require('child_process'); -{ - tmpdir.refresh(); -} +tmpdir.refresh(); const readOnlyFolder = tmpdir.resolve('read-only'); const readWriteFolder = tmpdir.resolve('read-write'); diff --git a/test/parallel/test-permission-fs-symlink.js b/test/parallel/test-permission-fs-symlink.js index 92965c960177d4..e5a80dba44ddf4 100644 --- a/test/parallel/test-permission-fs-symlink.js +++ b/test/parallel/test-permission-fs-symlink.js @@ -2,13 +2,19 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const fixtures = require('../common/fixtures'); -if (!common.canCreateSymLink()) +if (!common.canCreateSymLink()) { common.skip('insufficient privileges'); -if (!common.hasCrypto) +} +if (!common.hasCrypto) { common.skip('no crypto'); +} const assert = require('assert'); const fs = require('fs'); diff --git a/test/parallel/test-permission-fs-traversal-path.js b/test/parallel/test-permission-fs-traversal-path.js index 03571c2d01c861..ed9e434b6b862b 100644 --- a/test/parallel/test-permission-fs-traversal-path.js +++ b/test/parallel/test-permission-fs-traversal-path.js @@ -2,13 +2,20 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const fixtures = require('../common/fixtures'); -if (!common.canCreateSymLink()) +if (!common.canCreateSymLink()) { common.skip('insufficient privileges'); -if (!common.hasCrypto) +} + +if (!common.hasCrypto) { common.skip('no crypto'); +} const assert = require('assert'); const fs = require('fs'); diff --git a/test/parallel/test-permission-fs-wildcard.js b/test/parallel/test-permission-fs-wildcard.js index adca56ed0dba6d..1b67f37c2dcda2 100644 --- a/test/parallel/test-permission-fs-wildcard.js +++ b/test/parallel/test-permission-fs-wildcard.js @@ -2,7 +2,11 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const assert = require('assert'); const path = require('path'); diff --git a/test/parallel/test-permission-fs-windows-path.js b/test/parallel/test-permission-fs-windows-path.js index 6869b347cf283f..c3b3683b6479f7 100644 --- a/test/parallel/test-permission-fs-windows-path.js +++ b/test/parallel/test-permission-fs-windows-path.js @@ -2,7 +2,11 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const assert = require('assert'); const { spawnSync } = require('child_process'); diff --git a/test/parallel/test-permission-fs-write-report.js b/test/parallel/test-permission-fs-write-report.js index 111f73b7bcc1ed..a5f8d74904fedc 100644 --- a/test/parallel/test-permission-fs-write-report.js +++ b/test/parallel/test-permission-fs-write-report.js @@ -2,9 +2,15 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); -if (!common.hasCrypto) +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} + +if (!common.hasCrypto) { common.skip('no crypto'); +} const assert = require('assert'); diff --git a/test/parallel/test-permission-fs-write-v8.js b/test/parallel/test-permission-fs-write-v8.js index 85cb9a5519b3af..1b8691969b7afb 100644 --- a/test/parallel/test-permission-fs-write-v8.js +++ b/test/parallel/test-permission-fs-write-v8.js @@ -2,9 +2,15 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); -if (!common.hasCrypto) +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} + +if (!common.hasCrypto) { common.skip('no crypto'); +} const assert = require('assert'); const v8 = require('v8'); diff --git a/test/parallel/test-permission-fs-write.js b/test/parallel/test-permission-fs-write.js index 34eab7a40005db..385a37e2a92d86 100644 --- a/test/parallel/test-permission-fs-write.js +++ b/test/parallel/test-permission-fs-write.js @@ -2,9 +2,15 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); -if (!common.hasCrypto) +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} + +if (!common.hasCrypto) { common.skip('no crypto'); +} const assert = require('assert'); const path = require('path'); diff --git a/test/parallel/test-permission-inspector-brk.js b/test/parallel/test-permission-inspector-brk.js index 61c9c799ba7eb6..3cc7caabd42ba1 100644 --- a/test/parallel/test-permission-inspector-brk.js +++ b/test/parallel/test-permission-inspector-brk.js @@ -5,8 +5,12 @@ const assert = require('assert'); const { spawnSync } = require('child_process'); const fixtures = require('../common/fixtures'); const file = fixtures.path('permission', 'inspector-brk.js'); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} -common.skipIfWorker(); common.skipIfInspectorDisabled(); // See https://github.com/nodejs/node/issues/53385 diff --git a/test/parallel/test-permission-inspector.js b/test/parallel/test-permission-inspector.js index 9d3bf485fc4348..4b52e12abca090 100644 --- a/test/parallel/test-permission-inspector.js +++ b/test/parallel/test-permission-inspector.js @@ -2,7 +2,12 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} + common.skipIfInspectorDisabled(); const { Session } = require('inspector'); diff --git a/test/parallel/test-permission-no-addons.js b/test/parallel/test-permission-no-addons.js index a3ae6f4be10641..df08c4aa9f9db5 100644 --- a/test/parallel/test-permission-no-addons.js +++ b/test/parallel/test-permission-no-addons.js @@ -2,7 +2,11 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const { createRequire } = require('node:module'); const assert = require('node:assert'); diff --git a/test/parallel/test-permission-processbinding.js b/test/parallel/test-permission-processbinding.js index 47a1364f19e303..f5e33dac4deb52 100644 --- a/test/parallel/test-permission-processbinding.js +++ b/test/parallel/test-permission-processbinding.js @@ -1,7 +1,11 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} if (!common.hasCrypto) { common.skip('no crypto'); diff --git a/test/parallel/test-permission-worker-threads-cli.js b/test/parallel/test-permission-worker-threads-cli.js index efd98b2a3881aa..cf397c280474c1 100644 --- a/test/parallel/test-permission-worker-threads-cli.js +++ b/test/parallel/test-permission-worker-threads-cli.js @@ -2,13 +2,17 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); -const assert = require('assert'); const { Worker, isMainThread, } = require('worker_threads'); +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} + +const assert = require('assert'); + // Guarantee the initial state { assert.ok(!process.permission.has('worker')); diff --git a/test/parallel/test-pipe-file-to-http.js b/test/parallel/test-pipe-file-to-http.js index 82bdbe6a832a98..ffbab21f71fd9d 100644 --- a/test/parallel/test-pipe-file-to-http.js +++ b/test/parallel/test-pipe-file-to-http.js @@ -54,7 +54,12 @@ const server = http.createServer((req, res) => { server.listen(0); server.on('listening', () => { - common.createZeroFilledFile(filename); + + // Create a zero-filled file + const fd = fs.openSync(filename, 'w'); + fs.ftruncateSync(fd, 10 * 1024 * 1024); + fs.closeSync(fd); + makeRequest(); }); diff --git a/test/parallel/test-preload-self-referential.js b/test/parallel/test-preload-self-referential.js index 867e1c67983c83..68681332978ea6 100644 --- a/test/parallel/test-preload-self-referential.js +++ b/test/parallel/test-preload-self-referential.js @@ -4,11 +4,13 @@ const common = require('../common'); const fixtures = require('../common/fixtures'); const assert = require('assert'); const { exec } = require('child_process'); +const { isMainThread } = require('worker_threads'); const nodeBinary = process.argv[0]; -if (!common.isMainThread) +if (!isMainThread) { common.skip('process.chdir is not available in Workers'); +} const selfRefModule = fixtures.path('self_ref_module'); const fixtureA = fixtures.path('printA.js'); diff --git a/test/parallel/test-process-abort.js b/test/parallel/test-process-abort.js index 665e1399a3f362..34353befb02a44 100644 --- a/test/parallel/test-process-abort.js +++ b/test/parallel/test-process-abort.js @@ -2,9 +2,11 @@ const common = require('../common'); const assert = require('assert'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('process.abort() is not available in Workers'); +} // Check that our built-in methods do not have a prototype/constructor behaviour // if they don't need to. This could be tested for any of our C++ methods. diff --git a/test/parallel/test-process-beforeexit-throw-exit.js b/test/parallel/test-process-beforeexit-throw-exit.js index 6e9d764be90baa..c967d3a62712a7 100644 --- a/test/parallel/test-process-beforeexit-throw-exit.js +++ b/test/parallel/test-process-beforeexit-throw-exit.js @@ -1,6 +1,10 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} // Test that 'exit' is emitted if 'beforeExit' throws. diff --git a/test/parallel/test-process-chdir-errormessage.js b/test/parallel/test-process-chdir-errormessage.js index 0ed368287b377e..727a13f6f63f16 100644 --- a/test/parallel/test-process-chdir-errormessage.js +++ b/test/parallel/test-process-chdir-errormessage.js @@ -1,8 +1,11 @@ 'use strict'; const common = require('../common'); -if (!common.isMainThread) +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { common.skip('process.chdir is not available in Workers'); +} const assert = require('assert'); assert.throws( diff --git a/test/parallel/test-process-chdir.js b/test/parallel/test-process-chdir.js index ee59df853b24ce..42d2a60c8ec63e 100644 --- a/test/parallel/test-process-chdir.js +++ b/test/parallel/test-process-chdir.js @@ -4,9 +4,11 @@ const common = require('../common'); const assert = require('assert'); const fs = require('fs'); const path = require('path'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('process.chdir is not available in Workers'); +} const tmpdir = require('../common/tmpdir'); diff --git a/test/parallel/test-process-env-tz.js b/test/parallel/test-process-env-tz.js index dcc69ed4bf1d3b..b7bf730a9afa38 100644 --- a/test/parallel/test-process-env-tz.js +++ b/test/parallel/test-process-env-tz.js @@ -1,12 +1,15 @@ 'use strict'; const common = require('../common'); const assert = require('assert'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('process.env.TZ is not intercepted in Workers'); +} -if (common.isWindows) // Using a different TZ format. +if (common.isWindows) { // Using a different TZ format. common.skip('todo: test on Windows'); +} const date = new Date('2018-04-14T12:34:56.789Z'); diff --git a/test/parallel/test-process-euid-egid.js b/test/parallel/test-process-euid-egid.js index 11a8cfa0ed2b3c..3f4934233a6308 100644 --- a/test/parallel/test-process-euid-egid.js +++ b/test/parallel/test-process-euid-egid.js @@ -3,6 +3,8 @@ const common = require('../common'); const assert = require('assert'); +const { isMainThread } = require('worker_threads'); + if (common.isWindows) { assert.strictEqual(process.geteuid, undefined); assert.strictEqual(process.getegid, undefined); @@ -11,8 +13,9 @@ if (common.isWindows) { return; } -if (!common.isMainThread) +if (!isMainThread) { return; +} assert.throws(() => { process.seteuid({}); diff --git a/test/parallel/test-process-exit-handler.js b/test/parallel/test-process-exit-handler.js index d74e320fe63082..2546aa60a5cf89 100644 --- a/test/parallel/test-process-exit-handler.js +++ b/test/parallel/test-process-exit-handler.js @@ -1,8 +1,10 @@ 'use strict'; const common = require('../common'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('execArgv does not affect Workers'); +} // This test ensures that no asynchronous operations are performed in the 'exit' // handler. diff --git a/test/parallel/test-process-get-builtin.mjs b/test/parallel/test-process-get-builtin.mjs index b376e1b88f905a..6695828570f6c0 100644 --- a/test/parallel/test-process-get-builtin.mjs +++ b/test/parallel/test-process-get-builtin.mjs @@ -1,6 +1,7 @@ -import { isMainThread, hasCrypto, hasIntl } from '../common/index.mjs'; +import { hasCrypto, hasIntl } from '../common/index.mjs'; import assert from 'node:assert'; import { builtinModules } from 'node:module'; +import { isMainThread } from 'node:worker_threads'; for (const invalid of [1, undefined, null, false, [], {}, () => {}, Symbol('test')]) { assert.throws(() => process.getBuiltinModule(invalid), { code: 'ERR_INVALID_ARG_TYPE' }); diff --git a/test/parallel/test-process-initgroups.js b/test/parallel/test-process-initgroups.js index 6b4e3bdf1470b4..52597e096175e9 100644 --- a/test/parallel/test-process-initgroups.js +++ b/test/parallel/test-process-initgroups.js @@ -7,8 +7,11 @@ if (common.isWindows) { return; } -if (!common.isMainThread) +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { return; +} [undefined, null, true, {}, [], () => {}].forEach((val) => { assert.throws( diff --git a/test/parallel/test-process-load-env-file.js b/test/parallel/test-process-load-env-file.js index 1dada3aa9b7016..ec99c099d11b80 100644 --- a/test/parallel/test-process-load-env-file.js +++ b/test/parallel/test-process-load-env-file.js @@ -5,6 +5,7 @@ const fixtures = require('../../test/common/fixtures'); const assert = require('node:assert'); const { describe, it } = require('node:test'); const { join } = require('node:path'); +const { isMainThread } = require('worker_threads'); const basicValidEnvFilePath = fixtures.path('dotenv/basic-valid.env'); const validEnvFilePath = fixtures.path('dotenv/valid.env'); @@ -58,7 +59,7 @@ describe('process.loadEnvFile()', () => { const originalCwd = process.cwd(); try { - if (common.isMainThread) { + if (isMainThread) { process.chdir(join(originalCwd, 'lib')); } @@ -66,7 +67,7 @@ describe('process.loadEnvFile()', () => { process.loadEnvFile(); }, { code: 'ENOENT', syscall: 'open', path: '.env' }); } finally { - if (common.isMainThread) { + if (isMainThread) { process.chdir(originalCwd); } } diff --git a/test/parallel/test-process-setgroups.js b/test/parallel/test-process-setgroups.js index 9506f24a5f3447..49d147b6c2ddf5 100644 --- a/test/parallel/test-process-setgroups.js +++ b/test/parallel/test-process-setgroups.js @@ -1,14 +1,16 @@ 'use strict'; const common = require('../common'); const assert = require('assert'); +const { isMainThread } = require('worker_threads'); if (common.isWindows) { assert.strictEqual(process.setgroups, undefined); return; } -if (!common.isMainThread) +if (!isMainThread) { return; +} assert.throws( () => { diff --git a/test/parallel/test-process-uid-gid.js b/test/parallel/test-process-uid-gid.js index 54e87a6ff5c6e0..10eee45af1555b 100644 --- a/test/parallel/test-process-uid-gid.js +++ b/test/parallel/test-process-uid-gid.js @@ -23,6 +23,7 @@ const common = require('../common'); const assert = require('assert'); +const { isMainThread } = require('worker_threads'); if (common.isWindows) { // uid/gid functions are POSIX only. @@ -33,8 +34,9 @@ if (common.isWindows) { return; } -if (!common.isMainThread) +if (!isMainThread) { return; +} assert.throws(() => { process.setuid({}); diff --git a/test/parallel/test-process-umask-mask.js b/test/parallel/test-process-umask-mask.js index d599379761fd40..f0a67b8f14e895 100644 --- a/test/parallel/test-process-umask-mask.js +++ b/test/parallel/test-process-umask-mask.js @@ -5,8 +5,9 @@ const common = require('../common'); const assert = require('assert'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) common.skip('Setting process.umask is not supported in Workers'); let mask; diff --git a/test/parallel/test-process-umask.js b/test/parallel/test-process-umask.js index e90955f394df4e..594f75ebebed2b 100644 --- a/test/parallel/test-process-umask.js +++ b/test/parallel/test-process-umask.js @@ -22,8 +22,9 @@ 'use strict'; const common = require('../common'); const assert = require('assert'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) { +if (!isMainThread) { assert.strictEqual(typeof process.umask(), 'number'); assert.throws(() => { process.umask('0664'); diff --git a/test/parallel/test-readline-interface-no-trailing-newline.js b/test/parallel/test-readline-interface-no-trailing-newline.js index b3392db8619c95..398b85838c8b71 100644 --- a/test/parallel/test-readline-interface-no-trailing-newline.js +++ b/test/parallel/test-readline-interface-no-trailing-newline.js @@ -3,7 +3,9 @@ const common = require('../common'); const ArrayStream = require('../common/arraystream'); const assert = require('assert'); -common.skipIfDumbTerminal(); +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} const readline = require('readline'); const rli = new readline.Interface({ diff --git a/test/parallel/test-readline-interface-recursive-writes.js b/test/parallel/test-readline-interface-recursive-writes.js index 3a0aee5be9d619..ea3df1968d08d8 100644 --- a/test/parallel/test-readline-interface-recursive-writes.js +++ b/test/parallel/test-readline-interface-recursive-writes.js @@ -3,7 +3,9 @@ const common = require('../common'); const ArrayStream = require('../common/arraystream'); const assert = require('assert'); -common.skipIfDumbTerminal(); +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} const readline = require('readline'); const rli = new readline.Interface({ diff --git a/test/parallel/test-readline-interface.js b/test/parallel/test-readline-interface.js index a90e07d235030f..12ba0c709622e9 100644 --- a/test/parallel/test-readline-interface.js +++ b/test/parallel/test-readline-interface.js @@ -22,7 +22,10 @@ // Flags: --expose-internals 'use strict'; const common = require('../common'); -common.skipIfDumbTerminal(); + +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} const assert = require('assert'); const readline = require('readline'); diff --git a/test/parallel/test-readline-position.js b/test/parallel/test-readline-position.js index 3603a42ecedc68..ac2fe43b37a097 100644 --- a/test/parallel/test-readline-position.js +++ b/test/parallel/test-readline-position.js @@ -7,7 +7,9 @@ const assert = require('assert'); const ctrlU = { ctrl: true, name: 'u' }; -common.skipIfDumbTerminal(); +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} { const input = new PassThrough(); diff --git a/test/parallel/test-readline-promises-interface.js b/test/parallel/test-readline-promises-interface.js index 8e42d977301267..97424c1372629c 100644 --- a/test/parallel/test-readline-promises-interface.js +++ b/test/parallel/test-readline-promises-interface.js @@ -1,7 +1,10 @@ // Flags: --expose-internals 'use strict'; const common = require('../common'); -common.skipIfDumbTerminal(); + +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} const assert = require('assert'); const readline = require('readline/promises'); diff --git a/test/parallel/test-readline-promises-tab-complete.js b/test/parallel/test-readline-promises-tab-complete.js index fd32900e71d096..d8b0ac30ee779d 100644 --- a/test/parallel/test-readline-promises-tab-complete.js +++ b/test/parallel/test-readline-promises-tab-complete.js @@ -8,7 +8,9 @@ const assert = require('assert'); const { EventEmitter } = require('events'); const { getStringWidth } = require('internal/util/inspect'); -common.skipIfDumbTerminal(); +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} // This test verifies that the tab completion supports unicode and the writes // are limited to the minimum. diff --git a/test/parallel/test-readline-tab-complete.js b/test/parallel/test-readline-tab-complete.js index 64df237d56ad44..5b7b19102f412a 100644 --- a/test/parallel/test-readline-tab-complete.js +++ b/test/parallel/test-readline-tab-complete.js @@ -8,7 +8,9 @@ const assert = require('assert'); const EventEmitter = require('events').EventEmitter; const { getStringWidth } = require('internal/util/inspect'); -common.skipIfDumbTerminal(); +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} // This test verifies that the tab completion supports unicode and the writes // are limited to the minimum. diff --git a/test/parallel/test-readline-undefined-columns.js b/test/parallel/test-readline-undefined-columns.js index 25bafe957fa40a..d7000a16dd88a7 100644 --- a/test/parallel/test-readline-undefined-columns.js +++ b/test/parallel/test-readline-undefined-columns.js @@ -5,7 +5,9 @@ const assert = require('assert'); const PassThrough = require('stream').PassThrough; const readline = require('readline'); -common.skipIfDumbTerminal(); +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} // Checks that tab completion still works // when output column size is undefined diff --git a/test/parallel/test-readline.js b/test/parallel/test-readline.js index 77799fc14cf75f..0cf577942915a6 100644 --- a/test/parallel/test-readline.js +++ b/test/parallel/test-readline.js @@ -4,7 +4,9 @@ const { PassThrough } = require('stream'); const readline = require('readline'); const assert = require('assert'); -common.skipIfDumbTerminal(); +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} { const input = new PassThrough(); diff --git a/test/parallel/test-repl-autocomplete.js b/test/parallel/test-repl-autocomplete.js index cb17523494b2ff..a68322c501e264 100644 --- a/test/parallel/test-repl-autocomplete.js +++ b/test/parallel/test-repl-autocomplete.js @@ -9,7 +9,9 @@ const assert = require('assert'); const fs = require('fs'); const { inspect } = require('util'); -common.skipIfDumbTerminal(); +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); diff --git a/test/parallel/test-repl-editor.js b/test/parallel/test-repl-editor.js index e260f5e89174a8..fee647d0478e50 100644 --- a/test/parallel/test-repl-editor.js +++ b/test/parallel/test-repl-editor.js @@ -5,7 +5,9 @@ const assert = require('assert'); const repl = require('repl'); const ArrayStream = require('../common/arraystream'); -common.skipIfDumbTerminal(); +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} // \u001b[nG - Moves the cursor to n st column // \u001b[0J - Clear screen diff --git a/test/parallel/test-repl-history-navigation.js b/test/parallel/test-repl-history-navigation.js index 4df120d7cb9eae..64317be960e8d1 100644 --- a/test/parallel/test-repl-history-navigation.js +++ b/test/parallel/test-repl-history-navigation.js @@ -9,7 +9,9 @@ const assert = require('assert'); const fs = require('fs'); const { inspect } = require('util'); -common.skipIfDumbTerminal(); +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); diff --git a/test/parallel/test-repl-load-multiline-no-trailing-newline.js b/test/parallel/test-repl-load-multiline-no-trailing-newline.js index f57638d2521bbe..8fda91e35d1030 100644 --- a/test/parallel/test-repl-load-multiline-no-trailing-newline.js +++ b/test/parallel/test-repl-load-multiline-no-trailing-newline.js @@ -5,7 +5,9 @@ const fixtures = require('../common/fixtures'); const assert = require('assert'); const repl = require('repl'); -common.skipIfDumbTerminal(); +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} const command = `.load ${fixtures.path('repl-load-multiline-no-trailing-newline.js')}`; const terminalCode = '\u001b[1G\u001b[0J \u001b[1G'; diff --git a/test/parallel/test-repl-load-multiline.js b/test/parallel/test-repl-load-multiline.js index 4fcf206bef1be1..920f4b1c25d144 100644 --- a/test/parallel/test-repl-load-multiline.js +++ b/test/parallel/test-repl-load-multiline.js @@ -5,7 +5,9 @@ const fixtures = require('../common/fixtures'); const assert = require('assert'); const repl = require('repl'); -common.skipIfDumbTerminal(); +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} const command = `.load ${fixtures.path('repl-load-multiline.js')}`; const terminalCode = '\u001b[1G\u001b[0J \u001b[1G'; diff --git a/test/parallel/test-repl-mode.js b/test/parallel/test-repl-mode.js index aca8418904d082..f8a54d34089b00 100644 --- a/test/parallel/test-repl-mode.js +++ b/test/parallel/test-repl-mode.js @@ -4,7 +4,9 @@ const assert = require('assert'); const Stream = require('stream'); const repl = require('repl'); -common.skipIfDumbTerminal(); +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} const tests = [ testSloppyMode, diff --git a/test/parallel/test-repl-permission-model.js b/test/parallel/test-repl-permission-model.js index 938f5121163a23..ab5c7bff06cde8 100644 --- a/test/parallel/test-repl-permission-model.js +++ b/test/parallel/test-repl-permission-model.js @@ -8,7 +8,9 @@ const REPL = require('internal/repl'); const assert = require('assert'); const { inspect } = require('util'); -common.skipIfDumbTerminal(); +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} // Create an input stream specialized for testing an array of actions class ActionStream extends stream.Stream { diff --git a/test/parallel/test-repl-persistent-history.js b/test/parallel/test-repl-persistent-history.js index 99ba92eda4cf3d..f5e2d48139f449 100644 --- a/test/parallel/test-repl-persistent-history.js +++ b/test/parallel/test-repl-persistent-history.js @@ -11,7 +11,9 @@ const fs = require('fs'); const os = require('os'); const util = require('util'); -common.skipIfDumbTerminal(); +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); diff --git a/test/parallel/test-repl-programmatic-history.js b/test/parallel/test-repl-programmatic-history.js index 1ae5123c6c8ea1..aae15eb752c862 100644 --- a/test/parallel/test-repl-programmatic-history.js +++ b/test/parallel/test-repl-programmatic-history.js @@ -9,7 +9,9 @@ const fs = require('fs'); const os = require('os'); const util = require('util'); -common.skipIfDumbTerminal(); +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); diff --git a/test/parallel/test-repl-require-self-referential.js b/test/parallel/test-repl-require-self-referential.js index 7ced6dbf11721e..9a4fe000bbb7e3 100644 --- a/test/parallel/test-repl-require-self-referential.js +++ b/test/parallel/test-repl-require-self-referential.js @@ -4,9 +4,11 @@ const common = require('../common'); const fixtures = require('../common/fixtures'); const assert = require('assert'); const { spawn } = require('child_process'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('process.chdir is not available in Workers'); +} const selfRefModule = fixtures.path('self_ref_module'); const child = spawn(process.execPath, diff --git a/test/parallel/test-repl-require.js b/test/parallel/test-repl-require.js index fc431dea9f0f69..e740acef08b068 100644 --- a/test/parallel/test-repl-require.js +++ b/test/parallel/test-repl-require.js @@ -4,9 +4,11 @@ const common = require('../common'); const fixtures = require('../common/fixtures'); const assert = require('assert'); const net = require('net'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('process.chdir is not available in Workers'); +} process.chdir(fixtures.fixturesDir); const repl = require('repl'); diff --git a/test/parallel/test-repl-reverse-search.js b/test/parallel/test-repl-reverse-search.js index 93fb037c392c01..246488cbd0ef5f 100644 --- a/test/parallel/test-repl-reverse-search.js +++ b/test/parallel/test-repl-reverse-search.js @@ -9,7 +9,10 @@ const assert = require('assert'); const fs = require('fs'); const { inspect } = require('util'); -common.skipIfDumbTerminal(); +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} + common.allowGlobals('aaaa'); const tmpdir = require('../common/tmpdir'); diff --git a/test/parallel/test-repl-sigint-nested-eval.js b/test/parallel/test-repl-sigint-nested-eval.js index 62eb46e0af6759..7955cf413f7c49 100644 --- a/test/parallel/test-repl-sigint-nested-eval.js +++ b/test/parallel/test-repl-sigint-nested-eval.js @@ -4,8 +4,12 @@ if (common.isWindows) { // No way to send CTRL_C_EVENT to processes from JS right now. common.skip('platform not supported'); } -if (!common.isMainThread) + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { common.skip('No signal handling available in Workers'); +} const assert = require('assert'); const spawn = require('child_process').spawn; diff --git a/test/parallel/test-repl-sigint.js b/test/parallel/test-repl-sigint.js index 8ad0b2f5c2c853..f4087b11d488d6 100644 --- a/test/parallel/test-repl-sigint.js +++ b/test/parallel/test-repl-sigint.js @@ -4,8 +4,12 @@ if (common.isWindows) { // No way to send CTRL_C_EVENT to processes from JS right now. common.skip('platform not supported'); } -if (!common.isMainThread) + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { common.skip('No signal handling available in Workers'); +} const assert = require('assert'); const spawn = require('child_process').spawn; diff --git a/test/parallel/test-repl-strict-mode-previews.js b/test/parallel/test-repl-strict-mode-previews.js index a05e11b39cf3ee..e7fc1ea5191ea3 100644 --- a/test/parallel/test-repl-strict-mode-previews.js +++ b/test/parallel/test-repl-strict-mode-previews.js @@ -5,7 +5,10 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -common.skipIfDumbTerminal(); + +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} if (process.argv[2] === 'child') { const stream = require('stream'); diff --git a/test/parallel/test-repl-tab-complete-import.js b/test/parallel/test-repl-tab-complete-import.js index fe9f7a3d11795b..f4ef408c89174c 100644 --- a/test/parallel/test-repl-tab-complete-import.js +++ b/test/parallel/test-repl-tab-complete-import.js @@ -7,8 +7,11 @@ const assert = require('assert'); const { builtinModules } = require('module'); const publicUnprefixedModules = builtinModules.filter((lib) => !lib.startsWith('_') && !lib.startsWith('node:')); -if (!common.isMainThread) +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { common.skip('process.chdir is not available in Workers'); +} // We have to change the directory to ../fixtures before requiring repl // in order to make the tests for completion of node_modules work properly diff --git a/test/parallel/test-repl-tab-complete.js b/test/parallel/test-repl-tab-complete.js index ff1e927078ddf5..c79162129bd69b 100644 --- a/test/parallel/test-repl-tab-complete.js +++ b/test/parallel/test-repl-tab-complete.js @@ -34,9 +34,11 @@ const { builtinModules } = require('module'); const publicModules = builtinModules.filter((lib) => !lib.startsWith('_')); const hasInspector = process.features.inspector; +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('process.chdir is not available in Workers'); +} // We have to change the directory to ../fixtures before requiring repl // in order to make the tests for completion of node_modules work properly diff --git a/test/parallel/test-require-symlink.js b/test/parallel/test-require-symlink.js index 0c4477023bc90b..9ca543e8d64ca4 100644 --- a/test/parallel/test-require-symlink.js +++ b/test/parallel/test-require-symlink.js @@ -2,10 +2,14 @@ 'use strict'; const common = require('../common'); -if (!common.canCreateSymLink()) +if (!common.canCreateSymLink()) { common.skip('insufficient privileges'); -if (!common.isMainThread) +} +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { common.skip('process.chdir is not available in Workers'); +} const assert = require('assert'); const { spawn } = require('child_process'); diff --git a/test/parallel/test-runner-module-mocking.js b/test/parallel/test-runner-module-mocking.js index cb40df98147302..8502d4aa99a9b6 100644 --- a/test/parallel/test-runner-module-mocking.js +++ b/test/parallel/test-runner-module-mocking.js @@ -1,8 +1,9 @@ // Flags: --experimental-test-module-mocks --experimental-require-module 'use strict'; const common = require('../common'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) { +if (!isMainThread) { common.skip('registering customization hooks in Workers does not work'); } diff --git a/test/parallel/test-set-process-debug-port.js b/test/parallel/test-set-process-debug-port.js index d00a1ddf68ebb6..7f0cbe068549d0 100644 --- a/test/parallel/test-set-process-debug-port.js +++ b/test/parallel/test-set-process-debug-port.js @@ -2,7 +2,11 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -common.skipIfWorker(); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const assert = require('assert'); const kMinPort = 1024; diff --git a/test/parallel/test-setproctitle.js b/test/parallel/test-setproctitle.js index 7c4287829f7c0a..b08302e0a35ac0 100644 --- a/test/parallel/test-setproctitle.js +++ b/test/parallel/test-setproctitle.js @@ -1,15 +1,16 @@ 'use strict'; // Original test written by Jakub Lekstan const common = require('../common'); +const { isMainThread } = require('worker_threads'); // FIXME add sunos support -if (common.isSunOS) +if (common.isSunOS || common.isIBMi) { common.skip(`Unsupported platform [${process.platform}]`); -// FIXME add IBMi support -if (common.isIBMi) - common.skip('Unsupported platform IBMi'); -if (!common.isMainThread) +} + +if (!isMainThread) { common.skip('Setting the process title from Workers is not supported'); +} const assert = require('assert'); const { exec, execSync } = require('child_process'); @@ -25,8 +26,9 @@ process.title = title; assert.strictEqual(process.title, title); // Test setting the title but do not try to run `ps` on Windows. -if (common.isWindows) +if (common.isWindows) { common.skip('Windows does not have "ps" utility'); +} try { execSync('command -v ps'); diff --git a/test/parallel/test-shadow-realm-import-value-resolve.js b/test/parallel/test-shadow-realm-import-value-resolve.js index ee1c17d67c12f1..eeb00509d53a6c 100644 --- a/test/parallel/test-shadow-realm-import-value-resolve.js +++ b/test/parallel/test-shadow-realm-import-value-resolve.js @@ -3,8 +3,11 @@ const common = require('../common'); const assert = require('assert'); const path = require('path'); +const { isMainThread } = require('worker_threads'); -common.skipIfWorker('process.chdir is not supported in workers.'); +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} async function main() { const realm = new ShadowRealm(); diff --git a/test/parallel/test-signal-args.js b/test/parallel/test-signal-args.js index 7b72ed6dcb92d5..28a077ecc1c7d9 100644 --- a/test/parallel/test-signal-args.js +++ b/test/parallel/test-signal-args.js @@ -3,10 +3,15 @@ const common = require('../common'); const assert = require('assert'); -if (common.isWindows) +if (common.isWindows) { common.skip('Sending signals with process.kill is not supported on Windows'); -if (!common.isMainThread) +} + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { common.skip('No signal handling available in Workers'); +} process.once('SIGINT', common.mustCall((signal) => { assert.strictEqual(signal, 'SIGINT'); diff --git a/test/parallel/test-signal-handler.js b/test/parallel/test-signal-handler.js index 05ec4e7f73faf5..b84d2063a288db 100644 --- a/test/parallel/test-signal-handler.js +++ b/test/parallel/test-signal-handler.js @@ -23,10 +23,15 @@ const common = require('../common'); -if (common.isWindows) +if (common.isWindows) { common.skip('SIGUSR1 and SIGHUP signals are not supported'); -if (!common.isMainThread) +} + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { common.skip('Signal handling in Workers is not supported'); +} console.log(`process.pid: ${process.pid}`); diff --git a/test/parallel/test-stdio-pipe-access.js b/test/parallel/test-stdio-pipe-access.js index ac0e22c399a1b9..6bf6b107c60e92 100644 --- a/test/parallel/test-stdio-pipe-access.js +++ b/test/parallel/test-stdio-pipe-access.js @@ -1,7 +1,10 @@ 'use strict'; const common = require('../common'); -if (!common.isMainThread) +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { common.skip("Workers don't have process-like stdio"); +} // Test if Node handles accessing process.stdin if it is a redirected // pipe without deadlocking diff --git a/test/parallel/test-stdio-pipe-redirect.js b/test/parallel/test-stdio-pipe-redirect.js index 8b48133c8b0317..69367119ed3402 100644 --- a/test/parallel/test-stdio-pipe-redirect.js +++ b/test/parallel/test-stdio-pipe-redirect.js @@ -1,7 +1,10 @@ 'use strict'; const common = require('../common'); -if (!common.isMainThread) +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { common.skip("Workers don't have process-like stdio"); +} // Test if Node handles redirecting one child process stdout to another // process stdin without crashing. diff --git a/test/parallel/test-timers-immediate-unref-simple.js b/test/parallel/test-timers-immediate-unref-simple.js index 369894fcdebbae..fae8ad3eaea801 100644 --- a/test/parallel/test-timers-immediate-unref-simple.js +++ b/test/parallel/test-timers-immediate-unref-simple.js @@ -1,8 +1,9 @@ 'use strict'; const common = require('../common'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) { +if (!isMainThread) { // Note that test-timers-immediate-unref-nested-once works instead. common.skip('Worker bootstrapping works differently -> different timing'); } diff --git a/test/parallel/test-trace-events-api.js b/test/parallel/test-trace-events-api.js index 709f8de9097906..8792a40cf00c80 100644 --- a/test/parallel/test-trace-events-api.js +++ b/test/parallel/test-trace-events-api.js @@ -2,7 +2,12 @@ 'use strict'; const common = require('../common'); -common.skipIfWorker(); // https://github.com/nodejs/node/issues/22767 +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + // https://github.com/nodejs/node/issues/22767 + common.skip('This test only works on a main thread'); +} try { require('trace_events'); diff --git a/test/parallel/test-trace-events-dynamic-enable.js b/test/parallel/test-trace-events-dynamic-enable.js index 69251944031e1f..5b2ce313421568 100644 --- a/test/parallel/test-trace-events-dynamic-enable.js +++ b/test/parallel/test-trace-events-dynamic-enable.js @@ -4,7 +4,13 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -common.skipIfWorker(); // https://github.com/nodejs/node/issues/22767 + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + // https://github.com/nodejs/node/issues/22767 + common.skip('This test only works on a main thread'); +} const { internalBinding } = require('internal/test/binding'); diff --git a/test/parallel/test-warn-sigprof.js b/test/parallel/test-warn-sigprof.js index 36b0db78d82687..929deb69addb17 100644 --- a/test/parallel/test-warn-sigprof.js +++ b/test/parallel/test-warn-sigprof.js @@ -7,10 +7,15 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -if (common.isWindows) +if (common.isWindows) { common.skip('test does not apply to Windows'); +} -common.skipIfWorker(); // Worker inspector never has a server running +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} common.expectWarning('Warning', 'process.on(SIGPROF) is reserved while debugging'); diff --git a/test/parallel/test-worker-name.js b/test/parallel/test-worker-name.js index 952fcee0e05429..30f3710a826caf 100644 --- a/test/parallel/test-worker-name.js +++ b/test/parallel/test-worker-name.js @@ -4,10 +4,17 @@ const common = require('../common'); const fixtures = require('../common/fixtures'); common.skipIfInspectorDisabled(); -common.skipIfWorker(); // This test requires both main and worker threads. + +const { + Worker, + isMainThread, +} = require('worker_threads'); + +if (!isMainThread) { + common.skip('This test only works on a main thread'); +} const assert = require('assert'); -const { Worker, isMainThread } = require('worker_threads'); if (isMainThread) { const name = 'Hello Thread'; diff --git a/test/report/test-report-signal.js b/test/report/test-report-signal.js index cb5efd9fc39fe2..03908ddcf24f16 100644 --- a/test/report/test-report-signal.js +++ b/test/report/test-report-signal.js @@ -3,11 +3,15 @@ // Test producing a report via signal. const common = require('../common'); -if (common.isWindows) +if (common.isWindows) { return common.skip('Unsupported on Windows.'); +} -if (!common.isMainThread) +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { common.skip('Signal reporting is only supported in the main thread'); +} const assert = require('assert'); const helper = require('../common/report'); diff --git a/test/sequential/test-fs-watch.js b/test/sequential/test-fs-watch.js index cb12acfc115a4b..8db27a79e33d0a 100644 --- a/test/sequential/test-fs-watch.js +++ b/test/sequential/test-fs-watch.js @@ -29,9 +29,11 @@ const fs = require('fs'); const path = require('path'); const tmpdir = require('../common/tmpdir'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('process.chdir is not available in Workers'); +} const expectFilePath = common.isWindows || common.isLinux || diff --git a/test/sequential/test-heapdump.js b/test/sequential/test-heapdump.js index 1388623e61f939..f9df88375ae596 100644 --- a/test/sequential/test-heapdump.js +++ b/test/sequential/test-heapdump.js @@ -1,9 +1,11 @@ 'use strict'; const common = require('../common'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('process.chdir is not available in Workers'); +} const { writeHeapSnapshot, getHeapSnapshot } = require('v8'); const assert = require('assert'); diff --git a/test/sequential/test-init.js b/test/sequential/test-init.js index 7195369e0e4f8e..dd5db5640d1f0c 100644 --- a/test/sequential/test-init.js +++ b/test/sequential/test-init.js @@ -24,9 +24,11 @@ const common = require('../common'); const assert = require('assert'); const child = require('child_process'); const fixtures = require('../common/fixtures'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) { common.skip('process.chdir is not available in Workers'); +} if (process.env.TEST_INIT) { return process.stdout.write('Loaded successfully!'); diff --git a/test/sequential/test-perf-hooks.js b/test/sequential/test-perf-hooks.js index 1e11f26571480d..847decdef18bfc 100644 --- a/test/sequential/test-perf-hooks.js +++ b/test/sequential/test-perf-hooks.js @@ -1,11 +1,12 @@ 'use strict'; -const common = require('../common'); +require('../common'); const { performance } = require('perf_hooks'); // Get the start time as soon as possible. const testStartTime = performance.now(); const assert = require('assert'); const { writeSync } = require('fs'); +const { isMainThread } = require('worker_threads'); // Use writeSync to stdout to avoid disturbing the loop. function log(str) { @@ -131,7 +132,7 @@ function checkValue(timing, name, min, max) { } let loopStart = initialTiming.loopStart; -if (common.isMainThread) { +if (isMainThread) { // In the main thread, the loop does not start until we start an operation // that requires it, e.g. setTimeout(). assert.strictEqual(initialTiming.loopStart, -1); From 552362a36607f5b7cb3ec0420b5ee8b5bcfa347e Mon Sep 17 00:00:00 2001 From: James M Snell Date: Wed, 22 Jan 2025 15:08:23 -0800 Subject: [PATCH 12/38] test: make common/index slightly less node.js specific * s/global/globalThis * clean up knownGlobals a bit, make it a Set instead of an array and condense a bit. PR-URL: https://github.com/nodejs/node/pull/56712 Reviewed-By: Yagiz Nizipli Reviewed-By: Matteo Collina --- test/common/index.js | 140 ++++++++++++++++++------------------------- 1 file changed, 57 insertions(+), 83 deletions(-) diff --git a/test/common/index.js b/test/common/index.js index 6086d584f0b595..3647f4554a4647 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -20,7 +20,7 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. 'use strict'; -const process = global.process; // Some tests tamper with the process global. +const process = globalThis.process; // Some tests tamper with the process globalThis. const assert = require('assert'); const { exec, execSync, spawn, spawnSync } = require('child_process'); @@ -266,7 +266,7 @@ function platformTimeout(ms) { return ms; } -let knownGlobals = [ +const knownGlobals = new Set([ AbortController, atob, btoa, @@ -278,88 +278,59 @@ let knownGlobals = [ setInterval, setTimeout, queueMicrotask, -]; - -if (global.gc) { - knownGlobals.push(global.gc); -} - -if (global.navigator) { - knownGlobals.push(global.navigator); -} - -if (global.Navigator) { - knownGlobals.push(global.Navigator); -} - -if (global.Performance) { - knownGlobals.push(global.Performance); -} -if (global.performance) { - knownGlobals.push(global.performance); -} -if (global.PerformanceMark) { - knownGlobals.push(global.PerformanceMark); -} -if (global.PerformanceMeasure) { - knownGlobals.push(global.PerformanceMeasure); -} - -// TODO(@ethan-arrowood): Similar to previous checks, this can be temporary -// until v16.x is EOL. Once all supported versions have structuredClone we -// can add this to the list above instead. -if (global.structuredClone) { - knownGlobals.push(global.structuredClone); -} - -if (global.EventSource) { - knownGlobals.push(EventSource); -} - -if (global.fetch) { - knownGlobals.push(fetch); -} -if (hasCrypto && global.crypto) { - knownGlobals.push(global.crypto); - knownGlobals.push(global.Crypto); - knownGlobals.push(global.CryptoKey); - knownGlobals.push(global.SubtleCrypto); -} -if (global.CustomEvent) { - knownGlobals.push(global.CustomEvent); -} -if (global.ReadableStream) { - knownGlobals.push( - global.ReadableStream, - global.ReadableStreamDefaultReader, - global.ReadableStreamBYOBReader, - global.ReadableStreamBYOBRequest, - global.ReadableByteStreamController, - global.ReadableStreamDefaultController, - global.TransformStream, - global.TransformStreamDefaultController, - global.WritableStream, - global.WritableStreamDefaultWriter, - global.WritableStreamDefaultController, - global.ByteLengthQueuingStrategy, - global.CountQueuingStrategy, - global.TextEncoderStream, - global.TextDecoderStream, - global.CompressionStream, - global.DecompressionStream, - ); -} + structuredClone, + fetch, +]); + +['gc', + // The following are assumed to be conditionally available in the + // global object currently. They can likely be added to the fixed + // set of known globals, however. + 'navigator', + 'Navigator', + 'performance', + 'Performance', + 'PerformanceMark', + 'PerformanceMeasure', + 'EventSource', + 'CustomEvent', + 'ReadableStream', + 'ReadableStreamDefaultReader', + 'ReadableStreamBYOBReader', + 'ReadableStreamBYOBRequest', + 'ReadableByteStreamController', + 'ReadableStreamDefaultController', + 'TransformStream', + 'TransformStreamDefaultController', + 'WritableStream', + 'WritableStreamDefaultWriter', + 'WritableStreamDefaultController', + 'ByteLengthQueuingStrategy', + 'CountQueuingStrategy', + 'TextEncoderStream', + 'TextDecoderStream', + 'CompressionStream', + 'DecompressionStream', + 'Storage', + 'localStorage', + 'sessionStorage', +].forEach((i) => { + if (globalThis[i] !== undefined) { + knownGlobals.add(globalThis[i]); + } +}); -if (global.Storage) { - knownGlobals.push( - global.localStorage, - global.sessionStorage, - global.Storage, - ); +if (hasCrypto) { + knownGlobals.add(globalThis.crypto); + knownGlobals.add(globalThis.Crypto); + knownGlobals.add(globalThis.CryptoKey); + knownGlobals.add(globalThis.SubtleCrypto); } function allowGlobals(...allowlist) { - knownGlobals = knownGlobals.concat(allowlist); + for (const val of allowlist) { + knownGlobals.add(val); + } } if (process.env.NODE_TEST_KNOWN_GLOBALS !== '0') { @@ -371,10 +342,13 @@ if (process.env.NODE_TEST_KNOWN_GLOBALS !== '0') { function leakedGlobals() { const leaked = []; - for (const val in global) { + for (const val in globalThis) { // globalThis.crypto is a getter that throws if Node.js was compiled - // without OpenSSL. - if (val !== 'crypto' && !knownGlobals.includes(global[val])) { + // without OpenSSL so we'll skip it if it is not available. + if (val === 'crypto' && !hasCrypto) { + continue; + } + if (!knownGlobals.has(globalThis[val])) { leaked.push(val); } } From 97a3a8204c7c0eb35fc6c11274a7aee5d2ea3ddc Mon Sep 17 00:00:00 2001 From: James M Snell Date: Wed, 22 Jan 2025 15:30:30 -0800 Subject: [PATCH 13/38] test: replace more uses of `global` with `globalThis` PR-URL: https://github.com/nodejs/node/pull/56712 Reviewed-By: Yagiz Nizipli Reviewed-By: Matteo Collina --- test/parallel/test-aborted-util.js | 2 +- .../test-abortsignal-drop-settled-signals.mjs | 10 +-- test/parallel/test-assert-checktag.js | 8 +-- .../test-async-hooks-destroy-on-gc.js | 2 +- .../test-async-hooks-disable-gc-tracking.js | 2 +- ...test-async-hooks-prevent-double-destroy.js | 2 +- test/parallel/test-cli-eval.js | 2 +- test/parallel/test-common-gc.js | 4 +- .../parallel/test-console-assign-undefined.js | 16 ++--- test/parallel/test-console-instance.js | 4 +- test/parallel/test-console-self-assign.js | 2 +- test/parallel/test-crypto-dh-leak.js | 2 +- test/parallel/test-domain-crypto.js | 2 +- .../test-eventtarget-memoryleakwarning.js | 2 +- test/parallel/test-eventtarget.js | 2 +- .../test-finalization-registry-shutdown.js | 2 +- test/parallel/test-fs-filehandle.js | 2 +- .../test-fs-promises-file-handle-close.js | 2 +- test/parallel/test-fs-write-reuse-callback.js | 2 +- test/parallel/test-fs-write.js | 8 ++- .../test-gc-http-client-connaborted.js | 2 +- test/parallel/test-gc-net-timeout.js | 2 +- test/parallel/test-gc-tls-external-memory.js | 2 +- test/parallel/test-global-setters.js | 12 ++-- test/parallel/test-global.js | 14 ++-- ...-h2leak-destroy-session-on-socket-ended.js | 4 +- .../test-heapdump-async-hooks-init-promise.js | 2 +- .../test-http-agent-domain-reused-gc.js | 2 +- test/parallel/test-http-parser-bad-ref.js | 2 +- ...t-http-server-connections-checking-leak.js | 2 +- .../test-http-server-keepalive-req-gc.js | 4 +- test/parallel/test-http2-createwritereq.js | 2 +- ...-http2-session-gc-while-write-scheduled.js | 2 +- ...tp2-write-finishes-after-stream-destroy.js | 2 +- ...-https-server-connections-checking-leak.js | 2 +- .../test-inspector-scriptparsed-context.js | 4 +- ...r-vm-global-accessors-getter-sideeffect.js | 2 +- test/parallel/test-module-relative-lookup.js | 2 +- test/parallel/test-net-connect-memleak.js | 2 +- .../test-net-write-fully-async-buffer.js | 2 +- .../test-net-write-fully-async-hex-string.js | 2 +- test/parallel/test-performance-gc.js | 4 +- test/parallel/test-primitive-timer-leak.js | 2 +- test/parallel/test-repl-autolibs.js | 6 +- test/parallel/test-repl-underscore.js | 4 +- test/parallel/test-repl-use-global.js | 6 +- test/parallel/test-repl.js | 6 +- test/parallel/test-runner-mock-timers.js | 68 +++++++++---------- test/parallel/test-timers-api-refs.js | 12 ++-- .../parallel/test-timers-process-tampering.js | 4 +- test/parallel/test-tls-connect-memleak.js | 2 +- test/parallel/test-tls-securepair-leak.js | 2 +- ...test-tls-transport-destroy-after-own-gc.js | 6 +- test/parallel/test-trace-events-api.js | 2 +- test/parallel/test-util-format.js | 4 +- test/parallel/test-util-inspect.js | 6 +- test/parallel/test-vm-basic.js | 6 +- .../test-vm-create-and-run-in-context.js | 2 +- test/parallel/test-vm-cross-context.js | 2 +- test/parallel/test-vm-global-get-own.js | 26 +++---- test/parallel/test-vm-measure-memory-lazy.js | 10 +-- test/parallel/test-vm-module-basic.js | 12 ++-- .../test-vm-new-script-new-context.js | 34 +++++----- .../test-vm-new-script-this-context.js | 32 ++++----- test/parallel/test-vm-run-in-new-context.js | 34 +++++----- test/parallel/test-vm-static-this.js | 26 +++---- test/parallel/test-webstorage.js | 4 +- .../parallel/test-whatwg-url-custom-global.js | 4 +- test/parallel/test-worker-cli-options.js | 2 +- ...orker-message-channel-sharedarraybuffer.js | 2 +- .../parallel/test-worker-message-port-move.js | 2 +- ...est-worker-workerdata-sharedarraybuffer.js | 2 +- .../test-zlib-invalid-input-memory.js | 2 +- test/parallel/test-zlib-unused-weak.js | 4 +- 74 files changed, 246 insertions(+), 242 deletions(-) diff --git a/test/parallel/test-aborted-util.js b/test/parallel/test-aborted-util.js index 4bc45b9f5529bb..0566204ccdb074 100644 --- a/test/parallel/test-aborted-util.js +++ b/test/parallel/test-aborted-util.js @@ -32,7 +32,7 @@ test('Aborted with gc cleanup', async () => { const { promise, resolve } = Promise.withResolvers(); setImmediate(() => { - global.gc(); + globalThis.gc(); ac.abort(); strictEqual(ac.signal.aborted, true); strictEqual(getEventListeners(ac.signal, 'abort').length, 0); diff --git a/test/parallel/test-abortsignal-drop-settled-signals.mjs b/test/parallel/test-abortsignal-drop-settled-signals.mjs index 0abcaf81012716..b300b0e223fc93 100644 --- a/test/parallel/test-abortsignal-drop-settled-signals.mjs +++ b/test/parallel/test-abortsignal-drop-settled-signals.mjs @@ -16,7 +16,7 @@ function makeSubsequentCalls(limit, done, holdReferences = false) { // This setImmediate is necessary to ensure that in the last iteration the remaining signal is GCed (if not // retained) setImmediate(() => { - global.gc(); + globalThis.gc(); done(ac.signal, dependantSymbol); }); return; @@ -50,7 +50,7 @@ function runShortLivedSourceSignal(limit, done) { function run(iteration) { if (iteration > limit) { - global.gc(); + globalThis.gc(); done(signalRefs); return; } @@ -74,9 +74,9 @@ function runWithOrphanListeners(limit, done) { const ac = new AbortController(); if (iteration > limit) { setImmediate(() => { - global.gc(); + globalThis.gc(); setImmediate(() => { - global.gc(); + globalThis.gc(); done(composedSignalRefs); }); @@ -147,7 +147,7 @@ it('drops settled dependant signals when signal is composite', (t, done) => { t.assert.strictEqual(controllers[1].signal[kDependantSignals].size, 1); setImmediate(() => { - global.gc({ execution: 'async' }).then(async () => { + globalThis.gc({ execution: 'async' }).then(async () => { await gcUntil('all signals are GCed', () => { const totalDependantSignals = Math.max( controllers[0].signal[kDependantSignals].size, diff --git a/test/parallel/test-assert-checktag.js b/test/parallel/test-assert-checktag.js index 7587939436f40d..b86a1bde7f096d 100644 --- a/test/parallel/test-assert-checktag.js +++ b/test/parallel/test-assert-checktag.js @@ -49,13 +49,13 @@ test('', { skip: !hasCrypto }, () => { { // At the moment global has its own type tag const fakeGlobal = {}; - Object.setPrototypeOf(fakeGlobal, Object.getPrototypeOf(global)); - for (const prop of Object.keys(global)) { + Object.setPrototypeOf(fakeGlobal, Object.getPrototypeOf(globalThis)); + for (const prop of Object.keys(globalThis)) { fakeGlobal[prop] = global[prop]; } - assert.notDeepEqual(fakeGlobal, global); + assert.notDeepEqual(fakeGlobal, globalThis); // Message will be truncated anyway, don't validate - assert.throws(() => assert.deepStrictEqual(fakeGlobal, global), + assert.throws(() => assert.deepStrictEqual(fakeGlobal, globalThis), assert.AssertionError); } diff --git a/test/parallel/test-async-hooks-destroy-on-gc.js b/test/parallel/test-async-hooks-destroy-on-gc.js index fe6325e189734b..dd7eef8776cdf3 100644 --- a/test/parallel/test-async-hooks-destroy-on-gc.js +++ b/test/parallel/test-async-hooks-destroy-on-gc.js @@ -22,6 +22,6 @@ let asyncId = null; } setImmediate(() => { - global.gc(); + globalThis.gc(); setImmediate(() => assert.ok(destroyedIds.has(asyncId))); }); diff --git a/test/parallel/test-async-hooks-disable-gc-tracking.js b/test/parallel/test-async-hooks-disable-gc-tracking.js index 84c5043aad3335..87b096c258121c 100644 --- a/test/parallel/test-async-hooks-disable-gc-tracking.js +++ b/test/parallel/test-async-hooks-disable-gc-tracking.js @@ -14,7 +14,7 @@ const hook = async_hooks.createHook({ new async_hooks.AsyncResource('foobar', { requireManualDestroy: true }); setImmediate(() => { - global.gc(); + globalThis.gc(); setImmediate(() => { hook.disable(); }); diff --git a/test/parallel/test-async-hooks-prevent-double-destroy.js b/test/parallel/test-async-hooks-prevent-double-destroy.js index 689dc399f9d2f2..4aa55a5a6c87bf 100644 --- a/test/parallel/test-async-hooks-prevent-double-destroy.js +++ b/test/parallel/test-async-hooks-prevent-double-destroy.js @@ -17,7 +17,7 @@ const hook = async_hooks.createHook({ } setImmediate(() => { - global.gc(); + globalThis.gc(); setImmediate(() => { hook.disable(); }); diff --git a/test/parallel/test-cli-eval.js b/test/parallel/test-cli-eval.js index 24031581fd737e..9ec0fece409068 100644 --- a/test/parallel/test-cli-eval.js +++ b/test/parallel/test-cli-eval.js @@ -345,7 +345,7 @@ child.exec( // Regression test for https://github.com/nodejs/node/issues/45336 child.execFile(process.execPath, ['-p', - 'Object.defineProperty(global, "fs", { configurable: false });' + + 'Object.defineProperty(globalThis, "fs", { configurable: false });' + 'fs === require("node:fs")'], common.mustSucceed((stdout) => { assert.match(stdout, /^true/); diff --git a/test/parallel/test-common-gc.js b/test/parallel/test-common-gc.js index f7d73ccd0423e3..54abe3695cc3be 100644 --- a/test/parallel/test-common-gc.js +++ b/test/parallel/test-common-gc.js @@ -5,10 +5,10 @@ const { onGC } = require('../common/gc'); { onGC({}, { ongc: common.mustCall() }); - global.gc(); + globalThis.gc(); } { onGC(process, { ongc: common.mustNotCall() }); - global.gc(); + globalThis.gc(); } diff --git a/test/parallel/test-console-assign-undefined.js b/test/parallel/test-console-assign-undefined.js index 1021307b3c5f22..7f5b0e04727679 100644 --- a/test/parallel/test-console-assign-undefined.js +++ b/test/parallel/test-console-assign-undefined.js @@ -1,28 +1,28 @@ 'use strict'; -// Patch global.console before importing modules that may modify the console +// Patch globalThis.console before importing modules that may modify the console // object. -const tmp = global.console; -global.console = 42; +const tmp = globalThis.console; +globalThis.console = 42; require('../common'); const assert = require('assert'); // Originally the console had a getter. Test twice to verify it had no side // effect. -assert.strictEqual(global.console, 42); -assert.strictEqual(global.console, 42); +assert.strictEqual(globalThis.console, 42); +assert.strictEqual(globalThis.console, 42); assert.throws( () => console.log('foo'), { name: 'TypeError' } ); -global.console = 1; -assert.strictEqual(global.console, 1); +globalThis.console = 1; +assert.strictEqual(globalThis.console, 1); assert.strictEqual(console, 1); // Reset the console -global.console = tmp; +globalThis.console = tmp; console.log('foo'); diff --git a/test/parallel/test-console-instance.js b/test/parallel/test-console-instance.js index bf22314e22e031..0364a6213bc726 100644 --- a/test/parallel/test-console-instance.js +++ b/test/parallel/test-console-instance.js @@ -36,9 +36,9 @@ process.stdout.write = process.stderr.write = common.mustNotCall(); // Make sure that the "Console" function exists. assert.strictEqual(typeof Console, 'function'); -assert.strictEqual(requiredConsole, global.console); +assert.strictEqual(requiredConsole, globalThis.console); // Make sure the custom instanceof of Console works -assert.ok(global.console instanceof Console); +assert.ok(globalThis.console instanceof Console); assert.ok(!({} instanceof Console)); // Make sure that the Console constructor throws diff --git a/test/parallel/test-console-self-assign.js b/test/parallel/test-console-self-assign.js index 53c54ab9a327cf..46f9bc93d4f2bf 100644 --- a/test/parallel/test-console-self-assign.js +++ b/test/parallel/test-console-self-assign.js @@ -3,4 +3,4 @@ require('../common'); // Assigning to itself should not throw. -global.console = global.console; // eslint-disable-line no-self-assign +globalThis.console = globalThis.console; // eslint-disable-line no-self-assign diff --git a/test/parallel/test-crypto-dh-leak.js b/test/parallel/test-crypto-dh-leak.js index 3b5051feb43cd8..df1ba89737c619 100644 --- a/test/parallel/test-crypto-dh-leak.js +++ b/test/parallel/test-crypto-dh-leak.js @@ -22,7 +22,7 @@ const before = process.memoryUsage.rss(); dh.setPrivateKey(privateKey); } } -global.gc(); +globalThis.gc(); const after = process.memoryUsage.rss(); // RSS should stay the same, ceteris paribus, but allow for diff --git a/test/parallel/test-domain-crypto.js b/test/parallel/test-domain-crypto.js index e0a470bd9db515..47eb33f70aae45 100644 --- a/test/parallel/test-domain-crypto.js +++ b/test/parallel/test-domain-crypto.js @@ -31,7 +31,7 @@ const crypto = require('crypto'); // Pollution of global is intentional as part of test. common.allowGlobals(require('domain')); // See https://github.com/nodejs/node/commit/d1eff9ab -global.domain = require('domain'); +globalThis.domain = require('domain'); // Should not throw a 'TypeError: undefined is not a function' exception crypto.randomBytes(8); diff --git a/test/parallel/test-eventtarget-memoryleakwarning.js b/test/parallel/test-eventtarget-memoryleakwarning.js index 2c907165d865d9..2ec48720c8a8c5 100644 --- a/test/parallel/test-eventtarget-memoryleakwarning.js +++ b/test/parallel/test-eventtarget-memoryleakwarning.js @@ -103,7 +103,7 @@ common.expectWarning({ }); await setTimeout(0); - global.gc(); + globalThis.gc(); } })().then(common.mustCall(), common.mustNotCall()); } diff --git a/test/parallel/test-eventtarget.js b/test/parallel/test-eventtarget.js index cbe7eb3b0e8687..7153da172c1cf6 100644 --- a/test/parallel/test-eventtarget.js +++ b/test/parallel/test-eventtarget.js @@ -683,7 +683,7 @@ let asyncTest = Promise.resolve(); const et = new EventTarget(); et.addEventListener('foo', common.mustNotCall(), { [kWeakHandler]: {} }); setImmediate(() => { - global.gc(); + globalThis.gc(); et.dispatchEvent(new Event('foo')); }); } diff --git a/test/parallel/test-finalization-registry-shutdown.js b/test/parallel/test-finalization-registry-shutdown.js index f896aa2f285c75..e288d8fecca7e6 100644 --- a/test/parallel/test-finalization-registry-shutdown.js +++ b/test/parallel/test-finalization-registry-shutdown.js @@ -19,5 +19,5 @@ process.on('exit', () => { // This is the final chance to execute JavaScript. register(); // Queue a FinalizationRegistryCleanupTask by a testing gc request. - global.gc(); + globalThis.gc(); }); diff --git a/test/parallel/test-fs-filehandle.js b/test/parallel/test-fs-filehandle.js index 818a3824904431..bcb62da9e4c2cc 100644 --- a/test/parallel/test-fs-filehandle.js +++ b/test/parallel/test-fs-filehandle.js @@ -35,6 +35,6 @@ common.expectWarning({ 'DeprecationWarning': [[deprecationWarning, 'DEP0137']] }); -global.gc(); +globalThis.gc(); setTimeout(() => {}, 10); diff --git a/test/parallel/test-fs-promises-file-handle-close.js b/test/parallel/test-fs-promises-file-handle-close.js index d6417964746720..288bc31ea0ada5 100644 --- a/test/parallel/test-fs-promises-file-handle-close.js +++ b/test/parallel/test-fs-promises-file-handle-close.js @@ -32,7 +32,7 @@ doOpen().then(common.mustCall((fd) => { })).then(common.mustCall(() => { setImmediate(() => { // The FileHandle should be out-of-scope and no longer accessed now. - global.gc(); + globalThis.gc(); // Wait an extra event loop turn, as the warning is emitted from the // native layer in an unref()'ed setImmediate() callback. diff --git a/test/parallel/test-fs-write-reuse-callback.js b/test/parallel/test-fs-write-reuse-callback.js index 82c772ab340fed..c80902e54103fc 100644 --- a/test/parallel/test-fs-write-reuse-callback.js +++ b/test/parallel/test-fs-write-reuse-callback.js @@ -20,7 +20,7 @@ let done = 0; const ondone = common.mustSucceed(() => { if (++done < writes) { - if (done % 25 === 0) global.gc(); + if (done % 25 === 0) globalThis.gc(); setImmediate(write); } else { assert.strictEqual( diff --git a/test/parallel/test-fs-write.js b/test/parallel/test-fs-write.js index a4aeb4e16a748f..82f3425de2aa16 100644 --- a/test/parallel/test-fs-write.js +++ b/test/parallel/test-fs-write.js @@ -39,14 +39,18 @@ const { createExternalizableString, externalizeString, isOneByteString, -} = global; +} = globalThis; + +assert.notStrictEqual(createExternalizableString, undefined); +assert.notStrictEqual(externalizeString, undefined); +assert.notStrictEqual(isOneByteString, undefined); // Account for extra globals exposed by --expose_externalize_string. common.allowGlobals( createExternalizableString, externalizeString, isOneByteString, - global.x, + globalThis.x, ); { diff --git a/test/parallel/test-gc-http-client-connaborted.js b/test/parallel/test-gc-http-client-connaborted.js index 93ca8ee4de59f1..e52a555d788085 100644 --- a/test/parallel/test-gc-http-client-connaborted.js +++ b/test/parallel/test-gc-http-client-connaborted.js @@ -53,7 +53,7 @@ setImmediate(status); function status() { if (done > 0) { createClients = false; - global.gc(); + globalThis.gc(); console.log(`done/collected/total: ${done}/${countGC}/${count}`); if (countGC === count) { server.close(); diff --git a/test/parallel/test-gc-net-timeout.js b/test/parallel/test-gc-net-timeout.js index c4f74b34b79ec9..7a195c26bcdd6b 100644 --- a/test/parallel/test-gc-net-timeout.js +++ b/test/parallel/test-gc-net-timeout.js @@ -64,7 +64,7 @@ setImmediate(status); function status() { if (done > 0) { createClients = false; - global.gc(); + globalThis.gc(); console.log(`done/collected/total: ${done}/${countGC}/${count}`); if (countGC === count) { server.close(); diff --git a/test/parallel/test-gc-tls-external-memory.js b/test/parallel/test-gc-tls-external-memory.js index dcf38e11f6c6bf..480b1086b5395e 100644 --- a/test/parallel/test-gc-tls-external-memory.js +++ b/test/parallel/test-gc-tls-external-memory.js @@ -27,7 +27,7 @@ connect(); function connect() { if (runs % 64 === 0) - global.gc(); + globalThis.gc(); const externalMemoryUsage = process.memoryUsage().external; assert(externalMemoryUsage >= 0, `${externalMemoryUsage} < 0`); if (runs++ === 512) { diff --git a/test/parallel/test-global-setters.js b/test/parallel/test-global-setters.js index 7fd070ed8e1c4e..2da1097867261f 100644 --- a/test/parallel/test-global-setters.js +++ b/test/parallel/test-global-setters.js @@ -8,20 +8,20 @@ assert.strictEqual(process, _process); // eslint-disable-next-line no-global-assign process = 'asdf'; assert.strictEqual(process, 'asdf'); -assert.strictEqual(global.process, 'asdf'); -global.process = _process; +assert.strictEqual(globalThis.process, 'asdf'); +globalThis.process = _process; assert.strictEqual(process, _process); assert.strictEqual( - typeof Object.getOwnPropertyDescriptor(global, 'process').get, + typeof Object.getOwnPropertyDescriptor(globalThis, 'process').get, 'function'); assert.strictEqual(Buffer, _Buffer); // eslint-disable-next-line no-global-assign Buffer = 'asdf'; assert.strictEqual(Buffer, 'asdf'); -assert.strictEqual(global.Buffer, 'asdf'); -global.Buffer = _Buffer; +assert.strictEqual(globalThis.Buffer, 'asdf'); +globalThis.Buffer = _Buffer; assert.strictEqual(Buffer, _Buffer); assert.strictEqual( - typeof Object.getOwnPropertyDescriptor(global, 'Buffer').get, + typeof Object.getOwnPropertyDescriptor(globalThis, 'Buffer').get, 'function'); diff --git a/test/parallel/test-global.js b/test/parallel/test-global.js index 37f4db5252be5c..835bcc75a83e3b 100644 --- a/test/parallel/test-global.js +++ b/test/parallel/test-global.js @@ -60,9 +60,9 @@ for (const moduleName of builtinModules) { 'crypto', 'navigator', ]; - assert.deepStrictEqual(new Set(Object.keys(global)), new Set(expected)); + assert.deepStrictEqual(new Set(Object.keys(globalThis)), new Set(expected)); expected.forEach((value) => { - const desc = Object.getOwnPropertyDescriptor(global, value); + const desc = Object.getOwnPropertyDescriptor(globalThis, value); if (typeof desc.value === 'function') { assert.strictEqual(desc.value.name, value); } else if (typeof desc.get === 'function') { @@ -74,15 +74,15 @@ for (const moduleName of builtinModules) { common.allowGlobals('bar', 'foo'); baseFoo = 'foo'; // eslint-disable-line no-undef -global.baseBar = 'bar'; +globalThis.baseBar = 'bar'; -assert.strictEqual(global.baseFoo, 'foo', - `x -> global.x failed: global.baseFoo = ${global.baseFoo}`); +assert.strictEqual(globalThis.baseFoo, 'foo', + `x -> globalThis.x failed: globalThis.baseFoo = ${globalThis.baseFoo}`); assert.strictEqual(baseBar, // eslint-disable-line no-undef 'bar', // eslint-disable-next-line no-undef - `global.x -> x failed: baseBar = ${baseBar}`); + `globalThis.x -> x failed: baseBar = ${baseBar}`); const mod = require(fixtures.path('global', 'plain')); const fooBar = mod.fooBar; @@ -91,4 +91,4 @@ assert.strictEqual(fooBar.foo, 'foo'); assert.strictEqual(fooBar.bar, 'bar'); -assert.strictEqual(Object.prototype.toString.call(global), '[object global]'); +assert.strictEqual(Object.prototype.toString.call(globalThis), '[object global]'); diff --git a/test/parallel/test-h2leak-destroy-session-on-socket-ended.js b/test/parallel/test-h2leak-destroy-session-on-socket-ended.js index 3f0fe3e69d924d..af692b278f7d06 100644 --- a/test/parallel/test-h2leak-destroy-session-on-socket-ended.js +++ b/test/parallel/test-h2leak-destroy-session-on-socket-ended.js @@ -31,8 +31,8 @@ server.on('secureConnection', (s) => { firstServerStream = null; setImmediate(() => { - global.gc(); - global.gc(); + globalThis.gc(); + globalThis.gc(); server.close(); }); diff --git a/test/parallel/test-heapdump-async-hooks-init-promise.js b/test/parallel/test-heapdump-async-hooks-init-promise.js index c59cb89baa3d18..63b26843d1254e 100644 --- a/test/parallel/test-heapdump-async-hooks-init-promise.js +++ b/test/parallel/test-heapdump-async-hooks-init-promise.js @@ -43,4 +43,4 @@ async_hooks.createHook({ Promise.resolve().then(() => {}); -setImmediate(global.gc); +setImmediate(globalThis.gc); diff --git a/test/parallel/test-http-agent-domain-reused-gc.js b/test/parallel/test-http-agent-domain-reused-gc.js index 35146ee688eb9b..4f12c2ede839cd 100644 --- a/test/parallel/test-http-agent-domain-reused-gc.js +++ b/test/parallel/test-http-agent-domain-reused-gc.js @@ -26,7 +26,7 @@ async_hooks.createHook({ }, before(id) { if (id === reusedHandleId) { - global.gc(); + globalThis.gc(); checkBeforeCalled(); } } diff --git a/test/parallel/test-http-parser-bad-ref.js b/test/parallel/test-http-parser-bad-ref.js index 2c1bfe67485db7..e34054eca67063 100644 --- a/test/parallel/test-http-parser-bad-ref.js +++ b/test/parallel/test-http-parser-bad-ref.js @@ -18,7 +18,7 @@ let messagesComplete = 0; function flushPool() { Buffer.allocUnsafe(Buffer.poolSize - 1); - global.gc(); + globalThis.gc(); } function demoBug(part1, part2) { diff --git a/test/parallel/test-http-server-connections-checking-leak.js b/test/parallel/test-http-server-connections-checking-leak.js index 282c9a569fba7d..38dca83102cfea 100644 --- a/test/parallel/test-http-server-connections-checking-leak.js +++ b/test/parallel/test-http-server-connections-checking-leak.js @@ -20,5 +20,5 @@ for (let i = 0; i < max; i++) { } setImmediate(() => { - global.gc(); + globalThis.gc(); }); diff --git a/test/parallel/test-http-server-keepalive-req-gc.js b/test/parallel/test-http-server-keepalive-req-gc.js index 3bfb6c9600cc24..c827cd19ad7222 100644 --- a/test/parallel/test-http-server-keepalive-req-gc.js +++ b/test/parallel/test-http-server-keepalive-req-gc.js @@ -16,8 +16,8 @@ const server = createServer(common.mustCall((req, res) => { req.on('end', common.mustCall(() => { setImmediate(async () => { client.end(); - await global.gc({ type: 'major', execution: 'async' }); - await global.gc({ type: 'major', execution: 'async' }); + await globalThis.gc({ type: 'major', execution: 'async' }); + await globalThis.gc({ type: 'major', execution: 'async' }); }); })); res.end('hello world'); diff --git a/test/parallel/test-http2-createwritereq.js b/test/parallel/test-http2-createwritereq.js index 3015ad6c642801..6d2b07d5849ad0 100644 --- a/test/parallel/test-http2-createwritereq.js +++ b/test/parallel/test-http2-createwritereq.js @@ -69,7 +69,7 @@ server.listen(0, common.mustCall(function() { req.destroy = function(...args) { // Schedule a garbage collection event at the end of the current // MakeCallback() run. - process.nextTick(global.gc); + process.nextTick(globalThis.gc); return origDestroy.call(this, ...args); }; diff --git a/test/parallel/test-http2-session-gc-while-write-scheduled.js b/test/parallel/test-http2-session-gc-while-write-scheduled.js index 62379f7d7ed678..9693ded17c0a18 100644 --- a/test/parallel/test-http2-session-gc-while-write-scheduled.js +++ b/test/parallel/test-http2-session-gc-while-write-scheduled.js @@ -23,6 +23,6 @@ const tick = require('../common/tick'); // This schedules a write. client.settings(http2.getDefaultSettings()); client = null; - global.gc(); + globalThis.gc(); }); } diff --git a/test/parallel/test-http2-write-finishes-after-stream-destroy.js b/test/parallel/test-http2-write-finishes-after-stream-destroy.js index ed8833fdb926b1..bf9de8f9291917 100644 --- a/test/parallel/test-http2-write-finishes-after-stream-destroy.js +++ b/test/parallel/test-http2-write-finishes-after-stream-destroy.js @@ -9,7 +9,7 @@ const { duplexPair } = require('stream'); // Make sure the Http2Stream destructor works, since we don't clean the // stream up like we would otherwise do. -process.on('exit', global.gc); +process.on('exit', globalThis.gc); { const [ clientSide, serverSide ] = duplexPair(); diff --git a/test/parallel/test-https-server-connections-checking-leak.js b/test/parallel/test-https-server-connections-checking-leak.js index e920c8e403705f..f79149ef70a9ab 100644 --- a/test/parallel/test-https-server-connections-checking-leak.js +++ b/test/parallel/test-https-server-connections-checking-leak.js @@ -25,5 +25,5 @@ for (let i = 0; i < max; i++) { } setImmediate(() => { - global.gc(); + globalThis.gc(); }); diff --git a/test/parallel/test-inspector-scriptparsed-context.js b/test/parallel/test-inspector-scriptparsed-context.js index bd86ba53d4c986..31ae896c818b82 100644 --- a/test/parallel/test-inspector-scriptparsed-context.js +++ b/test/parallel/test-inspector-scriptparsed-context.js @@ -8,8 +8,8 @@ const script = ` 'use strict'; const assert = require('assert'); const vm = require('vm'); - global.outer = true; - global.inner = false; + globalThis.outer = true; + globalThis.inner = false; const context = vm.createContext({ outer: false, inner: true diff --git a/test/parallel/test-inspector-vm-global-accessors-getter-sideeffect.js b/test/parallel/test-inspector-vm-global-accessors-getter-sideeffect.js index 8b367e98c37f49..89414e50346871 100644 --- a/test/parallel/test-inspector-vm-global-accessors-getter-sideeffect.js +++ b/test/parallel/test-inspector-vm-global-accessors-getter-sideeffect.js @@ -14,7 +14,7 @@ session.connect(); const context = vm.createContext({ get a() { - global.foo = '1'; + globalThis.foo = '1'; return 100; } }); diff --git a/test/parallel/test-module-relative-lookup.js b/test/parallel/test-module-relative-lookup.js index 675c12c541fd4d..76af2b3b30c2e0 100644 --- a/test/parallel/test-module-relative-lookup.js +++ b/test/parallel/test-module-relative-lookup.js @@ -2,7 +2,7 @@ const common = require('../common'); const assert = require('assert'); -const _module = require('module'); // Avoid collision with global.module +const _module = require('module'); // Avoid collision with globalThis.module // Current directory gets highest priority for local modules function testFirstInPath(moduleName, isLocalModule) { diff --git a/test/parallel/test-net-connect-memleak.js b/test/parallel/test-net-connect-memleak.js index 84f643746838b6..079e45f7223a8b 100644 --- a/test/parallel/test-net-connect-memleak.js +++ b/test/parallel/test-net-connect-memleak.js @@ -49,7 +49,7 @@ const gcListener = { ongc() { collected = true; } }; } function done(sock) { - global.gc(); + globalThis.gc(); setImmediate(() => { assert.strictEqual(collected, true); sock.end(); diff --git a/test/parallel/test-net-write-fully-async-buffer.js b/test/parallel/test-net-write-fully-async-buffer.js index 0dddb51bd76ade..042dd79cb03127 100644 --- a/test/parallel/test-net-write-fully-async-buffer.js +++ b/test/parallel/test-net-write-fully-async-buffer.js @@ -23,7 +23,7 @@ const server = net.createServer(common.mustCall(function(conn) { } while (conn.write(Buffer.from(data))); - global.gc({ type: 'minor' }); + globalThis.gc({ type: 'minor' }); // The buffer allocated above should still be alive. } diff --git a/test/parallel/test-net-write-fully-async-hex-string.js b/test/parallel/test-net-write-fully-async-hex-string.js index 37b5cd75c1385c..b80b09f3244585 100644 --- a/test/parallel/test-net-write-fully-async-hex-string.js +++ b/test/parallel/test-net-write-fully-async-hex-string.js @@ -21,7 +21,7 @@ const server = net.createServer(common.mustCall(function(conn) { } while (conn.write(data, 'hex')); - global.gc({ type: 'minor' }); + globalThis.gc({ type: 'minor' }); // The buffer allocated inside the .write() call should still be alive. } diff --git a/test/parallel/test-performance-gc.js b/test/parallel/test-performance-gc.js index 9c4a3a850a22dd..9dddf7207f59d2 100644 --- a/test/parallel/test-performance-gc.js +++ b/test/parallel/test-performance-gc.js @@ -40,7 +40,7 @@ const kinds = [ obs.disconnect(); })); obs.observe({ entryTypes: ['gc'] }); - global.gc(); + globalThis.gc(); // Keep the event loop alive to witness the GC async callback happen. setImmediate(() => setImmediate(() => 0)); } @@ -51,6 +51,6 @@ const kinds = [ process.on('beforeExit', () => { assert(!didCall); didCall = true; - global.gc(); + globalThis.gc(); }); } diff --git a/test/parallel/test-primitive-timer-leak.js b/test/parallel/test-primitive-timer-leak.js index d590a0347b9cac..a0fe2765e1282d 100644 --- a/test/parallel/test-primitive-timer-leak.js +++ b/test/parallel/test-primitive-timer-leak.js @@ -5,7 +5,7 @@ const { onGC } = require('../common/gc'); // See https://github.com/nodejs/node/issues/53335 const poller = setInterval(() => { - global.gc(); + globalThis.gc(); }, 100); let count = 0; diff --git a/test/parallel/test-repl-autolibs.js b/test/parallel/test-repl-autolibs.js index 5cf3b1497221d0..a1eb476ef530b1 100644 --- a/test/parallel/test-repl-autolibs.js +++ b/test/parallel/test-repl-autolibs.js @@ -41,7 +41,7 @@ function test1() { assert.strictEqual(data, `${util.inspect(require('fs'), null, 2, false)}\n`); // Globally added lib matches required lib - assert.strictEqual(global.fs, require('fs')); + assert.strictEqual(globalThis.fs, require('fs')); test2(); } }; @@ -58,11 +58,11 @@ function test2() { // REPL response error message assert.strictEqual(data, '{}\n'); // Original value wasn't overwritten - assert.strictEqual(val, global.url); + assert.strictEqual(val, globalThis.url); } }; const val = {}; - global.url = val; + globalThis.url = val; common.allowGlobals(val); assert(!gotWrite); putIn.run(['url']); diff --git a/test/parallel/test-repl-underscore.js b/test/parallel/test-repl-underscore.js index 0f8103ce4573cd..8ce9de5563acfb 100644 --- a/test/parallel/test-repl-underscore.js +++ b/test/parallel/test-repl-underscore.js @@ -150,8 +150,8 @@ function testResetContextGlobal() { ]); // Delete globals leaked by REPL when `useGlobal` is `true` - delete global.module; - delete global.require; + delete globalThis.module; + delete globalThis.require; } function testError() { diff --git a/test/parallel/test-repl-use-global.js b/test/parallel/test-repl-use-global.js index 3457d0c5ba7210..06cda54f4d6fa2 100644 --- a/test/parallel/test-repl-use-global.js +++ b/test/parallel/test-repl-use-global.js @@ -20,10 +20,10 @@ const globalTest = (useGlobal, cb, output) => (err, repl) => { let str = ''; output.on('data', (data) => (str += data)); - global.lunch = 'tacos'; - repl.write('global.lunch;\n'); + globalThis.lunch = 'tacos'; + repl.write('globalThis.lunch;\n'); repl.close(); - delete global.lunch; + delete globalThis.lunch; cb(null, str.trim()); }; diff --git a/test/parallel/test-repl.js b/test/parallel/test-repl.js index d6c1cd6a9a7d6a..6c16d2a9679b21 100644 --- a/test/parallel/test-repl.js +++ b/test/parallel/test-repl.js @@ -36,7 +36,7 @@ const prompt_tcp = 'node via TCP socket> '; const moduleFilename = fixtures.path('a'); // Function for REPL to run -global.invoke_me = function(arg) { +globalThis.invoke_me = function(arg) { return `invoked ${arg}`; }; @@ -939,8 +939,8 @@ alternatively use dynamic import: const { default: alias, namedExport } = await socket.end(); } - common.allowGlobals(global.invoke_me, global.message, global.a, global.blah, - global.I, global.f, global.path, global.x, global.name, global.foo); + common.allowGlobals(globalThis.invoke_me, globalThis.message, globalThis.a, globalThis.blah, + globalThis.I, globalThis.f, globalThis.path, globalThis.x, globalThis.name, globalThis.foo); })().then(common.mustCall()); function startTCPRepl() { diff --git a/test/parallel/test-runner-mock-timers.js b/test/parallel/test-runner-mock-timers.js index 87b8ba7e3784d2..da7458b4c46dd3 100644 --- a/test/parallel/test-runner-mock-timers.js +++ b/test/parallel/test-runner-mock-timers.js @@ -69,7 +69,7 @@ describe('Mock Timers Test Suite', () => { 'clearImmediate', ]; - const globalTimersDescriptors = timers.map((fn) => getDescriptor(global, fn)); + const globalTimersDescriptors = timers.map((fn) => getDescriptor(globalThis, fn)); const nodeTimersDescriptors = timers.map((fn) => getDescriptor(nodeTimers, fn)); const nodeTimersPromisesDescriptors = timers .filter((fn) => !fn.includes('clear')) @@ -116,7 +116,7 @@ describe('Mock Timers Test Suite', () => { it('should reset all timers when calling .reset function', (t) => { t.mock.timers.enable(); const fn = t.mock.fn(); - global.setTimeout(fn, 1000); + globalThis.setTimeout(fn, 1000); t.mock.timers.reset(); assert.deepStrictEqual(Date.now, globalThis.Date.now); assert.throws(() => { @@ -131,7 +131,7 @@ describe('Mock Timers Test Suite', () => { it('should reset all timers when calling Symbol.dispose', (t) => { t.mock.timers.enable(); const fn = t.mock.fn(); - global.setTimeout(fn, 1000); + globalThis.setTimeout(fn, 1000); // TODO(benjamingr) refactor to `using` t.mock.timers[Symbol.dispose](); assert.throws(() => { @@ -148,8 +148,8 @@ describe('Mock Timers Test Suite', () => { const order = []; const fn1 = t.mock.fn(() => order.push('f1')); const fn2 = t.mock.fn(() => order.push('f2')); - global.setTimeout(fn1, 1000); - global.setTimeout(fn2, 1000); + globalThis.setTimeout(fn1, 1000); + globalThis.setTimeout(fn2, 1000); t.mock.timers.tick(1000); assert.strictEqual(fn1.mock.callCount(), 1); assert.strictEqual(fn2.mock.callCount(), 1); @@ -170,11 +170,11 @@ describe('Mock Timers Test Suite', () => { const intervalFn = t.mock.fn(); t.mock.timers.enable(); - global.setTimeout(timeoutFn, 1111); - const id = global.setInterval(intervalFn, 9999); + globalThis.setTimeout(timeoutFn, 1111); + const id = globalThis.setInterval(intervalFn, 9999); t.mock.timers.runAll(); - global.clearInterval(id); + globalThis.clearInterval(id); assert.strictEqual(timeoutFn.mock.callCount(), 1); assert.strictEqual(intervalFn.mock.callCount(), 1); }); @@ -184,11 +184,11 @@ describe('Mock Timers Test Suite', () => { const intervalFn = t.mock.fn(); t.mock.timers.enable(); - global.setTimeout(timeoutFn, 1111); - const id = global.setInterval(intervalFn, 9999); + globalThis.setTimeout(timeoutFn, 1111); + const id = globalThis.setInterval(intervalFn, 9999); t.mock.timers.runAll(); - global.clearInterval(id); + globalThis.clearInterval(id); assert.strictEqual(timeoutFn.mock.callCount(), 1); assert.strictEqual(intervalFn.mock.callCount(), 1); assert.strictEqual(Date.now(), 9999); @@ -209,7 +209,7 @@ describe('Mock Timers Test Suite', () => { const fn = mock.fn(); - global.setTimeout(fn, 4000); + globalThis.setTimeout(fn, 4000); mock.timers.tick(4000); assert.strictEqual(fn.mock.callCount(), 1); @@ -220,7 +220,7 @@ describe('Mock Timers Test Suite', () => { t.mock.timers.enable({ apis: ['setTimeout'] }); const fn = t.mock.fn(); - global.setTimeout(fn, 2000); + globalThis.setTimeout(fn, 2000); t.mock.timers.tick(1000); assert.strictEqual(fn.mock.callCount(), 0); @@ -234,7 +234,7 @@ describe('Mock Timers Test Suite', () => { t.mock.timers.enable({ apis: ['setTimeout'] }); const fn = t.mock.fn(); const args = ['a', 'b', 'c']; - global.setTimeout(fn, 2000, ...args); + globalThis.setTimeout(fn, 2000, ...args); t.mock.timers.tick(1000); t.mock.timers.tick(500); @@ -248,7 +248,7 @@ describe('Mock Timers Test Suite', () => { const now = Date.now(); const timeout = 2; const expected = () => now - timeout; - global.setTimeout(common.mustCall(() => { + globalThis.setTimeout(common.mustCall(() => { assert.strictEqual(now - timeout, expected()); done(); }), timeout); @@ -257,7 +257,7 @@ describe('Mock Timers Test Suite', () => { it('should change timeout to 1ms when it is > TIMEOUT_MAX', (t) => { t.mock.timers.enable({ apis: ['setTimeout'] }); const fn = t.mock.fn(); - global.setTimeout(fn, TIMEOUT_MAX + 1); + globalThis.setTimeout(fn, TIMEOUT_MAX + 1); t.mock.timers.tick(1); assert.strictEqual(fn.mock.callCount(), 1); }); @@ -265,7 +265,7 @@ describe('Mock Timers Test Suite', () => { it('should change the delay to one if timeout < 0', (t) => { t.mock.timers.enable({ apis: ['setTimeout'] }); const fn = t.mock.fn(); - global.setTimeout(fn, -1); + globalThis.setTimeout(fn, -1); t.mock.timers.tick(1); assert.strictEqual(fn.mock.callCount(), 1); }); @@ -277,8 +277,8 @@ describe('Mock Timers Test Suite', () => { const fn = mock.fn(); - const id = global.setTimeout(fn, 4000); - global.clearTimeout(id); + const id = globalThis.setTimeout(fn, 4000); + globalThis.clearTimeout(id); t.mock.timers.tick(4000); assert.strictEqual(fn.mock.callCount(), 0); @@ -297,13 +297,13 @@ describe('Mock Timers Test Suite', () => { t.mock.timers.enable({ apis: ['setInterval'] }); const fn = t.mock.fn(); - const id = global.setInterval(fn, 200); + const id = globalThis.setInterval(fn, 200); t.mock.timers.tick(200); t.mock.timers.tick(200); t.mock.timers.tick(200); - global.clearInterval(id); + globalThis.clearInterval(id); assert.strictEqual(fn.mock.callCount(), 3); }); @@ -312,13 +312,13 @@ describe('Mock Timers Test Suite', () => { t.mock.timers.enable({ apis: ['setInterval'] }); const fn = t.mock.fn(); const args = ['a', 'b', 'c']; - const id = global.setInterval(fn, 200, ...args); + const id = globalThis.setInterval(fn, 200, ...args); t.mock.timers.tick(200); t.mock.timers.tick(200); t.mock.timers.tick(200); - global.clearInterval(id); + globalThis.clearInterval(id); assert.strictEqual(fn.mock.callCount(), 3); assert.deepStrictEqual(fn.mock.calls[0].arguments, args); @@ -332,8 +332,8 @@ describe('Mock Timers Test Suite', () => { t.mock.timers.enable({ apis: ['setInterval'] }); const fn = mock.fn(); - const id = global.setInterval(fn, 200); - global.clearInterval(id); + const id = globalThis.setInterval(fn, 200); + globalThis.clearInterval(id); t.mock.timers.tick(200); assert.strictEqual(fn.mock.callCount(), 0); @@ -352,7 +352,7 @@ describe('Mock Timers Test Suite', () => { const now = Date.now(); const timeout = 2; const expected = () => now - timeout; - global.setImmediate(common.mustCall(() => { + globalThis.setImmediate(common.mustCall(() => { assert.strictEqual(now - timeout, expected()); done(); })); @@ -362,7 +362,7 @@ describe('Mock Timers Test Suite', () => { t.mock.timers.enable({ apis: ['setImmediate'] }); const fn = t.mock.fn(); const args = ['a', 'b', 'c']; - global.setImmediate(fn, ...args); + globalThis.setImmediate(fn, ...args); t.mock.timers.tick(9999); assert.strictEqual(fn.mock.callCount(), 1); @@ -372,14 +372,14 @@ describe('Mock Timers Test Suite', () => { it('should not advance in time if clearImmediate was invoked', (t) => { t.mock.timers.enable({ apis: ['setImmediate'] }); - const id = global.setImmediate(common.mustNotCall()); - global.clearImmediate(id); + const id = globalThis.setImmediate(common.mustNotCall()); + globalThis.clearImmediate(id); t.mock.timers.tick(200); }); it('should advance in time and trigger timers when calling the .tick function', (t) => { t.mock.timers.enable({ apis: ['setImmediate'] }); - global.setImmediate(common.mustCall(1)); + globalThis.setImmediate(common.mustCall(1)); t.mock.timers.tick(0); }); @@ -389,8 +389,8 @@ describe('Mock Timers Test Suite', () => { const fn1 = t.mock.fn(common.mustCall(() => order.push('f1'), 1)); const fn2 = t.mock.fn(common.mustCall(() => order.push('f2'), 1)); - global.setImmediate(fn1); - global.setImmediate(fn2); + globalThis.setImmediate(fn1); + globalThis.setImmediate(fn2); t.mock.timers.tick(0); @@ -403,8 +403,8 @@ describe('Mock Timers Test Suite', () => { const fn1 = t.mock.fn(common.mustCall(() => order.push('f1'), 1)); const fn2 = t.mock.fn(common.mustCall(() => order.push('f2'), 1)); - global.setTimeout(fn2, 0); - global.setImmediate(fn1); + globalThis.setTimeout(fn2, 0); + globalThis.setImmediate(fn1); t.mock.timers.tick(100); diff --git a/test/parallel/test-timers-api-refs.js b/test/parallel/test-timers-api-refs.js index 3c55a05ac4c20a..a6a541963110bb 100644 --- a/test/parallel/test-timers-api-refs.js +++ b/test/parallel/test-timers-api-refs.js @@ -4,12 +4,12 @@ const timers = require('timers'); // Delete global APIs to make sure they're not relied on by the internal timers // code -delete global.setTimeout; -delete global.clearTimeout; -delete global.setInterval; -delete global.clearInterval; -delete global.setImmediate; -delete global.clearImmediate; +delete globalThis.setTimeout; +delete globalThis.clearTimeout; +delete globalThis.setInterval; +delete globalThis.clearInterval; +delete globalThis.setImmediate; +delete globalThis.clearImmediate; const timeoutCallback = () => { timers.clearTimeout(timeout); }; const timeout = timers.setTimeout(common.mustCall(timeoutCallback), 1); diff --git a/test/parallel/test-timers-process-tampering.js b/test/parallel/test-timers-process-tampering.js index 766cc9f3560c82..8632e7c96fa086 100644 --- a/test/parallel/test-timers-process-tampering.js +++ b/test/parallel/test-timers-process-tampering.js @@ -3,6 +3,6 @@ 'use strict'; const common = require('../common'); -global.process = {}; // Boom! -common.allowGlobals(global.process); +globalThis.process = {}; // Boom! +common.allowGlobals(globalThis.process); setImmediate(common.mustCall()); diff --git a/test/parallel/test-tls-connect-memleak.js b/test/parallel/test-tls-connect-memleak.js index 5bdcbe89f785f6..7b9cb71d8df0ba 100644 --- a/test/parallel/test-tls-connect-memleak.js +++ b/test/parallel/test-tls-connect-memleak.js @@ -57,7 +57,7 @@ const gcListener = { ongc() { collected = true; } }; } function done(sock) { - global.gc(); + globalThis.gc(); setImmediate(() => { assert.strictEqual(collected, true); sock.end(); diff --git a/test/parallel/test-tls-securepair-leak.js b/test/parallel/test-tls-securepair-leak.js index 98bdcde76ec034..e3d5c2cdf37b5d 100644 --- a/test/parallel/test-tls-securepair-leak.js +++ b/test/parallel/test-tls-securepair-leak.js @@ -17,7 +17,7 @@ const before = process.memoryUsage().external; createSecurePair(context, false, false, false, options).destroy(); } setImmediate(() => { - global.gc(); + globalThis.gc(); const after = process.memoryUsage().external; // It's not an exact science but a SecurePair grows .external by about 45 KiB. diff --git a/test/parallel/test-tls-transport-destroy-after-own-gc.js b/test/parallel/test-tls-transport-destroy-after-own-gc.js index 17c494ca0b79d1..bcac2c6ebde2b8 100644 --- a/test/parallel/test-tls-transport-destroy-after-own-gc.js +++ b/test/parallel/test-tls-transport-destroy-after-own-gc.js @@ -19,11 +19,11 @@ let clientTLSHandle = clientTLS._handle; // eslint-disable-line no-unused-vars setImmediate(() => { clientTLS = null; - global.gc(); + globalThis.gc(); clientTLSHandle = null; - global.gc(); + globalThis.gc(); setImmediate(() => { clientSide = null; - global.gc(); + globalThis.gc(); }); }); diff --git a/test/parallel/test-trace-events-api.js b/test/parallel/test-trace-events-api.js index 8792a40cf00c80..9bffb3b78c4ba3 100644 --- a/test/parallel/test-trace-events-api.js +++ b/test/parallel/test-trace-events-api.js @@ -109,7 +109,7 @@ if (isChild) { assert.strictEqual(getEnabledCategories(), 'abc'); tracing3 = undefined; } - global.gc(); + globalThis.gc(); assert.strictEqual(getEnabledCategories(), 'abc'); // Not able to disable the thing after this point, however. } diff --git a/test/parallel/test-util-format.js b/test/parallel/test-util-format.js index 8d2cab5a9c7a1c..6f222d0fea0fb8 100644 --- a/test/parallel/test-util-format.js +++ b/test/parallel/test-util-format.js @@ -197,9 +197,9 @@ assert.strictEqual(util.format('%s', -Infinity), '-Infinity'); util.format('%s', Object.setPrototypeOf(new Foo(), null)), '[Foo: null prototype] {}' ); - global.Foo = Foo; + globalThis.Foo = Foo; assert.strictEqual(util.format('%s', new Foo()), 'Bar'); - delete global.Foo; + delete globalThis.Foo; class Bar { abc = true; } assert.strictEqual(util.format('%s', new Bar()), 'Bar { abc: true }'); class Foobar extends Array { aaa = true; } diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index 87d92369b8deca..2dc263443481a0 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -1267,9 +1267,9 @@ if (typeof Symbol !== 'undefined') { // a bonafide native Promise. { const oldPromise = Promise; - global.Promise = function() { this.bar = 42; }; + globalThis.Promise = function() { this.bar = 42; }; assert.strictEqual(util.inspect(new Promise()), '{ bar: 42 }'); - global.Promise = oldPromise; + globalThis.Promise = oldPromise; } // Test Map iterators. @@ -3183,7 +3183,7 @@ assert.strictEqual( } // Consistency check. - assert(fullObjectGraph(global).has(Function.prototype)); + assert(fullObjectGraph(globalThis).has(Function.prototype)); } { diff --git a/test/parallel/test-vm-basic.js b/test/parallel/test-vm-basic.js index 93c3fbaea631ab..5687987faeb0b9 100644 --- a/test/parallel/test-vm-basic.js +++ b/test/parallel/test-vm-basic.js @@ -59,9 +59,9 @@ const vm = require('vm'); const result = vm.runInThisContext( 'vmResult = "foo"; Object.prototype.toString.call(process);' ); - assert.strictEqual(global.vmResult, 'foo'); + assert.strictEqual(globalThis.vmResult, 'foo'); assert.strictEqual(result, '[object process]'); - delete global.vmResult; + delete globalThis.vmResult; } // vm.runInNewContext @@ -69,7 +69,7 @@ const vm = require('vm'); const result = vm.runInNewContext( 'vmResult = "foo"; typeof process;' ); - assert.strictEqual(global.vmResult, undefined); + assert.strictEqual(globalThis.vmResult, undefined); assert.strictEqual(result, 'undefined'); } diff --git a/test/parallel/test-vm-create-and-run-in-context.js b/test/parallel/test-vm-create-and-run-in-context.js index bd746cf2df7080..314ab9525743dc 100644 --- a/test/parallel/test-vm-create-and-run-in-context.js +++ b/test/parallel/test-vm-create-and-run-in-context.js @@ -45,6 +45,6 @@ assert.strictEqual(context.thing, 'lala'); // Run in contextified sandbox without referencing the context const sandbox = { x: 1 }; vm.createContext(sandbox); -global.gc(); +globalThis.gc(); vm.runInContext('x = 2', sandbox); // Should not crash. diff --git a/test/parallel/test-vm-cross-context.js b/test/parallel/test-vm-cross-context.js index b7cf1309d3689f..abdfde32a8d847 100644 --- a/test/parallel/test-vm-cross-context.js +++ b/test/parallel/test-vm-cross-context.js @@ -23,7 +23,7 @@ require('../common'); const vm = require('vm'); -const ctx = vm.createContext(global); +const ctx = vm.createContext(globalThis); // Should not throw. vm.runInContext('!function() { var x = console.log; }()', ctx); diff --git a/test/parallel/test-vm-global-get-own.js b/test/parallel/test-vm-global-get-own.js index 246fcbf866b8b6..de5e0a9619af65 100644 --- a/test/parallel/test-vm-global-get-own.js +++ b/test/parallel/test-vm-global-get-own.js @@ -9,7 +9,7 @@ const vm = require('vm'); // Related to: // - https://github.com/nodejs/node/issues/45983 -const global = vm.runInContext('this', vm.createContext()); +const contextGlobal = vm.runInContext('this', vm.createContext()); function runAssertions(data, property, viaDefine, value1, value2, value3) { // Define the property for the first time @@ -35,20 +35,20 @@ function runAssertionsOnSandbox(builder) { } // Assertions on: define property -runAssertions(global, 'toto', true, 1, 2, 3); -runAssertions(global, Symbol.for('toto'), true, 1, 2, 3); -runAssertions(global, 'tutu', true, fun1, fun2, fun3); -runAssertions(global, Symbol.for('tutu'), true, fun1, fun2, fun3); -runAssertions(global, 'tyty', true, fun1, 2, 3); -runAssertions(global, Symbol.for('tyty'), true, fun1, 2, 3); +runAssertions(contextGlobal, 'toto', true, 1, 2, 3); +runAssertions(contextGlobal, Symbol.for('toto'), true, 1, 2, 3); +runAssertions(contextGlobal, 'tutu', true, fun1, fun2, fun3); +runAssertions(contextGlobal, Symbol.for('tutu'), true, fun1, fun2, fun3); +runAssertions(contextGlobal, 'tyty', true, fun1, 2, 3); +runAssertions(contextGlobal, Symbol.for('tyty'), true, fun1, 2, 3); // Assertions on: direct assignment -runAssertions(global, 'titi', false, 1, 2, 3); -runAssertions(global, Symbol.for('titi'), false, 1, 2, 3); -runAssertions(global, 'tata', false, fun1, fun2, fun3); -runAssertions(global, Symbol.for('tata'), false, fun1, fun2, fun3); -runAssertions(global, 'tztz', false, fun1, 2, 3); -runAssertions(global, Symbol.for('tztz'), false, fun1, 2, 3); +runAssertions(contextGlobal, 'titi', false, 1, 2, 3); +runAssertions(contextGlobal, Symbol.for('titi'), false, 1, 2, 3); +runAssertions(contextGlobal, 'tata', false, fun1, fun2, fun3); +runAssertions(contextGlobal, Symbol.for('tata'), false, fun1, fun2, fun3); +runAssertions(contextGlobal, 'tztz', false, fun1, 2, 3); +runAssertions(contextGlobal, Symbol.for('tztz'), false, fun1, 2, 3); // Assertions on: define property from sandbox runAssertionsOnSandbox( diff --git a/test/parallel/test-vm-measure-memory-lazy.js b/test/parallel/test-vm-measure-memory-lazy.js index 513cfbc3672451..7f85f8d6ca9656 100644 --- a/test/parallel/test-vm-measure-memory-lazy.js +++ b/test/parallel/test-vm-measure-memory-lazy.js @@ -10,28 +10,28 @@ const vm = require('vm'); expectExperimentalWarning(); -// Test lazy memory measurement - we will need to global.gc() +// Test lazy memory measurement - we will need to globalThis.gc() // or otherwise these may not resolve. { vm.measureMemory() .then(common.mustCall(assertSummaryShape)); - global.gc(); + globalThis.gc(); } { vm.measureMemory({}) .then(common.mustCall(assertSummaryShape)); - global.gc(); + globalThis.gc(); } { vm.measureMemory({ mode: 'summary' }) .then(common.mustCall(assertSummaryShape)); - global.gc(); + globalThis.gc(); } { vm.measureMemory({ mode: 'detailed' }) .then(common.mustCall(assertSummaryShape)); - global.gc(); + globalThis.gc(); } diff --git a/test/parallel/test-vm-module-basic.js b/test/parallel/test-vm-module-basic.js index cba1e037ac455a..53fed6536079a0 100644 --- a/test/parallel/test-vm-module-basic.js +++ b/test/parallel/test-vm-module-basic.js @@ -37,15 +37,15 @@ const util = require('util'); (async () => { const m = new SourceTextModule(` - global.vmResultFoo = "foo"; - global.vmResultTypeofProcess = Object.prototype.toString.call(process); + globalThis.vmResultFoo = "foo"; + globalThis.vmResultTypeofProcess = Object.prototype.toString.call(process); `); await m.link(common.mustNotCall()); await m.evaluate(); - assert.strictEqual(global.vmResultFoo, 'foo'); - assert.strictEqual(global.vmResultTypeofProcess, '[object process]'); - delete global.vmResultFoo; - delete global.vmResultTypeofProcess; + assert.strictEqual(globalThis.vmResultFoo, 'foo'); + assert.strictEqual(globalThis.vmResultTypeofProcess, '[object process]'); + delete globalThis.vmResultFoo; + delete globalThis.vmResultTypeofProcess; })().then(common.mustCall()); (async () => { diff --git a/test/parallel/test-vm-new-script-new-context.js b/test/parallel/test-vm-new-script-new-context.js index 482b4130d615d9..b4221d81d98dcb 100644 --- a/test/parallel/test-vm-new-script-new-context.js +++ b/test/parallel/test-vm-new-script-new-context.js @@ -49,43 +49,43 @@ const Script = require('vm').Script; } { - global.hello = 5; + globalThis.hello = 5; const script = new Script('hello = 2'); script.runInNewContext(); - assert.strictEqual(global.hello, 5); + assert.strictEqual(globalThis.hello, 5); // Cleanup - delete global.hello; + delete globalThis.hello; } { - global.code = 'foo = 1;' + + globalThis.code = 'foo = 1;' + 'bar = 2;' + 'if (baz !== 3) throw new Error(\'test fail\');'; - global.foo = 2; - global.obj = { foo: 0, baz: 3 }; - const script = new Script(global.code); + globalThis.foo = 2; + globalThis.obj = { foo: 0, baz: 3 }; + const script = new Script(globalThis.code); /* eslint-disable no-unused-vars */ - const baz = script.runInNewContext(global.obj); + const baz = script.runInNewContext(globalThis.obj); /* eslint-enable no-unused-vars */ - assert.strictEqual(global.obj.foo, 1); - assert.strictEqual(global.obj.bar, 2); - assert.strictEqual(global.foo, 2); + assert.strictEqual(globalThis.obj.foo, 1); + assert.strictEqual(globalThis.obj.bar, 2); + assert.strictEqual(globalThis.foo, 2); // cleanup - delete global.code; - delete global.foo; - delete global.obj; + delete globalThis.code; + delete globalThis.foo; + delete globalThis.obj; } { const script = new Script('f()'); - function changeFoo() { global.foo = 100; } + function changeFoo() { globalThis.foo = 100; } script.runInNewContext({ f: changeFoo }); - assert.strictEqual(global.foo, 100); + assert.strictEqual(globalThis.foo, 100); // cleanup - delete global.foo; + delete globalThis.foo; } { diff --git a/test/parallel/test-vm-new-script-this-context.js b/test/parallel/test-vm-new-script-this-context.js index 18f39f9086ae2a..30b220e3d4a2c2 100644 --- a/test/parallel/test-vm-new-script-this-context.js +++ b/test/parallel/test-vm-new-script-this-context.js @@ -35,34 +35,34 @@ assert.throws(() => { script.runInThisContext(script); }, /^Error: test$/); -global.hello = 5; +globalThis.hello = 5; script = new Script('hello = 2'); script.runInThisContext(script); -assert.strictEqual(global.hello, 2); +assert.strictEqual(globalThis.hello, 2); // Pass values -global.code = 'foo = 1;' + +globalThis.code = 'foo = 1;' + 'bar = 2;' + 'if (typeof baz !== "undefined") throw new Error("test fail");'; -global.foo = 2; -global.obj = { foo: 0, baz: 3 }; -script = new Script(global.code); +globalThis.foo = 2; +globalThis.obj = { foo: 0, baz: 3 }; +script = new Script(globalThis.code); script.runInThisContext(script); -assert.strictEqual(global.obj.foo, 0); -assert.strictEqual(global.bar, 2); -assert.strictEqual(global.foo, 1); +assert.strictEqual(globalThis.obj.foo, 0); +assert.strictEqual(globalThis.bar, 2); +assert.strictEqual(globalThis.foo, 1); // Call a function -global.f = function() { global.foo = 100; }; +globalThis.f = function() { globalThis.foo = 100; }; script = new Script('f()'); script.runInThisContext(script); -assert.strictEqual(global.foo, 100); +assert.strictEqual(globalThis.foo, 100); common.allowGlobals( - global.hello, - global.code, - global.foo, - global.obj, - global.f + globalThis.hello, + globalThis.code, + globalThis.foo, + globalThis.obj, + globalThis.f ); diff --git a/test/parallel/test-vm-run-in-new-context.js b/test/parallel/test-vm-run-in-new-context.js index 6e8c42812bbc88..c6f8fbf893ca9a 100644 --- a/test/parallel/test-vm-run-in-new-context.js +++ b/test/parallel/test-vm-run-in-new-context.js @@ -26,7 +26,7 @@ const common = require('../common'); const assert = require('assert'); const vm = require('vm'); -if (typeof global.gc !== 'function') +if (typeof globalThis.gc !== 'function') assert.fail('Run this test with --expose-gc'); // Run a string @@ -38,28 +38,28 @@ assert.throws(() => { vm.runInNewContext('throw new Error(\'test\');'); }, /^Error: test$/); -global.hello = 5; +globalThis.hello = 5; vm.runInNewContext('hello = 2'); -assert.strictEqual(global.hello, 5); +assert.strictEqual(globalThis.hello, 5); // Pass values in and out -global.code = 'foo = 1;' + +globalThis.code = 'foo = 1;' + 'bar = 2;' + 'if (baz !== 3) throw new Error(\'test fail\');'; -global.foo = 2; -global.obj = { foo: 0, baz: 3 }; +globalThis.foo = 2; +globalThis.obj = { foo: 0, baz: 3 }; /* eslint-disable no-unused-vars */ -const baz = vm.runInNewContext(global.code, global.obj); +const baz = vm.runInNewContext(globalThis.code, globalThis.obj); /* eslint-enable no-unused-vars */ -assert.strictEqual(global.obj.foo, 1); -assert.strictEqual(global.obj.bar, 2); -assert.strictEqual(global.foo, 2); +assert.strictEqual(globalThis.obj.foo, 1); +assert.strictEqual(globalThis.obj.bar, 2); +assert.strictEqual(globalThis.foo, 2); // Call a function by reference -function changeFoo() { global.foo = 100; } +function changeFoo() { globalThis.foo = 100; } vm.runInNewContext('f()', { f: changeFoo }); -assert.strictEqual(global.foo, 100); +assert.strictEqual(globalThis.foo, 100); // Modify an object by reference const f = { a: 1 }; @@ -68,7 +68,7 @@ assert.strictEqual(f.a, 2); // Use function in context without referencing context const fn = vm.runInNewContext('(function() { obj.p = {}; })', { obj: {} }); -global.gc(); +globalThis.gc(); fn(); // Should not crash @@ -93,8 +93,8 @@ for (const arg of [filename, { filename }]) { } common.allowGlobals( - global.hello, - global.code, - global.foo, - global.obj + globalThis.hello, + globalThis.code, + globalThis.foo, + globalThis.obj ); diff --git a/test/parallel/test-vm-static-this.js b/test/parallel/test-vm-static-this.js index e9382d6c3b4c1a..f47c0b5d0d056a 100644 --- a/test/parallel/test-vm-static-this.js +++ b/test/parallel/test-vm-static-this.js @@ -33,9 +33,9 @@ assert.throws(function() { vm.runInThisContext('throw new Error(\'test\');'); }, /test/); -global.hello = 5; +globalThis.hello = 5; vm.runInThisContext('hello = 2'); -assert.strictEqual(global.hello, 2); +assert.strictEqual(globalThis.hello, 2); // pass values @@ -43,23 +43,23 @@ const code = 'foo = 1;' + 'bar = 2;' + 'if (typeof baz !== \'undefined\')' + 'throw new Error(\'test fail\');'; -global.foo = 2; -global.obj = { foo: 0, baz: 3 }; +globalThis.foo = 2; +globalThis.obj = { foo: 0, baz: 3 }; /* eslint-disable no-unused-vars */ const baz = vm.runInThisContext(code); /* eslint-enable no-unused-vars */ -assert.strictEqual(global.obj.foo, 0); -assert.strictEqual(global.bar, 2); -assert.strictEqual(global.foo, 1); +assert.strictEqual(globalThis.obj.foo, 0); +assert.strictEqual(globalThis.bar, 2); +assert.strictEqual(globalThis.foo, 1); // call a function -global.f = function() { global.foo = 100; }; +globalThis.f = function() { globalThis.foo = 100; }; vm.runInThisContext('f()'); -assert.strictEqual(global.foo, 100); +assert.strictEqual(globalThis.foo, 100); common.allowGlobals( - global.hello, - global.foo, - global.obj, - global.f + globalThis.hello, + globalThis.foo, + globalThis.obj, + globalThis.f ); diff --git a/test/parallel/test-webstorage.js b/test/parallel/test-webstorage.js index 4da6b67bd2932b..7f9fe8dfa53391 100644 --- a/test/parallel/test-webstorage.js +++ b/test/parallel/test-webstorage.js @@ -69,7 +69,7 @@ test('sessionStorage is not persisted', async () => { test('localStorage throws without --localstorage-file ', async () => { const cp = await spawnPromisified(process.execPath, [ '--experimental-webstorage', - '-pe', 'localStorage === global.localStorage', + '-pe', 'localStorage === globalThis.localStorage', ]); assert.strictEqual(cp.code, 1); assert.strictEqual(cp.signal, null); @@ -81,7 +81,7 @@ test('localStorage is not persisted if it is unused', async () => { const cp = await spawnPromisified(process.execPath, [ '--experimental-webstorage', '--localstorage-file', nextLocalStorage(), - '-pe', 'localStorage === global.localStorage', + '-pe', 'localStorage === globalThis.localStorage', ]); assert.strictEqual(cp.code, 0); assert.match(cp.stdout, /true/); diff --git a/test/parallel/test-whatwg-url-custom-global.js b/test/parallel/test-whatwg-url-custom-global.js index b99dfd8f3e7d94..16efdfa8df1174 100644 --- a/test/parallel/test-whatwg-url-custom-global.js +++ b/test/parallel/test-whatwg-url-custom-global.js @@ -6,7 +6,7 @@ require('../common'); const assert = require('assert'); assert.deepStrictEqual( - Object.getOwnPropertyDescriptor(global, 'URL'), + Object.getOwnPropertyDescriptor(globalThis, 'URL'), { value: URL, writable: true, @@ -16,7 +16,7 @@ assert.deepStrictEqual( ); assert.deepStrictEqual( - Object.getOwnPropertyDescriptor(global, 'URLSearchParams'), + Object.getOwnPropertyDescriptor(globalThis, 'URLSearchParams'), { value: URLSearchParams, writable: true, diff --git a/test/parallel/test-worker-cli-options.js b/test/parallel/test-worker-cli-options.js index 0c243d251e97bc..3e6ab46db6ea74 100644 --- a/test/parallel/test-worker-cli-options.js +++ b/test/parallel/test-worker-cli-options.js @@ -8,7 +8,7 @@ const CODE = ` // If the --expose-internals flag does not pass to worker // require function will throw an error require('internal/options'); -global.gc(); +globalThis.gc(); `; // Test if the flags is passed to worker threads correctly diff --git a/test/parallel/test-worker-message-channel-sharedarraybuffer.js b/test/parallel/test-worker-message-channel-sharedarraybuffer.js index 220aa978b12051..6ee577d447ec97 100644 --- a/test/parallel/test-worker-message-channel-sharedarraybuffer.js +++ b/test/parallel/test-worker-message-channel-sharedarraybuffer.js @@ -19,7 +19,7 @@ const { Worker } = require('worker_threads'); `, { eval: true }); w.on('message', common.mustCall(() => { assert.strictEqual(local.toString(), 'Hello world!'); - global.gc(); + globalThis.gc(); w.terminate(); })); w.postMessage({ sharedArrayBuffer }); diff --git a/test/parallel/test-worker-message-port-move.js b/test/parallel/test-worker-message-port-move.js index 44efd2e6a6b94f..b8db31b88c7bc4 100644 --- a/test/parallel/test-worker-message-port-move.js +++ b/test/parallel/test-worker-message-port-move.js @@ -48,7 +48,7 @@ vm.runInContext('(' + function() { { let threw = false; try { - port.postMessage(global); + port.postMessage(globalThis); } catch (e) { assert.strictEqual(e.constructor.name, 'DOMException'); assert(e instanceof Object); diff --git a/test/parallel/test-worker-workerdata-sharedarraybuffer.js b/test/parallel/test-worker-workerdata-sharedarraybuffer.js index 4e3d508ac94941..4f1b332461280f 100644 --- a/test/parallel/test-worker-workerdata-sharedarraybuffer.js +++ b/test/parallel/test-worker-workerdata-sharedarraybuffer.js @@ -23,7 +23,7 @@ const { Worker } = require('worker_threads'); }); w.on('message', common.mustCall(() => { assert.strictEqual(local.toString(), 'Hello world!'); - global.gc(); + globalThis.gc(); w.terminate(); })); w.postMessage({}); diff --git a/test/parallel/test-zlib-invalid-input-memory.js b/test/parallel/test-zlib-invalid-input-memory.js index 9761e4bbf097d8..ac718395dae184 100644 --- a/test/parallel/test-zlib-invalid-input-memory.js +++ b/test/parallel/test-zlib-invalid-input-memory.js @@ -17,7 +17,7 @@ const ongc = common.mustCall(); strm.once('error', common.mustCall((err) => { assert(err); setImmediate(() => { - global.gc(); + globalThis.gc(); // Keep the event loop alive for seeing the async_hooks destroy hook // we use for GC tracking... // TODO(addaleax): This should maybe not be necessary? diff --git a/test/parallel/test-zlib-unused-weak.js b/test/parallel/test-zlib-unused-weak.js index 2c1e2d729030dd..cd1ab91ceb5c4b 100644 --- a/test/parallel/test-zlib-unused-weak.js +++ b/test/parallel/test-zlib-unused-weak.js @@ -6,12 +6,12 @@ const zlib = require('zlib'); // Tests that native zlib handles start out their life as weak handles. -global.gc(); +globalThis.gc(); const before = process.memoryUsage().external; for (let i = 0; i < 100; ++i) zlib.createGzip(); const afterCreation = process.memoryUsage().external; -global.gc(); +globalThis.gc(); const afterGC = process.memoryUsage().external; assert((afterGC - before) / (afterCreation - before) <= 0.05, From 687be594bb45a5baed588067c3d7f1159f1ea62f Mon Sep 17 00:00:00 2001 From: yamachu Date: Wed, 22 Jan 2025 20:21:46 +0900 Subject: [PATCH 14/38] test: add test that uses multibyte for path and resolves modules PR-URL: https://github.com/nodejs/node/pull/56696 Fixes: https://github.com/nodejs/node/issues/56650 Refs: https://github.com/nodejs/node/pull/56657 Reviewed-By: James M Snell Reviewed-By: Yagiz Nizipli Reviewed-By: Luigi Pinca --- .../experimental.json" | 3 +++ .../test-module-create-require-multibyte.js | 24 +++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 "test/fixtures/copy/utf/\346\226\260\345\273\272\346\226\207\344\273\266\345\244\271/experimental.json" create mode 100644 test/parallel/test-module-create-require-multibyte.js diff --git "a/test/fixtures/copy/utf/\346\226\260\345\273\272\346\226\207\344\273\266\345\244\271/experimental.json" "b/test/fixtures/copy/utf/\346\226\260\345\273\272\346\226\207\344\273\266\345\244\271/experimental.json" new file mode 100644 index 00000000000000..12611d2385a5a5 --- /dev/null +++ "b/test/fixtures/copy/utf/\346\226\260\345\273\272\346\226\207\344\273\266\345\244\271/experimental.json" @@ -0,0 +1,3 @@ +{ + "ofLife": 42 +} diff --git a/test/parallel/test-module-create-require-multibyte.js b/test/parallel/test-module-create-require-multibyte.js new file mode 100644 index 00000000000000..f9c4b6345dc59e --- /dev/null +++ b/test/parallel/test-module-create-require-multibyte.js @@ -0,0 +1,24 @@ +'use strict'; + +require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); + +// This test ensures that the module can be resolved +// even if the path given to createRequire contains multibyte characters. + +const { createRequire } = require('module'); + +{ + const u = fixtures.fileURL('あ.js'); + + const reqToo = createRequire(u); + assert.deepStrictEqual(reqToo('./experimental'), { ofLife: 42 }); +} + +{ + const u = fixtures.fileURL('copy/utf/新建文件夹/index.js'); + + const reqToo = createRequire(u); + assert.deepStrictEqual(reqToo('./experimental'), { ofLife: 42 }); +} From 176002400886bc7f438236c727a61831efd5e565 Mon Sep 17 00:00:00 2001 From: yamachu Date: Wed, 22 Jan 2025 20:24:38 +0900 Subject: [PATCH 15/38] src: fix to generate path from wchar_t via wstring Take a similar approach to node_file and allow the creation of paths code point must be specified to convert from wchar_t to utf8. PR-URL: https://github.com/nodejs/node/pull/56696 Fixes: https://github.com/nodejs/node/issues/56650 Refs: https://github.com/nodejs/node/pull/56657 Reviewed-By: James M Snell Reviewed-By: Yagiz Nizipli Reviewed-By: Luigi Pinca --- src/node_file.cc | 15 +-------------- src/node_modules.cc | 24 ++++++++++++++++++++---- src/util-inl.h | 16 ++++++++++++++++ 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/src/node_file.cc b/src/node_file.cc index 984bc55ee9b941..8e29bb39887625 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -3146,21 +3146,8 @@ static void GetFormatOfExtensionlessFile( } #ifdef _WIN32 -std::wstring ConvertToWideString(const std::string& str) { - int size_needed = MultiByteToWideChar( - CP_UTF8, 0, &str[0], static_cast(str.size()), nullptr, 0); - std::wstring wstrTo(size_needed, 0); - MultiByteToWideChar(CP_UTF8, - 0, - &str[0], - static_cast(str.size()), - &wstrTo[0], - size_needed); - return wstrTo; -} - #define BufferValueToPath(str) \ - std::filesystem::path(ConvertToWideString(str.ToString())) + std::filesystem::path(ConvertToWideString(str.ToString(), CP_UTF8)) std::string ConvertWideToUTF8(const std::wstring& wstr) { if (wstr.empty()) return std::string(); diff --git a/src/node_modules.cc b/src/node_modules.cc index 85c8e21cf026ff..38d2c65c7f3282 100644 --- a/src/node_modules.cc +++ b/src/node_modules.cc @@ -349,8 +349,16 @@ void BindingData::GetNearestParentPackageJSON( path_value_str.push_back(kPathSeparator); } - auto package_json = - TraverseParent(realm, std::filesystem::path(path_value_str)); + std::filesystem::path path; + +#ifdef _WIN32 + std::wstring wide_path = ConvertToWideString(path_value_str, GetACP()); + path = std::filesystem::path(wide_path); +#else + path = std::filesystem::path(path_value_str); +#endif + + auto package_json = TraverseParent(realm, path); if (package_json != nullptr) { args.GetReturnValue().Set(package_json->Serialize(realm)); @@ -375,8 +383,16 @@ void BindingData::GetNearestParentPackageJSONType( path_value_str.push_back(kPathSeparator); } - auto package_json = - TraverseParent(realm, std::filesystem::path(path_value_str)); + std::filesystem::path path; + +#ifdef _WIN32 + std::wstring wide_path = ConvertToWideString(path_value_str, GetACP()); + path = std::filesystem::path(wide_path); +#else + path = std::filesystem::path(path_value_str); +#endif + + auto package_json = TraverseParent(realm, path); if (package_json == nullptr) { return; diff --git a/src/util-inl.h b/src/util-inl.h index a35e15eeed6576..b5ae5950b62767 100644 --- a/src/util-inl.h +++ b/src/util-inl.h @@ -562,6 +562,22 @@ bool IsWindowsBatchFile(const char* filename) { #endif // _WIN32 } +#ifdef _WIN32 +inline std::wstring ConvertToWideString(const std::string& str, + UINT code_page) { + int size_needed = MultiByteToWideChar( + code_page, 0, &str[0], static_cast(str.size()), nullptr, 0); + std::wstring wstrTo(size_needed, 0); + MultiByteToWideChar(code_page, + 0, + &str[0], + static_cast(str.size()), + &wstrTo[0], + size_needed); + return wstrTo; +} +#endif // _WIN32 + } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS From bade7a1866618b9e46358b839fe5fdf16b1db2be Mon Sep 17 00:00:00 2001 From: tjuhaszrh Date: Sat, 25 Jan 2025 10:34:54 +0100 Subject: [PATCH 16/38] src: fix build with GCC 15 Added cstdint to worker_inspector as on more recent version of gcc the build was failing due to changes to libstdc++ and the removal of transitive includes. PR-URL: https://github.com/nodejs/node/pull/56740 Fixes: https://github.com/nodejs/node/issues/56731 Reviewed-By: Antoine du Hamel Reviewed-By: Chengzhong Wu Reviewed-By: Richard Lau Reviewed-By: James M Snell --- src/inspector/worker_inspector.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/inspector/worker_inspector.h b/src/inspector/worker_inspector.h index d3254d5aa0ebe4..24403bb1704c40 100644 --- a/src/inspector/worker_inspector.h +++ b/src/inspector/worker_inspector.h @@ -5,6 +5,7 @@ #error("This header can only be used when inspector is enabled") #endif +#include #include #include #include From da1ca7db756c0db8a894f3b22a4d067701dc3c04 Mon Sep 17 00:00:00 2001 From: Robin Mehner Date: Sat, 25 Jan 2025 12:02:31 +0100 Subject: [PATCH 17/38] doc: fix typo in example code for util.styleText MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Code shows how to style `errorMessage`, but then only logs out `successMessage` twice. This might trip people up when copying the code. PR-URL: https://github.com/nodejs/node/pull/56720 Reviewed-By: Ulises Gascón Reviewed-By: Richard Lau Reviewed-By: James M Snell Reviewed-By: Luigi Pinca Reviewed-By: Minwoo Jung --- doc/api/util.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/util.md b/doc/api/util.md index 72a45b1cde8d11..958a49977e1a17 100644 --- a/doc/api/util.md +++ b/doc/api/util.md @@ -1972,7 +1972,7 @@ const errorMessage = styleText( // Validate if process.stderr has TTY { stream: stderr }, ); -console.error(successMessage); +console.error(errorMessage); ``` ```cjs From db5a2b55a59c2775f3b88f36080e51ca8069f9bd Mon Sep 17 00:00:00 2001 From: Burkov Egor Date: Wed, 22 Jan 2025 16:27:26 +0300 Subject: [PATCH 18/38] src: add default value for RSACipherConfig mode field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using default init of enum is UB Refs: https://github.com/nodejs/node/issues/56693 PR-URL: https://github.com/nodejs/node/pull/56701 Reviewed-By: Juan José Arboleda Reviewed-By: Yagiz Nizipli Reviewed-By: James M Snell --- src/crypto/crypto_rsa.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crypto/crypto_rsa.h b/src/crypto/crypto_rsa.h index 29b259ae2f5284..6fc48da1aea2cd 100644 --- a/src/crypto/crypto_rsa.h +++ b/src/crypto/crypto_rsa.h @@ -77,7 +77,7 @@ struct RSAKeyExportTraits final { using RSAKeyExportJob = KeyExportJob; struct RSACipherConfig final : public MemoryRetainer { - CryptoJobMode mode; + CryptoJobMode mode = kCryptoJobAsync; ByteSource label; int padding = 0; const EVP_MD* digest = nullptr; From 23d0a7f80c36f238db9fd6c5575552b9a54a0f0e Mon Sep 17 00:00:00 2001 From: James M Snell Date: Wed, 22 Jan 2025 16:58:49 -0800 Subject: [PATCH 19/38] test: make some requires lazy in common/index PR-URL: https://github.com/nodejs/node/pull/56715 Reviewed-By: Yagiz Nizipli Reviewed-By: Richard Lau Reviewed-By: Matteo Collina --- test/common/index.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/common/index.js b/test/common/index.js index 3647f4554a4647..8113f604dfcdb6 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -23,7 +23,6 @@ const process = globalThis.process; // Some tests tamper with the process globalThis. const assert = require('assert'); -const { exec, execSync, spawn, spawnSync } = require('child_process'); const fs = require('fs'); const net = require('net'); // Do not require 'os' until needed so that test-os-checked-function can @@ -31,7 +30,6 @@ const net = require('net'); const path = require('path'); const { inspect, getCallSites } = require('util'); const { isMainThread } = require('worker_threads'); -const { isModuleNamespaceObject } = require('util/types'); const tmpdir = require('./tmpdir'); const bits = ['arm64', 'loong64', 'mips', 'mipsel', 'ppc64', 'riscv64', 's390x', 'x64'] @@ -104,6 +102,7 @@ if (process.argv.length === 2 && inspect(flags), 'Use NODE_SKIP_FLAG_CHECK to run the test with the original flags.', ); + const { spawnSync } = require('child_process'); const args = [...flags, ...process.execArgv, ...process.argv.slice(1)]; const options = { encoding: 'utf8', stdio: 'inherit' }; const result = spawnSync(process.execPath, args, options); @@ -232,6 +231,7 @@ function childShouldThrowAndAbort() { // continuous testing and developers' machines escapedArgs[0] = 'ulimit -c 0 && ' + escapedArgs[0]; } + const { exec } = require('child_process'); const child = exec(...escapedArgs); child.on('exit', function onExit(exitCode, signal) { const errMsg = 'Test should have aborted ' + @@ -474,6 +474,7 @@ function canCreateSymLink() { 'System32', 'whoami.exe'); try { + const { execSync } = require('child_process'); const output = execSync(`${whoamiPath} /priv`, { timeout: 1000 }); return output.includes('SeCreateSymbolicLinkPrivilege'); } catch { @@ -780,6 +781,7 @@ function requireNoPackageJSONAbove(dir = __dirname) { } function spawnPromisified(...args) { + const { spawn } = require('child_process'); let stderr = ''; let stdout = ''; @@ -843,6 +845,7 @@ function escapePOSIXShell(cmdParts, ...args) { * @param {object} expectation shape of expected namespace. */ function expectRequiredModule(mod, expectation, checkESModule = true) { + const { isModuleNamespaceObject } = require('util/types'); const clone = { ...mod }; if (Object.hasOwn(mod, 'default') && checkESModule) { assert.strictEqual(mod.__esModule, true); @@ -920,6 +923,7 @@ const common = { }, get inFreeBSDJail() { + const { execSync } = require('child_process'); if (inFreeBSDJail !== null) return inFreeBSDJail; if (exports.isFreeBSD && From 59b3a8b21be9f206bf319cea50a6718c2230d09b Mon Sep 17 00:00:00 2001 From: Jonas Date: Sat, 25 Jan 2025 17:02:54 -0500 Subject: [PATCH 20/38] watch: reload env file for --env-file-if-exists PR-URL: https://github.com/nodejs/node/pull/56643 Reviewed-By: Yagiz Nizipli Reviewed-By: James M Snell --- lib/internal/main/watch_mode.js | 2 +- test/sequential/test-watch-mode.mjs | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/lib/internal/main/watch_mode.js b/lib/internal/main/watch_mode.js index 6e2528e64737c7..60639efb45482d 100644 --- a/lib/internal/main/watch_mode.js +++ b/lib/internal/main/watch_mode.js @@ -33,7 +33,7 @@ markBootstrapComplete(); // TODO(MoLow): Make kill signal configurable const kKillSignal = 'SIGTERM'; const kShouldFilterModules = getOptionValue('--watch-path').length === 0; -const kEnvFile = getOptionValue('--env-file'); +const kEnvFile = getOptionValue('--env-file') || getOptionValue('--env-file-if-exists'); const kWatchedPaths = ArrayPrototypeMap(getOptionValue('--watch-path'), (path) => resolve(path)); const kPreserveOutput = getOptionValue('--watch-preserve-output'); const kCommand = ArrayPrototypeSlice(process.argv, 1); diff --git a/test/sequential/test-watch-mode.mjs b/test/sequential/test-watch-mode.mjs index 39bc7223dffdfc..324cdd10b3b4ef 100644 --- a/test/sequential/test-watch-mode.mjs +++ b/test/sequential/test-watch-mode.mjs @@ -242,6 +242,32 @@ describe('watch mode', { concurrency: !process.env.TEST_PARALLEL, timeout: 60_00 } }); + it('should load new env variables when --env-file-if-exists changes', async () => { + const envKey = `TEST_ENV_${Date.now()}`; + const envKey2 = `TEST_ENV_2_${Date.now()}`; + const jsFile = createTmpFile(`console.log('ENV: ' + process.env.${envKey} + '\\n' + 'ENV2: ' + process.env.${envKey2});`); + const envFile = createTmpFile(`${envKey}=value1`, '.env'); + const { done, restart } = runInBackground({ args: ['--watch', `--env-file-if-exists=${envFile}`, jsFile] }); + + try { + await restart(); + writeFileSync(envFile, `${envKey}=value1\n${envKey2}=newValue`); + + // Second restart, after env change + const { stderr, stdout } = await restart(); + + assert.strictEqual(stderr, ''); + assert.deepStrictEqual(stdout, [ + `Restarting ${inspect(jsFile)}`, + 'ENV: value1', + 'ENV2: newValue', + `Completed running ${inspect(jsFile)}`, + ]); + } finally { + await done(); + } + }); + it('should watch changes to a failing file', async () => { const file = createTmpFile('throw new Error("fails");'); const { stderr, stdout } = await runWriteSucceed({ From 7119303a811b7f8ab5bebdaa8df4cc81f002dede Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Sat, 25 Jan 2025 23:32:13 +0000 Subject: [PATCH 21/38] module: fix bad `require.resolve` with option paths for `.` and `..` this change fixes `require.resolve` used with the `paths` option not considering `.` and `..` as relative Fixes: https://github.com/nodejs/node/issues/47000 PR-URL: https://github.com/nodejs/node/pull/56735 Reviewed-By: Yagiz Nizipli Reviewed-By: James M Snell Reviewed-By: Jordan Harband Reviewed-By: Matteo Collina --- lib/internal/modules/cjs/loader.js | 34 +++++++-------- .../relative/subdir/relative-subdir.js | 1 + ...est-require-resolve-opts-paths-relative.js | 43 +++++++++++++++++++ 3 files changed, 61 insertions(+), 17 deletions(-) create mode 100644 test/fixtures/module-require/relative/subdir/relative-subdir.js create mode 100644 test/parallel/test-require-resolve-opts-paths-relative.js diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js index a558185e08ddb1..6608be9d2db029 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js @@ -722,18 +722,8 @@ Module._findPath = function(request, paths, isMain, conditions = getCjsCondition ) )); - const isRelative = StringPrototypeCharCodeAt(request, 0) === CHAR_DOT && - ( - request.length === 1 || - StringPrototypeCharCodeAt(request, 1) === CHAR_FORWARD_SLASH || - (isWindows && StringPrototypeCharCodeAt(request, 1) === CHAR_BACKWARD_SLASH) || - (StringPrototypeCharCodeAt(request, 1) === CHAR_DOT && (( - request.length === 2 || - StringPrototypeCharCodeAt(request, 2) === CHAR_FORWARD_SLASH) || - (isWindows && StringPrototypeCharCodeAt(request, 2) === CHAR_BACKWARD_SLASH))) - ); let insidePath = true; - if (isRelative) { + if (isRelative(request)) { const normalizedRequest = path.normalize(request); if (StringPrototypeStartsWith(normalizedRequest, '..')) { insidePath = false; @@ -1328,12 +1318,7 @@ Module._resolveFilename = function(request, parent, isMain, options) { if (typeof options === 'object' && options !== null) { if (ArrayIsArray(options.paths)) { - const isRelative = StringPrototypeStartsWith(request, './') || - StringPrototypeStartsWith(request, '../') || - ((isWindows && StringPrototypeStartsWith(request, '.\\')) || - StringPrototypeStartsWith(request, '..\\')); - - if (isRelative) { + if (isRelative(request)) { paths = options.paths; } else { const fakeParent = new Module('', null); @@ -1978,6 +1963,21 @@ function createRequire(filename) { return createRequireFromPath(filepath); } +/** + * Checks if a path is relative + * @param {string} path the target path + * @returns {boolean} true if the path is relative, false otherwise + */ +function isRelative(path) { + if (StringPrototypeCharCodeAt(path, 0) !== CHAR_DOT) { return false; } + + return path.length === 1 || path === '..' || + StringPrototypeStartsWith(path, './') || + StringPrototypeStartsWith(path, '../') || + ((isWindows && StringPrototypeStartsWith(path, '.\\')) || + StringPrototypeStartsWith(path, '..\\')); +} + Module.createRequire = createRequire; /** diff --git a/test/fixtures/module-require/relative/subdir/relative-subdir.js b/test/fixtures/module-require/relative/subdir/relative-subdir.js new file mode 100644 index 00000000000000..34eb71b3c6ca39 --- /dev/null +++ b/test/fixtures/module-require/relative/subdir/relative-subdir.js @@ -0,0 +1 @@ +exports.value = 'relative subdir'; diff --git a/test/parallel/test-require-resolve-opts-paths-relative.js b/test/parallel/test-require-resolve-opts-paths-relative.js new file mode 100644 index 00000000000000..522a1fdbce82a4 --- /dev/null +++ b/test/parallel/test-require-resolve-opts-paths-relative.js @@ -0,0 +1,43 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const fixtures = require('../common/fixtures'); + +if (!common.isMainThread) + common.skip('process.chdir is not available in Workers'); + +const subdir = fixtures.path('module-require', 'relative', 'subdir'); + +process.chdir(subdir); + +// Parent directory paths (`..`) work as intended +{ + assert(require.resolve('.', { paths: ['../'] }).endsWith('index.js')); + assert(require.resolve('./index.js', { paths: ['../'] }).endsWith('index.js')); + + // paths: [".."] should resolve like paths: ["../"] + assert(require.resolve('.', { paths: ['..'] }).endsWith('index.js')); + assert(require.resolve('./index.js', { paths: ['..'] }).endsWith('index.js')); +} + +process.chdir('..'); + +// Current directory paths (`.`) work as intended +{ + assert(require.resolve('.', { paths: ['.'] }).endsWith('index.js')); + assert(require.resolve('./index.js', { paths: ['./'] }).endsWith('index.js')); + + // paths: ["."] should resolve like paths: ["../"] + assert(require.resolve('.', { paths: ['.'] }).endsWith('index.js')); + assert(require.resolve('./index.js', { paths: ['.'] }).endsWith('index.js')); +} + +// Sub directory paths work as intended +{ + // assert.deepStrictEqual(fs.readdirSync('./subdir'), [5]); + assert(require.resolve('./relative-subdir.js', { paths: ['./subdir'] }).endsWith('relative-subdir.js')); + + // paths: ["subdir"] should resolve like paths: ["./subdir"] + assert(require.resolve('./relative-subdir.js', { paths: ['subdir'] }).endsWith('relative-subdir.js')); +} From ce6a62872081d720734d82bf975653a322795288 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Sun, 26 Jan 2025 01:30:11 +0000 Subject: [PATCH 22/38] doc: add note regarding commit message trailers Co-authored-by: Yagiz Nizipli Co-authored-by: Antoine du Hamel PR-URL: https://github.com/nodejs/node/pull/56736 Reviewed-By: James M Snell Reviewed-By: Antoine du Hamel Reviewed-By: Rafael Gonzaga Reviewed-By: Luigi Pinca --- doc/contributing/pull-requests.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/contributing/pull-requests.md b/doc/contributing/pull-requests.md index 2ad538b3fd8e29..8914d60c95aa2f 100644 --- a/doc/contributing/pull-requests.md +++ b/doc/contributing/pull-requests.md @@ -184,6 +184,11 @@ A good commit message should describe what changed and why. of the log. Use the `Fixes:` prefix and the full issue URL. For other references use `Refs:`. + `Fixes:` and `Refs:` trailers get automatically added to your commit message + when the Pull Request lands as long as they are included in the + Pull Request's description. If the Pull Request lands in several commits, + by default the trailers found in the description are added to each commits. + Examples: * `Fixes: https://github.com/nodejs/node/issues/1337` From e0a71517fef4ca83f2d40d2d1600022bc82a7f9f Mon Sep 17 00:00:00 2001 From: James M Snell Date: Wed, 15 Jan 2025 13:57:47 -0800 Subject: [PATCH 23/38] src: move more crypto to ncrypto PR-URL: https://github.com/nodejs/node/pull/56653 Reviewed-By: Yagiz Nizipli --- deps/ncrypto/ncrypto.cc | 942 ++++++++++++++++++++++++++++++++++- deps/ncrypto/ncrypto.h | 289 ++++++++++- src/crypto/crypto_cipher.cc | 126 ++--- src/crypto/crypto_cipher.h | 16 +- src/crypto/crypto_common.cc | 9 +- src/crypto/crypto_context.cc | 2 +- src/crypto/crypto_dh.cc | 24 +- src/crypto/crypto_dsa.cc | 47 +- src/crypto/crypto_ec.cc | 71 +-- src/crypto/crypto_hash.cc | 140 +++--- src/crypto/crypto_hkdf.cc | 4 +- src/crypto/crypto_hmac.cc | 75 +-- src/crypto/crypto_keygen.cc | 6 +- src/crypto/crypto_keys.cc | 15 +- src/crypto/crypto_pbkdf2.cc | 6 +- src/crypto/crypto_random.cc | 22 +- src/crypto/crypto_rsa.cc | 291 ++++------- src/crypto/crypto_sig.cc | 596 ++++++++++------------ src/crypto/crypto_sig.h | 49 +- src/crypto/crypto_tls.cc | 76 ++- src/crypto/crypto_tls.h | 22 +- src/crypto/crypto_util.cc | 13 +- src/crypto/crypto_util.h | 45 +- src/crypto/crypto_x509.cc | 227 ++++----- src/crypto/crypto_x509.h | 10 +- 25 files changed, 1989 insertions(+), 1134 deletions(-) diff --git a/deps/ncrypto/ncrypto.cc b/deps/ncrypto/ncrypto.cc index ce2e7b384eb198..be3ef98d763366 100644 --- a/deps/ncrypto/ncrypto.cc +++ b/deps/ncrypto/ncrypto.cc @@ -15,8 +15,23 @@ #include "dh-primes.h" #endif // OPENSSL_IS_BORINGSSL +// EVP_PKEY_CTX_set_dsa_paramgen_q_bits was added in OpenSSL 1.1.1e. +#if OPENSSL_VERSION_NUMBER < 0x1010105fL +#define EVP_PKEY_CTX_set_dsa_paramgen_q_bits(ctx, qbits) \ + EVP_PKEY_CTX_ctrl((ctx), \ + EVP_PKEY_DSA, \ + EVP_PKEY_OP_PARAMGEN, \ + EVP_PKEY_CTRL_DSA_PARAMGEN_Q_BITS, \ + (qbits), \ + nullptr) +#endif + namespace ncrypto { namespace { +using BignumCtxPointer = DeleteFnPtr; +using BignumGenCallbackPointer = DeleteFnPtr; +using NetscapeSPKIPointer = DeleteFnPtr; + static constexpr int kX509NameFlagsRFC2253WithinUtf8JSON = XN_FLAG_RFC2253 & ~ASN1_STRFLGS_ESC_MSB & ~ASN1_STRFLGS_ESC_CTRL; } // namespace @@ -87,6 +102,10 @@ DataPointer DataPointer::Alloc(size_t len) { return DataPointer(OPENSSL_zalloc(len), len); } +DataPointer DataPointer::Copy(const Buffer& buffer) { + return DataPointer(OPENSSL_memdup(buffer.data, buffer.len), buffer.len); +} + DataPointer::DataPointer(void* data, size_t length) : data_(data), len_(length) {} @@ -109,6 +128,11 @@ DataPointer::~DataPointer() { reset(); } +void DataPointer::zero() { + if (!data_) return; + OPENSSL_cleanse(data_, len_); +} + void DataPointer::reset(void* data, size_t length) { if (data_ != nullptr) { OPENSSL_clear_free(data_, len_); @@ -131,6 +155,15 @@ Buffer DataPointer::release() { return buf; } +DataPointer DataPointer::resize(size_t len) { + size_t actual_len = std::min(len_, len); + auto buf = release(); + if (actual_len == len_) return DataPointer(buf); + buf.data = OPENSSL_realloc(buf.data, actual_len); + buf.len = actual_len; + return DataPointer(buf); +} + // ============================================================================ bool isFipsEnabled() { #if OPENSSL_VERSION_MAJOR >= 3 @@ -782,7 +815,7 @@ bool PrintGeneralName(const BIOPointer& out, const GENERAL_NAME* gen) { bool SafeX509SubjectAltNamePrint(const BIOPointer& out, X509_EXTENSION* ext) { auto ret = OBJ_obj2nid(X509_EXTENSION_get_object(ext)); - NCRYPTO_ASSERT_EQUAL(ret, NID_subject_alt_name, "unexpected extension type"); + if (ret != NID_subject_alt_name) return false; GENERAL_NAMES* names = static_cast(X509V3_EXT_d2i(ext)); if (names == nullptr) return false; @@ -805,7 +838,7 @@ bool SafeX509SubjectAltNamePrint(const BIOPointer& out, X509_EXTENSION* ext) { bool SafeX509InfoAccessPrint(const BIOPointer& out, X509_EXTENSION* ext) { auto ret = OBJ_obj2nid(X509_EXTENSION_get_object(ext)); - NCRYPTO_ASSERT_EQUAL(ret, NID_info_access, "unexpected extension type"); + if (ret != NID_info_access) return false; AUTHORITY_INFO_ACCESS* descs = static_cast(X509V3_EXT_d2i(ext)); @@ -1132,6 +1165,49 @@ Result X509Pointer::Parse( return Result(ERR_get_error()); } +bool X509View::enumUsages(UsageCallback callback) const { + if (cert_ == nullptr) return false; + StackOfASN1 eku(static_cast( + X509_get_ext_d2i(cert_, NID_ext_key_usage, nullptr, nullptr))); + if (!eku) return false; + const int count = sk_ASN1_OBJECT_num(eku.get()); + char buf[256]{}; + + for (int i = 0; i < count; i++) { + if (OBJ_obj2txt(buf, sizeof(buf), sk_ASN1_OBJECT_value(eku.get(), i), 1) >= + 0) { + callback(buf); + } + } + return true; +} + +bool X509View::ifRsa(KeyCallback callback) const { + if (cert_ == nullptr) return true; + OSSL3_CONST EVP_PKEY* pkey = X509_get0_pubkey(cert_); + auto id = EVP_PKEY_id(pkey); + if (id == EVP_PKEY_RSA || id == EVP_PKEY_RSA2 || id == EVP_PKEY_RSA_PSS) { + Rsa rsa(EVP_PKEY_get0_RSA(pkey)); + if (!rsa) [[unlikely]] + return true; + return callback(rsa); + } + return true; +} + +bool X509View::ifEc(KeyCallback callback) const { + if (cert_ == nullptr) return true; + OSSL3_CONST EVP_PKEY* pkey = X509_get0_pubkey(cert_); + auto id = EVP_PKEY_id(pkey); + if (id == EVP_PKEY_EC) { + Ec ec(EVP_PKEY_get0_EC_KEY(pkey)); + if (!ec) [[unlikely]] + return true; + return callback(ec); + } + return true; +} + X509Pointer X509Pointer::IssuerFrom(const SSLPointer& ssl, const X509View& view) { return IssuerFrom(SSL_get_SSL_CTX(ssl.get()), view); @@ -1493,7 +1569,7 @@ DataPointer DHPointer::stateless(const EVPKeyPointer& ourKey, size_t out_size; if (!ourKey || !theirKey) return {}; - EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new(ourKey.get(), nullptr)); + auto ctx = EVPKeyCtxPointer::New(ourKey); if (!ctx || EVP_PKEY_derive_init(ctx.get()) <= 0 || EVP_PKEY_derive_set_peer(ctx.get(), theirKey.get()) <= 0 || EVP_PKEY_derive(ctx.get(), nullptr, &out_size) <= 0) { @@ -1522,9 +1598,18 @@ DataPointer DHPointer::stateless(const EVPKeyPointer& ourKey, // KDF const EVP_MD* getDigestByName(const std::string_view name) { + // Historically, "dss1" and "DSS1" were DSA aliases for SHA-1 + // exposed through the public API. + if (name == "dss1" || name == "DSS1") [[unlikely]] { + return EVP_sha1(); + } return EVP_get_digestbyname(name.data()); } +const EVP_CIPHER* getCipherByName(const std::string_view name) { + return EVP_get_cipherbyname(name.data()); +} + bool checkHkdfLength(const EVP_MD* md, size_t length) { // HKDF-Expand computes up to 255 HMAC blocks, each having as many bits as // the output of the hash function. 255 is a hard limit because HKDF appends @@ -1547,8 +1632,7 @@ DataPointer hkdf(const EVP_MD* md, return {}; } - EVPKeyCtxPointer ctx = - EVPKeyCtxPointer(EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, nullptr)); + auto ctx = EVPKeyCtxPointer::NewFromID(EVP_PKEY_HKDF); if (!ctx || !EVP_PKEY_derive_init(ctx.get()) || !EVP_PKEY_CTX_set_hkdf_md(ctx.get(), md) || !EVP_PKEY_CTX_add1_hkdf_info(ctx.get(), info.data, info.len)) { @@ -1704,6 +1788,26 @@ EVPKeyPointer EVPKeyPointer::NewRawPrivate( EVP_PKEY_new_raw_private_key(id, nullptr, data.data, data.len)); } +EVPKeyPointer EVPKeyPointer::NewDH(DHPointer&& dh) { + if (!dh) return {}; + auto key = New(); + if (!key) return {}; + if (EVP_PKEY_assign_DH(key.get(), dh.get())) { + dh.release(); + } + return key; +} + +EVPKeyPointer EVPKeyPointer::NewRSA(RSAPointer&& rsa) { + if (!rsa) return {}; + auto key = New(); + if (!key) return {}; + if (EVP_PKEY_assign_RSA(key.get(), rsa.get())) { + rsa.release(); + } + return key; +} + EVPKeyPointer::EVPKeyPointer(EVP_PKEY* pkey) : pkey_(pkey) {} EVPKeyPointer::EVPKeyPointer(EVPKeyPointer&& other) noexcept @@ -1757,7 +1861,7 @@ size_t EVPKeyPointer::size() const { EVPKeyCtxPointer EVPKeyPointer::newCtx() const { if (!pkey_) return {}; - return EVPKeyCtxPointer(EVP_PKEY_CTX_new(get(), nullptr)); + return EVPKeyCtxPointer::New(*this); } size_t EVPKeyPointer::rawPublicKeySize() const { @@ -2230,6 +2334,84 @@ Result EVPKeyPointer::writePublicKey( return bio; } +bool EVPKeyPointer::isRsaVariant() const { + if (!pkey_) return false; + int type = id(); + return type == EVP_PKEY_RSA || type == EVP_PKEY_RSA2 || + type == EVP_PKEY_RSA_PSS; +} + +bool EVPKeyPointer::isOneShotVariant() const { + if (!pkey_) return false; + int type = id(); + return type == EVP_PKEY_ED25519 || type == EVP_PKEY_ED448; +} + +bool EVPKeyPointer::isSigVariant() const { + if (!pkey_) return false; + int type = id(); + return type == EVP_PKEY_EC || type == EVP_PKEY_DSA; +} + +int EVPKeyPointer::getDefaultSignPadding() const { + return id() == EVP_PKEY_RSA_PSS ? RSA_PKCS1_PSS_PADDING : RSA_PKCS1_PADDING; +} + +std::optional EVPKeyPointer::getBytesOfRS() const { + if (!pkey_) return std::nullopt; + int bits, id = base_id(); + + if (id == EVP_PKEY_DSA) { + const DSA* dsa_key = EVP_PKEY_get0_DSA(get()); + // Both r and s are computed mod q, so their width is limited by that of q. + bits = BignumPointer::GetBitCount(DSA_get0_q(dsa_key)); + } else if (id == EVP_PKEY_EC) { + bits = EC_GROUP_order_bits(ECKeyPointer::GetGroup(*this)); + } else { + return std::nullopt; + } + + return (bits + 7) / 8; +} + +EVPKeyPointer::operator Rsa() const { + int type = id(); + if (type != EVP_PKEY_RSA && type != EVP_PKEY_RSA_PSS) return {}; + + // TODO(tniessen): Remove the "else" branch once we drop support for OpenSSL + // versions older than 1.1.1e via FIPS / dynamic linking. + OSSL3_CONST RSA* rsa; + if (OPENSSL_VERSION_NUMBER >= 0x1010105fL) { + rsa = EVP_PKEY_get0_RSA(get()); + } else { + rsa = static_cast(EVP_PKEY_get0(get())); + } + if (rsa == nullptr) return {}; + return Rsa(rsa); +} + +bool EVPKeyPointer::validateDsaParameters() const { + if (!pkey_) return false; + /* Validate DSA2 parameters from FIPS 186-4 */ +#if OPENSSL_VERSION_MAJOR >= 3 + if (EVP_default_properties_is_fips_enabled(nullptr) && EVP_PKEY_DSA == id()) { +#else + if (FIPS_mode() && EVP_PKEY_DSA == id()) { +#endif + const DSA* dsa = EVP_PKEY_get0_DSA(pkey_.get()); + const BIGNUM* p; + const BIGNUM* q; + DSA_get0_pqg(dsa, &p, &q, nullptr); + int L = BignumPointer::GetBitCount(p); + int N = BignumPointer::GetBitCount(q); + + return (L == 1024 && N == 160) || (L == 2048 && N == 224) || + (L == 2048 && N == 256) || (L == 3072 && N == 256); + } + + return true; +} + // ============================================================================ SSLPointer::SSLPointer(SSL* ssl) : ssl_(ssl) {} @@ -2883,4 +3065,752 @@ ECKeyPointer ECKeyPointer::New(const EC_GROUP* group) { return ptr; } +// ============================================================================ + +EVPKeyCtxPointer::EVPKeyCtxPointer() : ctx_(nullptr) {} + +EVPKeyCtxPointer::EVPKeyCtxPointer(EVP_PKEY_CTX* ctx) : ctx_(ctx) {} + +EVPKeyCtxPointer::EVPKeyCtxPointer(EVPKeyCtxPointer&& other) noexcept + : ctx_(other.release()) {} + +EVPKeyCtxPointer& EVPKeyCtxPointer::operator=( + EVPKeyCtxPointer&& other) noexcept { + ctx_.reset(other.release()); + return *this; +} + +EVPKeyCtxPointer::~EVPKeyCtxPointer() { + reset(); +} + +void EVPKeyCtxPointer::reset(EVP_PKEY_CTX* ctx) { + ctx_.reset(ctx); +} + +EVP_PKEY_CTX* EVPKeyCtxPointer::release() { + return ctx_.release(); +} + +EVPKeyCtxPointer EVPKeyCtxPointer::New(const EVPKeyPointer& key) { + if (!key) return {}; + return EVPKeyCtxPointer(EVP_PKEY_CTX_new(key.get(), nullptr)); +} + +EVPKeyCtxPointer EVPKeyCtxPointer::NewFromID(int id) { + return EVPKeyCtxPointer(EVP_PKEY_CTX_new_id(id, nullptr)); +} + +bool EVPKeyCtxPointer::initForDerive(const EVPKeyPointer& peer) { + if (!ctx_) return false; + if (EVP_PKEY_derive_init(ctx_.get()) != 1) return false; + return EVP_PKEY_derive_set_peer(ctx_.get(), peer.get()) == 1; +} + +bool EVPKeyCtxPointer::initForKeygen() { + if (!ctx_) return false; + return EVP_PKEY_keygen_init(ctx_.get()) == 1; +} + +bool EVPKeyCtxPointer::initForParamgen() { + if (!ctx_) return false; + return EVP_PKEY_paramgen_init(ctx_.get()) == 1; +} + +int EVPKeyCtxPointer::initForVerify() { + if (!ctx_) return 0; + return EVP_PKEY_verify_init(ctx_.get()); +} + +int EVPKeyCtxPointer::initForSign() { + if (!ctx_) return 0; + return EVP_PKEY_sign_init(ctx_.get()); +} + +bool EVPKeyCtxPointer::setDhParameters(int prime_size, uint32_t generator) { + if (!ctx_) return false; + return EVP_PKEY_CTX_set_dh_paramgen_prime_len(ctx_.get(), prime_size) == 1 && + EVP_PKEY_CTX_set_dh_paramgen_generator(ctx_.get(), generator) == 1; +} + +bool EVPKeyCtxPointer::setDsaParameters(uint32_t bits, + std::optional q_bits) { + if (!ctx_) return false; + if (EVP_PKEY_CTX_set_dsa_paramgen_bits(ctx_.get(), bits) != 1) { + return false; + } + if (q_bits.has_value() && + EVP_PKEY_CTX_set_dsa_paramgen_q_bits(ctx_.get(), q_bits.value()) != 1) { + return false; + } + return true; +} + +bool EVPKeyCtxPointer::setEcParameters(int curve, int encoding) { + if (!ctx_) return false; + return EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx_.get(), curve) == 1 && + EVP_PKEY_CTX_set_ec_param_enc(ctx_.get(), encoding) == 1; +} + +bool EVPKeyCtxPointer::setRsaOaepMd(const EVP_MD* md) { + if (md == nullptr || !ctx_) return false; + return EVP_PKEY_CTX_set_rsa_oaep_md(ctx_.get(), md) > 0; +} + +bool EVPKeyCtxPointer::setRsaMgf1Md(const EVP_MD* md) { + if (md == nullptr || !ctx_) return false; + return EVP_PKEY_CTX_set_rsa_mgf1_md(ctx_.get(), md) > 0; +} + +bool EVPKeyCtxPointer::setRsaPadding(int padding) { + return setRsaPadding(ctx_.get(), padding, std::nullopt); +} + +bool EVPKeyCtxPointer::setRsaPadding(EVP_PKEY_CTX* ctx, + int padding, + std::optional salt_len) { + if (ctx == nullptr) return false; + if (EVP_PKEY_CTX_set_rsa_padding(ctx, padding) <= 0) { + return false; + } + if (padding == RSA_PKCS1_PSS_PADDING && salt_len.has_value()) { + return EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, salt_len.value()) > 0; + } + return true; +} + +bool EVPKeyCtxPointer::setRsaKeygenBits(int bits) { + if (!ctx_) return false; + return EVP_PKEY_CTX_set_rsa_keygen_bits(ctx_.get(), bits) == 1; +} + +bool EVPKeyCtxPointer::setRsaKeygenPubExp(BignumPointer&& e) { + if (!ctx_) return false; + if (EVP_PKEY_CTX_set_rsa_keygen_pubexp(ctx_.get(), e.get()) == 1) { + // The ctx_ takes ownership of e on success. + e.release(); + return true; + } + return false; +} + +bool EVPKeyCtxPointer::setRsaPssKeygenMd(const EVP_MD* md) { + if (md == nullptr || !ctx_) return false; + return EVP_PKEY_CTX_set_rsa_pss_keygen_md(ctx_.get(), md) > 0; +} + +bool EVPKeyCtxPointer::setRsaPssKeygenMgf1Md(const EVP_MD* md) { + if (md == nullptr || !ctx_) return false; + return EVP_PKEY_CTX_set_rsa_pss_keygen_mgf1_md(ctx_.get(), md) > 0; +} + +bool EVPKeyCtxPointer::setRsaPssSaltlen(int salt_len) { + if (!ctx_) return false; + return EVP_PKEY_CTX_set_rsa_pss_keygen_saltlen(ctx_.get(), salt_len) > 0; +} + +bool EVPKeyCtxPointer::setRsaImplicitRejection() { + if (!ctx_) return false; + return EVP_PKEY_CTX_ctrl_str( + ctx_.get(), "rsa_pkcs1_implicit_rejection", "1") > 0; + // From the doc -2 means that the option is not supported. + // The default for the option is enabled and if it has been + // specifically disabled we want to respect that so we will + // not throw an error if the option is supported regardless + // of how it is set. The call to set the value + // will not affect what is used since a different context is + // used in the call if the option is supported +} + +bool EVPKeyCtxPointer::setRsaOaepLabel(DataPointer&& data) { + if (!ctx_) return false; + if (EVP_PKEY_CTX_set0_rsa_oaep_label(ctx_.get(), + static_cast(data.get()), + data.size()) > 0) { + // The ctx_ takes ownership of data on success. + data.release(); + return true; + } + return false; +} + +bool EVPKeyCtxPointer::setSignatureMd(const EVPMDCtxPointer& md) { + if (!ctx_) return false; + return EVP_PKEY_CTX_set_signature_md(ctx_.get(), EVP_MD_CTX_md(md.get())) == + 1; +} + +bool EVPKeyCtxPointer::initForEncrypt() { + if (!ctx_) return false; + return EVP_PKEY_encrypt_init(ctx_.get()) == 1; +} + +bool EVPKeyCtxPointer::initForDecrypt() { + if (!ctx_) return false; + return EVP_PKEY_decrypt_init(ctx_.get()) == 1; +} + +DataPointer EVPKeyCtxPointer::derive() const { + if (!ctx_) return {}; + size_t len = 0; + if (EVP_PKEY_derive(ctx_.get(), nullptr, &len) != 1) return {}; + auto data = DataPointer::Alloc(len); + if (!data) return {}; + if (EVP_PKEY_derive( + ctx_.get(), static_cast(data.get()), &len) != 1) { + return {}; + } + return data; +} + +EVPKeyPointer EVPKeyCtxPointer::paramgen() const { + if (!ctx_) return {}; + EVP_PKEY* key = nullptr; + if (EVP_PKEY_paramgen(ctx_.get(), &key) != 1) return {}; + return EVPKeyPointer(key); +} + +bool EVPKeyCtxPointer::publicCheck() const { + if (!ctx_) return false; +#if OPENSSL_VERSION_MAJOR >= 3 + return EVP_PKEY_public_check_quick(ctx_.get()) == 1; +#else + return EVP_PKEY_public_check(ctx_.get()) == 1; +#endif +} + +bool EVPKeyCtxPointer::privateCheck() const { + if (!ctx_) return false; + return EVP_PKEY_check(ctx_.get()) == 1; +} + +bool EVPKeyCtxPointer::verify(const Buffer& sig, + const Buffer& data) { + if (!ctx_) return false; + return EVP_PKEY_verify(ctx_.get(), sig.data, sig.len, data.data, data.len) == + 1; +} + +DataPointer EVPKeyCtxPointer::sign(const Buffer& data) { + if (!ctx_) return {}; + size_t len = 0; + if (EVP_PKEY_sign(ctx_.get(), nullptr, &len, data.data, data.len) != 1) { + return {}; + } + auto buf = DataPointer::Alloc(len); + if (!buf) return {}; + if (EVP_PKEY_sign(ctx_.get(), + static_cast(buf.get()), + &len, + data.data, + data.len) != 1) { + return {}; + } + return buf.resize(len); +} + +bool EVPKeyCtxPointer::signInto(const Buffer& data, + Buffer* sig) { + if (!ctx_) return false; + size_t len = sig->len; + if (EVP_PKEY_sign(ctx_.get(), sig->data, &len, data.data, data.len) != 1) { + return false; + } + sig->len = len; + return true; +} + +// ============================================================================ + +namespace { + +using EVP_PKEY_cipher_init_t = int(EVP_PKEY_CTX* ctx); +using EVP_PKEY_cipher_t = int(EVP_PKEY_CTX* ctx, + unsigned char* out, + size_t* outlen, + const unsigned char* in, + size_t inlen); + +template +DataPointer RSA_Cipher(const EVPKeyPointer& key, + const Rsa::CipherParams& params, + const Buffer in) { + if (!key) return {}; + EVPKeyCtxPointer ctx = key.newCtx(); + + if (!ctx || init(ctx.get()) <= 0 || !ctx.setRsaPadding(params.padding) || + (params.digest != nullptr && (!ctx.setRsaOaepMd(params.digest) || + !ctx.setRsaMgf1Md(params.digest)))) { + return {}; + } + + if (params.label.len != 0 && params.label.data != nullptr && + !ctx.setRsaOaepLabel(DataPointer::Copy(params.label))) { + return {}; + } + + size_t out_len = 0; + if (cipher(ctx.get(), + nullptr, + &out_len, + reinterpret_cast(in.data), + in.len) <= 0) { + return {}; + } + + auto buf = DataPointer::Alloc(out_len); + if (!buf) return {}; + + if (cipher(ctx.get(), + static_cast(buf.get()), + &out_len, + static_cast(in.data), + in.len) <= 0) { + return {}; + } + + return buf.resize(out_len); +} + +template +DataPointer CipherImpl(const EVPKeyPointer& key, + const Rsa::CipherParams& params, + const Buffer in) { + if (!key) return {}; + EVPKeyCtxPointer ctx = key.newCtx(); + if (!ctx || init(ctx.get()) <= 0 || !ctx.setRsaPadding(params.padding) || + (params.digest != nullptr && !ctx.setRsaOaepMd(params.digest))) { + return {}; + } + + if (params.label.len != 0 && params.label.data != nullptr && + !ctx.setRsaOaepLabel(DataPointer::Copy(params.label))) { + return {}; + } + + size_t out_len = 0; + if (cipher(ctx.get(), + nullptr, + &out_len, + static_cast(in.data), + in.len) <= 0) { + return {}; + } + + auto buf = DataPointer::Alloc(out_len); + if (!buf) return {}; + + if (cipher(ctx.get(), + static_cast(buf.get()), + &out_len, + static_cast(in.data), + in.len) <= 0) { + return {}; + } + + return buf.resize(out_len); +} +} // namespace + +Rsa::Rsa() : rsa_(nullptr) {} + +Rsa::Rsa(OSSL3_CONST RSA* ptr) : rsa_(ptr) {} + +const Rsa::PublicKey Rsa::getPublicKey() const { + if (rsa_ == nullptr) return {}; + PublicKey key; + RSA_get0_key(rsa_, &key.n, &key.e, &key.d); + return key; +} + +const Rsa::PrivateKey Rsa::getPrivateKey() const { + if (rsa_ == nullptr) return {}; + PrivateKey key; + RSA_get0_factors(rsa_, &key.p, &key.q); + RSA_get0_crt_params(rsa_, &key.dp, &key.dq, &key.qi); + return key; +} + +const std::optional Rsa::getPssParams() const { + if (rsa_ == nullptr) return std::nullopt; + const RSA_PSS_PARAMS* params = RSA_get0_pss_params(rsa_); + if (params == nullptr) return std::nullopt; + Rsa::PssParams ret{ + .digest = OBJ_nid2ln(NID_sha1), + .mgf1_digest = OBJ_nid2ln(NID_sha1), + .salt_length = 20, + }; + + if (params->hashAlgorithm != nullptr) { + const ASN1_OBJECT* hash_obj; + X509_ALGOR_get0(&hash_obj, nullptr, nullptr, params->hashAlgorithm); + ret.digest = OBJ_nid2ln(OBJ_obj2nid(hash_obj)); + } + + if (params->maskGenAlgorithm != nullptr) { + const ASN1_OBJECT* mgf_obj; + X509_ALGOR_get0(&mgf_obj, nullptr, nullptr, params->maskGenAlgorithm); + int mgf_nid = OBJ_obj2nid(mgf_obj); + if (mgf_nid == NID_mgf1) { + const ASN1_OBJECT* mgf1_hash_obj; + X509_ALGOR_get0(&mgf1_hash_obj, nullptr, nullptr, params->maskHash); + ret.mgf1_digest = OBJ_nid2ln(OBJ_obj2nid(mgf1_hash_obj)); + } + } + + if (params->saltLength != nullptr) { + if (ASN1_INTEGER_get_int64(&ret.salt_length, params->saltLength) != 1) { + return std::nullopt; + } + } + return ret; +} + +bool Rsa::setPublicKey(BignumPointer&& n, BignumPointer&& e) { + if (!n || !e) return false; + if (RSA_set0_key(const_cast(rsa_), n.get(), e.get(), nullptr) == 1) { + n.release(); + e.release(); + return true; + } + return false; +} + +bool Rsa::setPrivateKey(BignumPointer&& d, + BignumPointer&& q, + BignumPointer&& p, + BignumPointer&& dp, + BignumPointer&& dq, + BignumPointer&& qi) { + if (!RSA_set0_key(const_cast(rsa_), nullptr, nullptr, d.get())) { + return false; + } + d.release(); + + if (!RSA_set0_factors(const_cast(rsa_), p.get(), q.get())) { + return false; + } + p.release(); + q.release(); + + if (!RSA_set0_crt_params( + const_cast(rsa_), dp.get(), dq.get(), qi.get())) { + return false; + } + dp.release(); + dq.release(); + qi.release(); + return true; +} + +DataPointer Rsa::encrypt(const EVPKeyPointer& key, + const Rsa::CipherParams& params, + const Buffer in) { + if (!key) return {}; + return RSA_Cipher(key, params, in); +} + +DataPointer Rsa::decrypt(const EVPKeyPointer& key, + const Rsa::CipherParams& params, + const Buffer in) { + if (!key) return {}; + return RSA_Cipher(key, params, in); +} + +DataPointer Cipher::encrypt(const EVPKeyPointer& key, + const CipherParams& params, + const Buffer in) { + // public operation + return CipherImpl(key, params, in); +} + +DataPointer Cipher::decrypt(const EVPKeyPointer& key, + const CipherParams& params, + const Buffer in) { + // private operation + return CipherImpl(key, params, in); +} + +DataPointer Cipher::sign(const EVPKeyPointer& key, + const CipherParams& params, + const Buffer in) { + // private operation + return CipherImpl(key, params, in); +} + +DataPointer Cipher::recover(const EVPKeyPointer& key, + const CipherParams& params, + const Buffer in) { + // public operation + return CipherImpl( + key, params, in); +} + +// ============================================================================ + +Ec::Ec() : ec_(nullptr) {} + +Ec::Ec(OSSL3_CONST EC_KEY* key) : ec_(key) {} + +const EC_GROUP* Ec::getGroup() const { + return ECKeyPointer::GetGroup(ec_); +} + +int Ec::getCurve() const { + return EC_GROUP_get_curve_name(getGroup()); +} + +// ============================================================================ + +EVPMDCtxPointer::EVPMDCtxPointer() : ctx_(nullptr) {} + +EVPMDCtxPointer::EVPMDCtxPointer(EVP_MD_CTX* ctx) : ctx_(ctx) {} + +EVPMDCtxPointer::EVPMDCtxPointer(EVPMDCtxPointer&& other) noexcept + : ctx_(other.release()) {} + +EVPMDCtxPointer& EVPMDCtxPointer::operator=(EVPMDCtxPointer&& other) noexcept { + ctx_.reset(other.release()); + return *this; +} + +EVPMDCtxPointer::~EVPMDCtxPointer() { + reset(); +} + +void EVPMDCtxPointer::reset(EVP_MD_CTX* ctx) { + ctx_.reset(ctx); +} + +EVP_MD_CTX* EVPMDCtxPointer::release() { + return ctx_.release(); +} + +bool EVPMDCtxPointer::digestInit(const EVP_MD* digest) { + if (!ctx_) return false; + return EVP_DigestInit_ex(ctx_.get(), digest, nullptr) > 0; +} + +bool EVPMDCtxPointer::digestUpdate(const Buffer& in) { + if (!ctx_) return false; + return EVP_DigestUpdate(ctx_.get(), in.data, in.len) > 0; +} + +DataPointer EVPMDCtxPointer::digestFinal(size_t length) { + if (!ctx_) return {}; + + auto buf = DataPointer::Alloc(length); + if (!buf) return {}; + + Buffer buffer = buf; + + if (!digestFinalInto(&buffer)) [[unlikely]] { + return {}; + } + + return buf; +} + +bool EVPMDCtxPointer::digestFinalInto(Buffer* buf) { + if (!ctx_) return false; + + auto ptr = static_cast(buf->data); + + int ret = (buf->len == getExpectedSize()) + ? EVP_DigestFinal_ex(ctx_.get(), ptr, nullptr) + : EVP_DigestFinalXOF(ctx_.get(), ptr, buf->len); + + if (ret != 1) [[unlikely]] + return false; + + return true; +} + +size_t EVPMDCtxPointer::getExpectedSize() { + if (!ctx_) return 0; + return EVP_MD_CTX_size(ctx_.get()); +} + +size_t EVPMDCtxPointer::getDigestSize() const { + return EVP_MD_size(getDigest()); +} + +const EVP_MD* EVPMDCtxPointer::getDigest() const { + if (!ctx_) return nullptr; + return EVP_MD_CTX_md(ctx_.get()); +} + +bool EVPMDCtxPointer::hasXofFlag() const { + if (!ctx_) return false; + return (EVP_MD_flags(getDigest()) & EVP_MD_FLAG_XOF) == EVP_MD_FLAG_XOF; +} + +bool EVPMDCtxPointer::copyTo(const EVPMDCtxPointer& other) const { + if (!ctx_ || !other) return {}; + if (EVP_MD_CTX_copy(other.get(), ctx_.get()) != 1) return false; + return true; +} + +std::optional EVPMDCtxPointer::signInit(const EVPKeyPointer& key, + const EVP_MD* digest) { + EVP_PKEY_CTX* ctx = nullptr; + if (!EVP_DigestSignInit(ctx_.get(), &ctx, digest, nullptr, key.get())) { + return std::nullopt; + } + return ctx; +} + +std::optional EVPMDCtxPointer::verifyInit( + const EVPKeyPointer& key, const EVP_MD* digest) { + EVP_PKEY_CTX* ctx = nullptr; + if (!EVP_DigestVerifyInit(ctx_.get(), &ctx, digest, nullptr, key.get())) { + return std::nullopt; + } + return ctx; +} + +DataPointer EVPMDCtxPointer::signOneShot( + const Buffer& buf) const { + if (!ctx_) return {}; + size_t len; + if (!EVP_DigestSign(ctx_.get(), nullptr, &len, buf.data, buf.len)) { + return {}; + } + auto data = DataPointer::Alloc(len); + if (!data) [[unlikely]] + return {}; + + if (!EVP_DigestSign(ctx_.get(), + static_cast(data.get()), + &len, + buf.data, + buf.len)) { + return {}; + } + return data; +} + +DataPointer EVPMDCtxPointer::sign( + const Buffer& buf) const { + if (!ctx_) [[unlikely]] + return {}; + size_t len; + if (!EVP_DigestSignUpdate(ctx_.get(), buf.data, buf.len) || + !EVP_DigestSignFinal(ctx_.get(), nullptr, &len)) { + return {}; + } + auto data = DataPointer::Alloc(len); + if (!data) [[unlikely]] + return {}; + if (!EVP_DigestSignFinal( + ctx_.get(), static_cast(data.get()), &len)) { + return {}; + } + return data.resize(len); +} + +bool EVPMDCtxPointer::verify(const Buffer& buf, + const Buffer& sig) const { + if (!ctx_) return false; + int ret = EVP_DigestVerify(ctx_.get(), sig.data, sig.len, buf.data, buf.len); + return ret == 1; +} + +EVPMDCtxPointer EVPMDCtxPointer::New() { + return EVPMDCtxPointer(EVP_MD_CTX_new()); +} + +// ============================================================================ + +bool extractP1363(const Buffer& buf, + unsigned char* dest, + size_t n) { + auto asn1_sig = ECDSASigPointer::Parse(buf); + if (!asn1_sig) return false; + + return BignumPointer::EncodePaddedInto(asn1_sig.r(), dest, n) > 0 && + BignumPointer::EncodePaddedInto(asn1_sig.s(), dest + n, n) > 0; +} + +// ============================================================================ + +HMACCtxPointer::HMACCtxPointer() : ctx_(nullptr) {} + +HMACCtxPointer::HMACCtxPointer(HMAC_CTX* ctx) : ctx_(ctx) {} + +HMACCtxPointer::HMACCtxPointer(HMACCtxPointer&& other) noexcept + : ctx_(other.release()) {} + +HMACCtxPointer& HMACCtxPointer::operator=(HMACCtxPointer&& other) noexcept { + ctx_.reset(other.release()); + return *this; +} + +HMACCtxPointer::~HMACCtxPointer() { + reset(); +} + +void HMACCtxPointer::reset(HMAC_CTX* ctx) { + ctx_.reset(ctx); +} + +HMAC_CTX* HMACCtxPointer::release() { + return ctx_.release(); +} + +bool HMACCtxPointer::init(const Buffer& buf, const EVP_MD* md) { + if (!ctx_) return false; + return HMAC_Init_ex(ctx_.get(), buf.data, buf.len, md, nullptr) == 1; +} + +bool HMACCtxPointer::update(const Buffer& buf) { + if (!ctx_) return false; + return HMAC_Update(ctx_.get(), + static_cast(buf.data), + buf.len) == 1; +} + +DataPointer HMACCtxPointer::digest() { + auto data = DataPointer::Alloc(EVP_MAX_MD_SIZE); + if (!data) return {}; + Buffer buf = data; + if (!digestInto(&buf)) return {}; + return data.resize(buf.len); +} + +bool HMACCtxPointer::digestInto(Buffer* buf) { + if (!ctx_) return false; + + unsigned int len = buf->len; + if (!HMAC_Final(ctx_.get(), static_cast(buf->data), &len)) { + return false; + } + buf->len = len; + return true; +} + +HMACCtxPointer HMACCtxPointer::New() { + return HMACCtxPointer(HMAC_CTX_new()); +} + +DataPointer hashDigest(const Buffer& buf, + const EVP_MD* md) { + if (md == nullptr) return {}; + size_t md_len = EVP_MD_size(md); + unsigned int result_size; + auto data = DataPointer::Alloc(md_len); + if (!data) return {}; + + if (!EVP_Digest(buf.data, + buf.len, + reinterpret_cast(data.get()), + &result_size, + md, + nullptr)) { + return {}; + } + + return data.resize(result_size); +} + } // namespace ncrypto diff --git a/deps/ncrypto/ncrypto.h b/deps/ncrypto/ncrypto.h index e5bf2b529bf239..75ac9fd8d705aa 100644 --- a/deps/ncrypto/ncrypto.h +++ b/deps/ncrypto/ncrypto.h @@ -201,18 +201,28 @@ struct FunctionDeleter { template using DeleteFnPtr = typename FunctionDeleter::Pointer; -using BignumCtxPointer = DeleteFnPtr; -using BignumGenCallbackPointer = DeleteFnPtr; -using EVPKeyCtxPointer = DeleteFnPtr; -using EVPMDCtxPointer = DeleteFnPtr; -using HMACCtxPointer = DeleteFnPtr; -using NetscapeSPKIPointer = DeleteFnPtr; using PKCS8Pointer = DeleteFnPtr; using RSAPointer = DeleteFnPtr; using SSLSessionPointer = DeleteFnPtr; +class BIOPointer; +class BignumPointer; class CipherCtxPointer; +class DataPointer; +class DHPointer; class ECKeyPointer; +class EVPKeyPointer; +class EVPMDCtxPointer; +class SSLCtxPointer; +class SSLPointer; +class X509View; +class X509Pointer; +class ECDSASigPointer; +class ECGroupPointer; +class ECPointPointer; +class ECKeyPointer; +class Rsa; +class Ec; struct StackOfXASN1Deleter { void operator()(STACK_OF(ASN1_OBJECT) * p) const { @@ -228,6 +238,9 @@ struct Buffer { size_t len = 0; }; +DataPointer hashDigest(const Buffer& data, + const EVP_MD* md); + class Cipher final { public: Cipher() = default; @@ -258,15 +271,108 @@ class Cipher final { static const Cipher FromNid(int nid); static const Cipher FromCtx(const CipherCtxPointer& ctx); + struct CipherParams { + int padding; + const EVP_MD* digest; + const Buffer label; + }; + + static DataPointer encrypt(const EVPKeyPointer& key, + const CipherParams& params, + const Buffer in); + static DataPointer decrypt(const EVPKeyPointer& key, + const CipherParams& params, + const Buffer in); + + static DataPointer sign(const EVPKeyPointer& key, + const CipherParams& params, + const Buffer in); + + static DataPointer recover(const EVPKeyPointer& key, + const CipherParams& params, + const Buffer in); + private: const EVP_CIPHER* cipher_ = nullptr; }; +// ============================================================================ +// RSA + +class Rsa final { + public: + Rsa(); + Rsa(OSSL3_CONST RSA* rsa); + NCRYPTO_DISALLOW_COPY_AND_MOVE(Rsa) + + inline operator bool() const { return rsa_ != nullptr; } + inline operator OSSL3_CONST RSA*() const { return rsa_; } + + struct PublicKey { + const BIGNUM* n; + const BIGNUM* e; + const BIGNUM* d; + }; + struct PrivateKey { + const BIGNUM* p; + const BIGNUM* q; + const BIGNUM* dp; + const BIGNUM* dq; + const BIGNUM* qi; + }; + struct PssParams { + std::string_view digest = "sha1"; + std::optional mgf1_digest = "sha1"; + int64_t salt_length = 20; + }; + + const PublicKey getPublicKey() const; + const PrivateKey getPrivateKey() const; + const std::optional getPssParams() const; + + bool setPublicKey(BignumPointer&& n, BignumPointer&& e); + bool setPrivateKey(BignumPointer&& d, + BignumPointer&& q, + BignumPointer&& p, + BignumPointer&& dp, + BignumPointer&& dq, + BignumPointer&& qi); + + using CipherParams = Cipher::CipherParams; + + static DataPointer encrypt(const EVPKeyPointer& key, + const CipherParams& params, + const Buffer in); + static DataPointer decrypt(const EVPKeyPointer& key, + const CipherParams& params, + const Buffer in); + + private: + OSSL3_CONST RSA* rsa_; +}; + +class Ec final { + public: + Ec(); + Ec(OSSL3_CONST EC_KEY* key); + NCRYPTO_DISALLOW_COPY_AND_MOVE(Ec) + + const EC_GROUP* getGroup() const; + int getCurve() const; + + inline operator bool() const { return ec_ != nullptr; } + inline operator OSSL3_CONST EC_KEY*() const { return ec_; } + + private: + OSSL3_CONST EC_KEY* ec_ = nullptr; +}; + // A managed pointer to a buffer of data. When destroyed the underlying // buffer will be freed. class DataPointer final { public: static DataPointer Alloc(size_t len); + static DataPointer Copy(const Buffer& buffer); DataPointer() = default; explicit DataPointer(void* data, size_t len); @@ -283,6 +389,11 @@ class DataPointer final { void reset(void* data = nullptr, size_t len = 0); void reset(const Buffer& buffer); + // Sets the underlying data buffer to all zeros. + void zero(); + + DataPointer resize(size_t len); + // Releases ownership of the underlying data buffer. It is the caller's // responsibility to ensure the buffer is appropriately freed. Buffer release(); @@ -471,6 +582,74 @@ class CipherCtxPointer final { DeleteFnPtr ctx_; }; +class EVPKeyCtxPointer final { + public: + EVPKeyCtxPointer(); + explicit EVPKeyCtxPointer(EVP_PKEY_CTX* ctx); + EVPKeyCtxPointer(EVPKeyCtxPointer&& other) noexcept; + EVPKeyCtxPointer& operator=(EVPKeyCtxPointer&& other) noexcept; + NCRYPTO_DISALLOW_COPY(EVPKeyCtxPointer) + ~EVPKeyCtxPointer(); + + inline bool operator==(std::nullptr_t) const noexcept { + return ctx_ == nullptr; + } + inline operator bool() const { return ctx_ != nullptr; } + inline EVP_PKEY_CTX* get() const { return ctx_.get(); } + void reset(EVP_PKEY_CTX* ctx = nullptr); + EVP_PKEY_CTX* release(); + + bool initForDerive(const EVPKeyPointer& peer); + DataPointer derive() const; + + bool initForParamgen(); + bool setDhParameters(int prime_size, uint32_t generator); + bool setDsaParameters(uint32_t bits, std::optional q_bits); + bool setEcParameters(int curve, int encoding); + + bool setRsaOaepMd(const EVP_MD* md); + bool setRsaMgf1Md(const EVP_MD* md); + bool setRsaPadding(int padding); + bool setRsaKeygenPubExp(BignumPointer&& e); + bool setRsaKeygenBits(int bits); + bool setRsaPssKeygenMd(const EVP_MD* md); + bool setRsaPssKeygenMgf1Md(const EVP_MD* md); + bool setRsaPssSaltlen(int salt_len); + bool setRsaImplicitRejection(); + bool setRsaOaepLabel(DataPointer&& data); + + bool setSignatureMd(const EVPMDCtxPointer& md); + + bool publicCheck() const; + bool privateCheck() const; + + bool verify(const Buffer& sig, + const Buffer& data); + DataPointer sign(const Buffer& data); + bool signInto(const Buffer& data, + Buffer* sig); + + static constexpr int kDefaultRsaExponent = 0x10001; + + static bool setRsaPadding(EVP_PKEY_CTX* ctx, + int padding, + std::optional salt_len = std::nullopt); + + EVPKeyPointer paramgen() const; + + bool initForEncrypt(); + bool initForDecrypt(); + bool initForKeygen(); + int initForVerify(); + int initForSign(); + + static EVPKeyCtxPointer New(const EVPKeyPointer& key); + static EVPKeyCtxPointer NewFromID(int id); + + private: + DeleteFnPtr ctx_; +}; + class EVPKeyPointer final { public: static EVPKeyPointer New(); @@ -478,6 +657,8 @@ class EVPKeyPointer final { const Buffer& data); static EVPKeyPointer NewRawPrivate(int id, const Buffer& data); + static EVPKeyPointer NewDH(DHPointer&& dh); + static EVPKeyPointer NewRSA(RSAPointer&& rsa); enum class PKEncodingType { // RSAPublicKey / RSAPrivateKey according to PKCS#1. @@ -578,6 +759,15 @@ class EVPKeyPointer final { static bool IsRSAPrivateKey(const Buffer& buffer); + std::optional getBytesOfRS() const; + int getDefaultSignPadding() const; + operator Rsa() const; + + bool isRsaVariant() const; + bool isOneShotVariant() const; + bool isSigVariant() const; + bool validateDsaParameters() const; + private: DeleteFnPtr pkey_; }; @@ -663,9 +853,6 @@ struct StackOfX509Deleter { }; using StackOfX509 = std::unique_ptr; -class X509Pointer; -class X509View; - class SSLCtxPointer final { public: SSLCtxPointer() = default; @@ -792,6 +979,14 @@ class X509View final { CheckMatch checkEmail(const std::string_view email, int flags) const; CheckMatch checkIp(const std::string_view ip, int flags) const; + using UsageCallback = std::function; + bool enumUsages(UsageCallback callback) const; + + template + using KeyCallback = std::function; + bool ifRsa(KeyCallback callback) const; + bool ifEc(KeyCallback callback) const; + private: const X509* cert_ = nullptr; }; @@ -948,6 +1143,77 @@ class ECKeyPointer final { DeleteFnPtr key_; }; +class EVPMDCtxPointer final { + public: + EVPMDCtxPointer(); + explicit EVPMDCtxPointer(EVP_MD_CTX* ctx); + EVPMDCtxPointer(EVPMDCtxPointer&& other) noexcept; + EVPMDCtxPointer& operator=(EVPMDCtxPointer&& other) noexcept; + NCRYPTO_DISALLOW_COPY(EVPMDCtxPointer) + ~EVPMDCtxPointer(); + + inline bool operator==(std::nullptr_t) noexcept { return ctx_ == nullptr; } + inline operator bool() const { return ctx_ != nullptr; } + inline EVP_MD_CTX* get() const { return ctx_.get(); } + inline operator EVP_MD_CTX*() const { return ctx_.get(); } + void reset(EVP_MD_CTX* ctx = nullptr); + EVP_MD_CTX* release(); + + bool digestInit(const EVP_MD* digest); + bool digestUpdate(const Buffer& in); + DataPointer digestFinal(size_t length); + bool digestFinalInto(Buffer* buf); + size_t getExpectedSize(); + + std::optional signInit(const EVPKeyPointer& key, + const EVP_MD* digest); + std::optional verifyInit(const EVPKeyPointer& key, + const EVP_MD* digest); + + DataPointer signOneShot(const Buffer& buf) const; + DataPointer sign(const Buffer& buf) const; + bool verify(const Buffer& buf, + const Buffer& sig) const; + + const EVP_MD* getDigest() const; + size_t getDigestSize() const; + bool hasXofFlag() const; + + bool copyTo(const EVPMDCtxPointer& other) const; + + static EVPMDCtxPointer New(); + + private: + DeleteFnPtr ctx_; +}; + +class HMACCtxPointer final { + public: + HMACCtxPointer(); + explicit HMACCtxPointer(HMAC_CTX* ctx); + HMACCtxPointer(HMACCtxPointer&& other) noexcept; + HMACCtxPointer& operator=(HMACCtxPointer&& other) noexcept; + NCRYPTO_DISALLOW_COPY(HMACCtxPointer) + ~HMACCtxPointer(); + + inline bool operator==(std::nullptr_t) noexcept { return ctx_ == nullptr; } + inline operator bool() const { return ctx_ != nullptr; } + inline HMAC_CTX* get() const { return ctx_.get(); } + inline operator HMAC_CTX*() const { return ctx_.get(); } + void reset(HMAC_CTX* ctx = nullptr); + HMAC_CTX* release(); + + bool init(const Buffer& buf, const EVP_MD* md); + bool update(const Buffer& buf); + DataPointer digest(); + bool digestInto(Buffer* buf); + + static HMACCtxPointer New(); + + private: + DeleteFnPtr ctx_; +}; + #ifndef OPENSSL_NO_ENGINE class EnginePointer final { public: @@ -1025,12 +1291,17 @@ Buffer ExportChallenge(const char* input, size_t length); // KDF const EVP_MD* getDigestByName(const std::string_view name); +const EVP_CIPHER* getCipherByName(const std::string_view name); // Verify that the specified HKDF output length is valid for the given digest. // The maximum length for HKDF output for a given digest is 255 times the // hash size for the given digest algorithm. bool checkHkdfLength(const EVP_MD* md, size_t length); +bool extractP1363(const Buffer& buf, + unsigned char* dest, + size_t n); + DataPointer hkdf(const EVP_MD* md, const Buffer& key, const Buffer& info, diff --git a/src/crypto/crypto_cipher.cc b/src/crypto/crypto_cipher.cc index 61dd1e97d9672a..dca59f16723ef8 100644 --- a/src/crypto/crypto_cipher.cc +++ b/src/crypto/crypto_cipher.cc @@ -20,6 +20,7 @@ using ncrypto::SSLPointer; using v8::Array; using v8::ArrayBuffer; using v8::BackingStore; +using v8::BackingStoreInitializationMode; using v8::Context; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; @@ -244,26 +245,22 @@ void CipherBase::Initialize(Environment* env, Local target) { target, "publicEncrypt", PublicKeyCipher::Cipher); + ncrypto::Cipher::encrypt>); SetMethod(context, target, "privateDecrypt", PublicKeyCipher::Cipher); + ncrypto::Cipher::decrypt>); SetMethod(context, target, "privateEncrypt", PublicKeyCipher::Cipher); + ncrypto::Cipher::sign>); SetMethod(context, target, "publicDecrypt", PublicKeyCipher::Cipher); + ncrypto::Cipher::recover>); SetMethodNoSideEffect(context, target, "getCipherInfo", GetCipherInfo); @@ -288,17 +285,13 @@ void CipherBase::RegisterExternalReferences( registry->Register(GetCiphers); registry->Register(PublicKeyCipher::Cipher); + ncrypto::Cipher::encrypt>); registry->Register(PublicKeyCipher::Cipher); + ncrypto::Cipher::decrypt>); registry->Register(PublicKeyCipher::Cipher); + ncrypto::Cipher::sign>); registry->Register(PublicKeyCipher::Cipher); + ncrypto::Cipher::recover>); registry->Register(GetCipherInfo); } @@ -773,10 +766,10 @@ CipherBase::UpdateResult CipherBase::Update( return kErrorState; } - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env()->isolate_data()); - *out = ArrayBuffer::NewBackingStore(env()->isolate(), buf_len); - } + *out = ArrayBuffer::NewBackingStore( + env()->isolate(), + buf_len, + BackingStoreInitializationMode::kUninitialized); buffer = { .data = reinterpret_cast(data), @@ -853,11 +846,10 @@ bool CipherBase::Final(std::unique_ptr* out) { const int mode = ctx_.getMode(); - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env()->isolate_data()); - *out = ArrayBuffer::NewBackingStore( - env()->isolate(), static_cast(ctx_.getBlockSize())); - } + *out = ArrayBuffer::NewBackingStore( + env()->isolate(), + static_cast(ctx_.getBlockSize()), + BackingStoreInitializationMode::kUninitialized); if (kind_ == kDecipher && Cipher::FromCtx(ctx_).isSupportedAuthenticatedMode()) { @@ -939,9 +931,7 @@ void CipherBase::Final(const FunctionCallbackInfo& args) { Buffer::New(env, ab, 0, ab->ByteLength()).FromMaybe(Local())); } -template +template bool PublicKeyCipher::Cipher( Environment* env, const EVPKeyPointer& pkey, @@ -950,62 +940,32 @@ bool PublicKeyCipher::Cipher( const ArrayBufferOrViewContents& oaep_label, const ArrayBufferOrViewContents& data, std::unique_ptr* out) { - EVPKeyCtxPointer ctx = pkey.newCtx(); - if (!ctx) - return false; - if (EVP_PKEY_cipher_init(ctx.get()) <= 0) - return false; - if (EVP_PKEY_CTX_set_rsa_padding(ctx.get(), padding) <= 0) - return false; - - if (digest != nullptr) { - if (EVP_PKEY_CTX_set_rsa_oaep_md(ctx.get(), digest) <= 0) - return false; - } - - if (!SetRsaOaepLabel(ctx, oaep_label.ToByteSource())) return false; + auto label = oaep_label.ToByteSource(); + auto in = data.ToByteSource(); - size_t out_len = 0; - if (EVP_PKEY_cipher( - ctx.get(), - nullptr, - &out_len, - data.data(), - data.size()) <= 0) { - return false; - } - - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - *out = ArrayBuffer::NewBackingStore(env->isolate(), out_len); - } + const ncrypto::Cipher::CipherParams params{ + .padding = padding, + .digest = digest, + .label = label, + }; - if (EVP_PKEY_cipher( - ctx.get(), - static_cast((*out)->Data()), - &out_len, - data.data(), - data.size()) <= 0) { - return false; - } + auto buf = cipher(pkey, params, in); + if (!buf) return false; - CHECK_LE(out_len, (*out)->ByteLength()); - if (out_len == 0) { + if (buf.size() == 0) { *out = ArrayBuffer::NewBackingStore(env->isolate(), 0); - } else if (out_len != (*out)->ByteLength()) { - std::unique_ptr old_out = std::move(*out); - *out = ArrayBuffer::NewBackingStore(env->isolate(), out_len); + } else { + *out = ArrayBuffer::NewBackingStore(env->isolate(), buf.size()); memcpy(static_cast((*out)->Data()), - static_cast(old_out->Data()), - out_len); + static_cast(buf.get()), + buf.size()); } return true; } template + PublicKeyCipher::Cipher_t cipher> void PublicKeyCipher::Cipher(const FunctionCallbackInfo& args) { MarkPopErrorOnReturn mark_pop_error_on_return; Environment* env = Environment::GetCurrent(args); @@ -1024,25 +984,16 @@ void PublicKeyCipher::Cipher(const FunctionCallbackInfo& args) { uint32_t padding; if (!args[offset + 1]->Uint32Value(env->context()).To(&padding)) return; - if (EVP_PKEY_cipher == EVP_PKEY_decrypt && + if (cipher == ncrypto::Cipher::decrypt && operation == PublicKeyCipher::kPrivate && padding == RSA_PKCS1_PADDING) { EVPKeyCtxPointer ctx = pkey.newCtx(); CHECK(ctx); - if (EVP_PKEY_decrypt_init(ctx.get()) <= 0) { + if (!ctx.initForDecrypt()) { return ThrowCryptoError(env, ERR_get_error()); } - int rsa_pkcs1_implicit_rejection = - EVP_PKEY_CTX_ctrl_str(ctx.get(), "rsa_pkcs1_implicit_rejection", "1"); - // From the doc -2 means that the option is not supported. - // The default for the option is enabled and if it has been - // specifically disabled we want to respect that so we will - // not throw an error if the option is supported regardless - // of how it is set. The call to set the value - // will not affect what is used since a different context is - // used in the call if the option is supported - if (rsa_pkcs1_implicit_rejection <= 0) { + if (!ctx.setRsaImplicitRejection()) { return THROW_ERR_INVALID_ARG_VALUE( env, "RSA_PKCS1_PADDING is no longer supported for private decryption"); @@ -1052,7 +1003,7 @@ void PublicKeyCipher::Cipher(const FunctionCallbackInfo& args) { const EVP_MD* digest = nullptr; if (args[offset + 2]->IsString()) { const Utf8Value oaep_str(env->isolate(), args[offset + 2]); - digest = EVP_get_digestbyname(*oaep_str); + digest = ncrypto::getDigestByName(oaep_str.ToStringView()); if (digest == nullptr) return THROW_ERR_OSSL_EVP_INVALID_DIGEST(env); } @@ -1063,8 +1014,7 @@ void PublicKeyCipher::Cipher(const FunctionCallbackInfo& args) { return THROW_ERR_OUT_OF_RANGE(env, "oaepLabel is too big"); } std::unique_ptr out; - if (!Cipher( - env, pkey, padding, digest, oaep_label, buf, &out)) { + if (!Cipher(env, pkey, padding, digest, oaep_label, buf, &out)) { return ThrowCryptoError(env, ERR_get_error()); } diff --git a/src/crypto/crypto_cipher.h b/src/crypto/crypto_cipher.h index 57c424e7509fa2..950acfa2521ede 100644 --- a/src/crypto/crypto_cipher.h +++ b/src/crypto/crypto_cipher.h @@ -96,19 +96,17 @@ class CipherBase : public BaseObject { class PublicKeyCipher { public: - typedef int (*EVP_PKEY_cipher_init_t)(EVP_PKEY_CTX* ctx); - typedef int (*EVP_PKEY_cipher_t)(EVP_PKEY_CTX* ctx, - unsigned char* out, size_t* outlen, - const unsigned char* in, size_t inlen); + using Cipher_t = + ncrypto::DataPointer(const ncrypto::EVPKeyPointer&, + const ncrypto::Cipher::CipherParams& params, + const ncrypto::Buffer); enum Operation { kPublic, kPrivate }; - template + template static bool Cipher(Environment* env, const ncrypto::EVPKeyPointer& pkey, int padding, @@ -117,9 +115,7 @@ class PublicKeyCipher { const ArrayBufferOrViewContents& data, std::unique_ptr* out); - template + template static void Cipher(const v8::FunctionCallbackInfo& args); }; diff --git a/src/crypto/crypto_common.cc b/src/crypto/crypto_common.cc index d94f6e1c82c4a6..591509e735b943 100644 --- a/src/crypto/crypto_common.cc +++ b/src/crypto/crypto_common.cc @@ -36,7 +36,7 @@ using ncrypto::StackOfX509; using ncrypto::X509Pointer; using ncrypto::X509View; using v8::ArrayBuffer; -using v8::BackingStore; +using v8::BackingStoreInitializationMode; using v8::Context; using v8::EscapableHandleScope; using v8::Integer; @@ -307,11 +307,8 @@ MaybeLocal ECPointToBuffer(Environment* env, return MaybeLocal(); } - std::unique_ptr bs; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - bs = ArrayBuffer::NewBackingStore(env->isolate(), len); - } + auto bs = ArrayBuffer::NewBackingStore( + env->isolate(), len, BackingStoreInitializationMode::kUninitialized); len = EC_POINT_point2oct(group, point, diff --git a/src/crypto/crypto_context.cc b/src/crypto/crypto_context.cc index c7574e67f03f03..da5cebb87a3d51 100644 --- a/src/crypto/crypto_context.cc +++ b/src/crypto/crypto_context.cc @@ -1451,7 +1451,7 @@ void SecureContext::GetCertificate(const FunctionCallbackInfo& args) { } // UseExtraCaCerts is called only once at the start of the Node.js process. -void UseExtraCaCerts(const std::string& file) { +void UseExtraCaCerts(std::string_view file) { extra_root_certs_file = file; } diff --git a/src/crypto/crypto_dh.cc b/src/crypto/crypto_dh.cc index 7041eb985d9f6d..0e25a937e175fa 100644 --- a/src/crypto/crypto_dh.cc +++ b/src/crypto/crypto_dh.cc @@ -397,31 +397,23 @@ EVPKeyCtxPointer DhKeyGenTraits::Setup(DhKeyPairGenConfig* params) { auto dh = DHPointer::New(std::move(prime), std::move(bn_g)); if (!dh) return {}; - key_params = EVPKeyPointer::New(); - CHECK(key_params); - CHECK_EQ(EVP_PKEY_assign_DH(key_params.get(), dh.release()), 1); + key_params = EVPKeyPointer::NewDH(std::move(dh)); } else if (int* prime_size = std::get_if(¶ms->params.prime)) { - EVPKeyCtxPointer param_ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_DH, nullptr)); - EVP_PKEY* raw_params = nullptr; - if (!param_ctx || - EVP_PKEY_paramgen_init(param_ctx.get()) <= 0 || - EVP_PKEY_CTX_set_dh_paramgen_prime_len( - param_ctx.get(), - *prime_size) <= 0 || - EVP_PKEY_CTX_set_dh_paramgen_generator( - param_ctx.get(), - params->params.generator) <= 0 || - EVP_PKEY_paramgen(param_ctx.get(), &raw_params) <= 0) { + auto param_ctx = EVPKeyCtxPointer::NewFromID(EVP_PKEY_DH); + if (!param_ctx.initForParamgen() || + !param_ctx.setDhParameters(*prime_size, params->params.generator)) { return {}; } - key_params = EVPKeyPointer(raw_params); + key_params = param_ctx.paramgen(); } else { UNREACHABLE(); } + if (!key_params) return {}; + EVPKeyCtxPointer ctx = key_params.newCtx(); - if (!ctx || EVP_PKEY_keygen_init(ctx.get()) <= 0) return {}; + if (!ctx.initForKeygen()) return {}; return ctx; } diff --git a/src/crypto/crypto_dsa.cc b/src/crypto/crypto_dsa.cc index 471fee77531139..cac05aa55f8dd2 100644 --- a/src/crypto/crypto_dsa.cc +++ b/src/crypto/crypto_dsa.cc @@ -12,22 +12,10 @@ #include -// EVP_PKEY_CTX_set_dsa_paramgen_q_bits was added in OpenSSL 1.1.1e. -#if OPENSSL_VERSION_NUMBER < 0x1010105fL -#define EVP_PKEY_CTX_set_dsa_paramgen_q_bits(ctx, qbits) \ - EVP_PKEY_CTX_ctrl((ctx), \ - EVP_PKEY_DSA, \ - EVP_PKEY_OP_PARAMGEN, \ - EVP_PKEY_CTRL_DSA_PARAMGEN_Q_BITS, \ - (qbits), \ - nullptr) -#endif - namespace node { using ncrypto::BignumPointer; using ncrypto::EVPKeyCtxPointer; -using ncrypto::EVPKeyPointer; using v8::FunctionCallbackInfo; using v8::Int32; using v8::JustVoid; @@ -41,33 +29,22 @@ using v8::Value; namespace crypto { EVPKeyCtxPointer DsaKeyGenTraits::Setup(DsaKeyPairGenConfig* params) { - EVPKeyCtxPointer param_ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_DSA, nullptr)); - EVP_PKEY* raw_params = nullptr; - - if (!param_ctx || - EVP_PKEY_paramgen_init(param_ctx.get()) <= 0 || - EVP_PKEY_CTX_set_dsa_paramgen_bits( - param_ctx.get(), - params->params.modulus_bits) <= 0) { - return EVPKeyCtxPointer(); - } - - if (params->params.divisor_bits != -1) { - if (EVP_PKEY_CTX_set_dsa_paramgen_q_bits( - param_ctx.get(), params->params.divisor_bits) <= 0) { - return EVPKeyCtxPointer(); - } + auto param_ctx = EVPKeyCtxPointer::NewFromID(EVP_PKEY_DSA); + + if (!param_ctx.initForParamgen() || + !param_ctx.setDsaParameters( + params->params.modulus_bits, + params->params.divisor_bits != -1 + ? std::optional(params->params.divisor_bits) + : std::nullopt)) { + return {}; } - if (EVP_PKEY_paramgen(param_ctx.get(), &raw_params) <= 0) - return EVPKeyCtxPointer(); + auto key_params = param_ctx.paramgen(); + if (!key_params) return {}; - EVPKeyPointer key_params(raw_params); EVPKeyCtxPointer key_ctx = key_params.newCtx(); - - if (!key_ctx || EVP_PKEY_keygen_init(key_ctx.get()) <= 0) - return EVPKeyCtxPointer(); - + if (!key_ctx.initForKeygen()) return {}; return key_ctx; } diff --git a/src/crypto/crypto_ec.cc b/src/crypto/crypto_ec.cc index 5ccda6f0768873..98f1e1312769ca 100644 --- a/src/crypto/crypto_ec.cc +++ b/src/crypto/crypto_ec.cc @@ -28,7 +28,7 @@ using ncrypto::EVPKeyPointer; using ncrypto::MarkPopErrorOnReturn; using v8::Array; using v8::ArrayBuffer; -using v8::BackingStore; +using v8::BackingStoreInitializationMode; using v8::Context; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; @@ -201,14 +201,10 @@ void ECDH::ComputeSecret(const FunctionCallbackInfo& args) { return; } - std::unique_ptr bs; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - // NOTE: field_size is in bits - int field_size = EC_GROUP_get_degree(ecdh->group_); - size_t out_len = (field_size + 7) / 8; - bs = ArrayBuffer::NewBackingStore(env->isolate(), out_len); - } + int field_size = EC_GROUP_get_degree(ecdh->group_); + size_t out_len = (field_size + 7) / 8; + auto bs = ArrayBuffer::NewBackingStore( + env->isolate(), out_len, BackingStoreInitializationMode::kUninitialized); if (!ECDH_compute_key( bs->Data(), bs->ByteLength(), pub, ecdh->key_.get(), nullptr)) @@ -257,12 +253,11 @@ void ECDH::GetPrivateKey(const FunctionCallbackInfo& args) { return THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to get ECDH private key"); - std::unique_ptr bs; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - bs = ArrayBuffer::NewBackingStore(env->isolate(), - BignumPointer::GetByteCount(b)); - } + auto bs = ArrayBuffer::NewBackingStore( + env->isolate(), + BignumPointer::GetByteCount(b), + BackingStoreInitializationMode::kUninitialized); + CHECK_EQ(bs->ByteLength(), BignumPointer::EncodePaddedInto( b, static_cast(bs->Data()), bs->ByteLength())); @@ -459,24 +454,14 @@ bool ECDHBitsTraits::DeriveBits(Environment* env, case EVP_PKEY_X25519: // Fall through case EVP_PKEY_X448: { - EVPKeyCtxPointer ctx = m_privkey.newCtx(); Mutex::ScopedLock pub_lock(params.public_.mutex()); - if (EVP_PKEY_derive_init(ctx.get()) <= 0 || - EVP_PKEY_derive_set_peer( - ctx.get(), - m_pubkey.get()) <= 0 || - EVP_PKEY_derive(ctx.get(), nullptr, &len) <= 0) { - return false; - } - - ByteSource::Builder buf(len); - - if (EVP_PKEY_derive(ctx.get(), buf.data(), &len) <= 0) { - return false; - } + EVPKeyCtxPointer ctx = m_privkey.newCtx(); + if (!ctx.initForDerive(m_pubkey)) return false; - *out = std::move(buf).release(len); + auto data = ctx.derive(); + if (!data) return false; + *out = ByteSource::Allocated(data.release()); break; } default: { @@ -523,28 +508,24 @@ EVPKeyCtxPointer EcKeyGenTraits::Setup(EcKeyPairGenConfig* params) { case EVP_PKEY_X25519: // Fall through case EVP_PKEY_X448: - key_ctx.reset(EVP_PKEY_CTX_new_id(params->params.curve_nid, nullptr)); + key_ctx = EVPKeyCtxPointer::NewFromID(params->params.curve_nid); break; default: { - EVPKeyCtxPointer param_ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr)); - EVP_PKEY* raw_params = nullptr; - if (!param_ctx || - EVP_PKEY_paramgen_init(param_ctx.get()) <= 0 || - EVP_PKEY_CTX_set_ec_paramgen_curve_nid( - param_ctx.get(), params->params.curve_nid) <= 0 || - EVP_PKEY_CTX_set_ec_param_enc( - param_ctx.get(), params->params.param_encoding) <= 0 || - EVP_PKEY_paramgen(param_ctx.get(), &raw_params) <= 0) { - return EVPKeyCtxPointer(); + auto param_ctx = EVPKeyCtxPointer::NewFromID(EVP_PKEY_EC); + if (!param_ctx.initForParamgen() || + !param_ctx.setEcParameters(params->params.curve_nid, + params->params.param_encoding)) { + return {}; } - EVPKeyPointer key_params(raw_params); + + auto key_params = param_ctx.paramgen(); + if (!key_params) return {}; + key_ctx = key_params.newCtx(); } } - if (key_ctx && EVP_PKEY_keygen_init(key_ctx.get()) <= 0) - key_ctx.reset(); - + if (!key_ctx.initForKeygen()) return {}; return key_ctx; } diff --git a/src/crypto/crypto_hash.cc b/src/crypto/crypto_hash.cc index bcd4c533b07ceb..851847483327c1 100644 --- a/src/crypto/crypto_hash.cc +++ b/src/crypto/crypto_hash.cc @@ -11,6 +11,7 @@ namespace node { +using ncrypto::DataPointer; using ncrypto::EVPMDCtxPointer; using ncrypto::MarkPopErrorOnReturn; using v8::Context; @@ -59,7 +60,7 @@ struct MaybeCachedMD { }; MaybeCachedMD FetchAndMaybeCacheMD(Environment* env, const char* search_name) { - const EVP_MD* implicit_md = EVP_get_digestbyname(search_name); + const EVP_MD* implicit_md = ncrypto::getDigestByName(search_name); if (!implicit_md) return {nullptr, nullptr, -1}; const char* real_name = EVP_MD_get0_name(implicit_md); @@ -202,7 +203,7 @@ const EVP_MD* GetDigestImplementation(Environment* env, return result.explicit_md ? result.explicit_md : result.implicit_md; #else Utf8Value utf8(env->isolate(), algorithm); - return EVP_get_digestbyname(*utf8); + return ncrypto::getDigestByName(utf8.ToStringView()); #endif } @@ -220,7 +221,7 @@ void Hash::OneShotDigest(const FunctionCallbackInfo& args) { CHECK(args[5]->IsUint32() || args[5]->IsUndefined()); // outputEncodingId const EVP_MD* md = GetDigestImplementation(env, args[0], args[1], args[2]); - if (md == nullptr) { + if (md == nullptr) [[unlikely]] { Utf8Value method(isolate, args[0]); std::string message = "Digest method " + method.ToString() + " is not supported"; @@ -229,41 +230,36 @@ void Hash::OneShotDigest(const FunctionCallbackInfo& args) { enum encoding output_enc = ParseEncoding(isolate, args[4], args[5], HEX); - int md_len = EVP_MD_size(md); - unsigned int result_size; - ByteSource::Builder output(md_len); - int success; - // On smaller inputs, EVP_Digest() can be slower than the - // deprecated helpers e.g SHA256_XXX. The speedup may not - // be worth using deprecated APIs, however, so we use - // EVP_Digest(), unless there's a better alternative - // in the future. - // https://github.com/openssl/openssl/issues/19612 - if (args[3]->IsString()) { - Utf8Value utf8(isolate, args[3]); - success = EVP_Digest(utf8.out(), - utf8.length(), - output.data(), - &result_size, - md, - nullptr); - } else { + DataPointer output = ([&] { + if (args[3]->IsString()) { + Utf8Value utf8(isolate, args[3]); + ncrypto::Buffer buf{ + .data = reinterpret_cast(utf8.out()), + .len = utf8.length(), + }; + return ncrypto::hashDigest(buf, md); + } + ArrayBufferViewContents input(args[3]); - success = EVP_Digest(input.data(), - input.length(), - output.data(), - &result_size, - md, - nullptr); - } - if (!success) { + ncrypto::Buffer buf{ + .data = reinterpret_cast(input.data()), + .len = input.length(), + }; + return ncrypto::hashDigest(buf, md); + })(); + + if (!output) [[unlikely]] { return ThrowCryptoError(env, ERR_get_error()); } Local error; - MaybeLocal rc = StringBytes::Encode( - env->isolate(), output.data(), md_len, output_enc, &error); - if (rc.IsEmpty()) { + MaybeLocal rc = + StringBytes::Encode(env->isolate(), + static_cast(output.get()), + output.size(), + output_enc, + &error); + if (rc.IsEmpty()) [[unlikely]] { CHECK(!error.IsEmpty()); env->isolate()->ThrowException(error); return; @@ -314,7 +310,8 @@ void Hash::New(const FunctionCallbackInfo& args) { const EVP_MD* md = nullptr; if (args[0]->IsObject()) { ASSIGN_OR_RETURN_UNWRAP(&orig, args[0].As()); - md = EVP_MD_CTX_md(orig->mdctx_.get()); + CHECK_NOT_NULL(orig); + md = orig->mdctx_.getDigest(); } else { md = GetDigestImplementation(env, args[0], args[2], args[3]); } @@ -331,25 +328,25 @@ void Hash::New(const FunctionCallbackInfo& args) { "Digest method not supported"); } - if (orig != nullptr && - 0 >= EVP_MD_CTX_copy(hash->mdctx_.get(), orig->mdctx_.get())) { + if (orig != nullptr && !orig->mdctx_.copyTo(hash->mdctx_)) { return ThrowCryptoError(env, ERR_get_error(), "Digest copy error"); } } bool Hash::HashInit(const EVP_MD* md, Maybe xof_md_len) { - mdctx_.reset(EVP_MD_CTX_new()); - if (!mdctx_ || EVP_DigestInit_ex(mdctx_.get(), md, nullptr) <= 0) { + mdctx_ = EVPMDCtxPointer::New(); + if (!mdctx_.digestInit(md)) [[unlikely]] { mdctx_.reset(); return false; } - md_len_ = EVP_MD_size(md); + md_len_ = mdctx_.getDigestSize(); if (xof_md_len.IsJust() && xof_md_len.FromJust() != md_len_) { // This is a little hack to cause createHash to fail when an incorrect // hashSize option was passed for a non-XOF hash function. - if ((EVP_MD_flags(md) & EVP_MD_FLAG_XOF) == 0) { + if (!mdctx_.hasXofFlag()) [[unlikely]] { EVPerr(EVP_F_EVP_DIGESTFINALXOF, EVP_R_NOT_XOF_OR_INVALID_LENGTH); + mdctx_.reset(); return false; } md_len_ = xof_md_len.FromJust(); @@ -359,9 +356,11 @@ bool Hash::HashInit(const EVP_MD* md, Maybe xof_md_len) { } bool Hash::HashUpdate(const char* data, size_t len) { - if (!mdctx_) - return false; - return EVP_DigestUpdate(mdctx_.get(), data, len) == 1; + if (!mdctx_) return false; + return mdctx_.digestUpdate(ncrypto::Buffer{ + .data = data, + .len = len, + }); } void Hash::HashUpdate(const FunctionCallbackInfo& args) { @@ -402,31 +401,18 @@ void Hash::HashDigest(const FunctionCallbackInfo& args) { // and Hash.digest can both be used to retrieve the digest, // so we need to cache it. // See https://github.com/nodejs/node/issues/28245. - - ByteSource::Builder digest(len); - - size_t default_len = EVP_MD_CTX_size(hash->mdctx_.get()); - int ret; - if (len == default_len) { - ret = EVP_DigestFinal_ex( - hash->mdctx_.get(), digest.data(), &len); - // The output length should always equal hash->md_len_ - CHECK_EQ(len, hash->md_len_); - } else { - ret = EVP_DigestFinalXOF( - hash->mdctx_.get(), digest.data(), len); - } - - if (ret != 1) + auto data = hash->mdctx_.digestFinal(len); + if (!data) [[unlikely]] { return ThrowCryptoError(env, ERR_get_error()); + } - hash->digest_ = std::move(digest).release(); + hash->digest_ = ByteSource::Allocated(data.release()); } Local error; MaybeLocal rc = StringBytes::Encode( env->isolate(), hash->digest_.data(), len, encoding, &error); - if (rc.IsEmpty()) { + if (rc.IsEmpty()) [[unlikely]] { CHECK(!error.IsEmpty()); env->isolate()->ThrowException(error); return; @@ -469,7 +455,7 @@ Maybe HashTraits::AdditionalConfig( CHECK(args[offset]->IsString()); // Hash algorithm Utf8Value digest(env->isolate(), args[offset]); - params->digest = EVP_get_digestbyname(*digest); + params->digest = ncrypto::getDigestByName(digest.ToStringView()); if (params->digest == nullptr) [[unlikely]] { THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *digest); return Nothing(); @@ -492,7 +478,7 @@ Maybe HashTraits::AdditionalConfig( static_cast(args[offset + 2] .As()->Value()) / CHAR_BIT; if (params->length != expected) { - if ((EVP_MD_flags(params->digest) & EVP_MD_FLAG_XOF) == 0) { + if ((EVP_MD_flags(params->digest) & EVP_MD_FLAG_XOF) == 0) [[unlikely]] { THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Digest method not supported"); return Nothing(); } @@ -506,29 +492,19 @@ bool HashTraits::DeriveBits( Environment* env, const HashConfig& params, ByteSource* out) { - EVPMDCtxPointer ctx(EVP_MD_CTX_new()); + auto ctx = EVPMDCtxPointer::New(); - if (!ctx || EVP_DigestInit_ex(ctx.get(), params.digest, nullptr) <= 0 || - EVP_DigestUpdate(ctx.get(), params.in.data(), params.in.size()) <= - 0) [[unlikely]] { + if (!ctx.digestInit(params.digest) || !ctx.digestUpdate(params.in)) + [[unlikely]] { return false; } if (params.length > 0) [[likely]] { - unsigned int length = params.length; - ByteSource::Builder buf(length); - - size_t expected = EVP_MD_CTX_size(ctx.get()); - - int ret = - (length == expected) - ? EVP_DigestFinal_ex(ctx.get(), buf.data(), &length) - : EVP_DigestFinalXOF(ctx.get(), buf.data(), length); - - if (ret != 1) [[unlikely]] + auto data = ctx.digestFinal(params.length); + if (!data) [[unlikely]] return false; - *out = std::move(buf).release(); + *out = ByteSource::Allocated(data.release()); } return true; @@ -548,7 +524,7 @@ void InternalVerifyIntegrity(const v8::FunctionCallbackInfo& args) { CHECK(args[2]->IsArrayBufferView()); ArrayBufferOrViewContents expected(args[2]); - const EVP_MD* md_type = EVP_get_digestbyname(*algorithm); + const EVP_MD* md_type = ncrypto::getDigestByName(algorithm.ToStringView()); unsigned char digest[EVP_MAX_MD_SIZE]; unsigned int digest_size; if (md_type == nullptr || EVP_Digest(content.data(), @@ -556,7 +532,7 @@ void InternalVerifyIntegrity(const v8::FunctionCallbackInfo& args) { digest, &digest_size, md_type, - nullptr) != 1) { + nullptr) != 1) [[unlikely]] { return ThrowCryptoError( env, ERR_get_error(), "Digest method not supported"); } @@ -570,7 +546,7 @@ void InternalVerifyIntegrity(const v8::FunctionCallbackInfo& args) { digest_size, BASE64, &error); - if (rc.IsEmpty()) { + if (rc.IsEmpty()) [[unlikely]] { CHECK(!error.IsEmpty()); env->isolate()->ThrowException(error); return; diff --git a/src/crypto/crypto_hkdf.cc b/src/crypto/crypto_hkdf.cc index 2a465c849def44..10bb8e4258bf63 100644 --- a/src/crypto/crypto_hkdf.cc +++ b/src/crypto/crypto_hkdf.cc @@ -55,7 +55,7 @@ Maybe HKDFTraits::AdditionalConfig( Utf8Value hash(env->isolate(), args[offset]); params->digest = ncrypto::getDigestByName(hash.ToStringView()); - if (params->digest == nullptr) { + if (params->digest == nullptr) [[unlikely]] { THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *hash); return Nothing(); } @@ -88,7 +88,7 @@ Maybe HKDFTraits::AdditionalConfig( // HKDF-Expand computes up to 255 HMAC blocks, each having as many bits as the // output of the hash function. 255 is a hard limit because HKDF appends an // 8-bit counter to each HMAC'd message, starting at 1. - if (!ncrypto::checkHkdfLength(params->digest, params->length)) { + if (!ncrypto::checkHkdfLength(params->digest, params->length)) [[unlikely]] { THROW_ERR_CRYPTO_INVALID_KEYLEN(env); return Nothing(); } diff --git a/src/crypto/crypto_hmac.cc b/src/crypto/crypto_hmac.cc index 25ccb1b9d04e51..56a09e1a2d9b0f 100644 --- a/src/crypto/crypto_hmac.cc +++ b/src/crypto/crypto_hmac.cc @@ -70,15 +70,21 @@ void Hmac::New(const FunctionCallbackInfo& args) { void Hmac::HmacInit(const char* hash_type, const char* key, int key_len) { HandleScope scope(env()->isolate()); - const EVP_MD* md = EVP_get_digestbyname(hash_type); - if (md == nullptr) + const EVP_MD* md = ncrypto::getDigestByName(hash_type); + if (md == nullptr) [[unlikely]] { return THROW_ERR_CRYPTO_INVALID_DIGEST( env(), "Invalid digest: %s", hash_type); + } if (key_len == 0) { key = ""; } - ctx_.reset(HMAC_CTX_new()); - if (!ctx_ || !HMAC_Init_ex(ctx_.get(), key, key_len, md, nullptr)) { + + ctx_ = HMACCtxPointer::New(); + ncrypto::Buffer key_buf{ + .data = key, + .len = static_cast(key_len), + }; + if (!ctx_.init(key_buf, md)) [[unlikely]] { ctx_.reset(); return ThrowCryptoError(env(), ERR_get_error()); } @@ -95,9 +101,11 @@ void Hmac::HmacInit(const FunctionCallbackInfo& args) { } bool Hmac::HmacUpdate(const char* data, size_t len) { - return ctx_ && HMAC_Update(ctx_.get(), - reinterpret_cast(data), - len) == 1; + ncrypto::Buffer buf{ + .data = data, + .len = len, + }; + return ctx_.update(buf); } void Hmac::HmacUpdate(const FunctionCallbackInfo& args) { @@ -123,24 +131,27 @@ void Hmac::HmacDigest(const FunctionCallbackInfo& args) { } unsigned char md_value[EVP_MAX_MD_SIZE]; - unsigned int md_len = 0; + ncrypto::Buffer buf{ + .data = md_value, + .len = sizeof(md_value), + }; if (hmac->ctx_) { - bool ok = HMAC_Final(hmac->ctx_.get(), md_value, &md_len); - hmac->ctx_.reset(); - if (!ok) { + if (!hmac->ctx_.digestInto(&buf)) [[unlikely]] { + hmac->ctx_.reset(); return ThrowCryptoError(env, ERR_get_error(), "Failed to finalize HMAC"); } + hmac->ctx_.reset(); } Local error; MaybeLocal rc = StringBytes::Encode(env->isolate(), reinterpret_cast(md_value), - md_len, + buf.len, encoding, &error); - if (rc.IsEmpty()) { + if (rc.IsEmpty()) [[unlikely]] { CHECK(!error.IsEmpty()); env->isolate()->ThrowException(error); return; @@ -188,8 +199,8 @@ Maybe HmacTraits::AdditionalConfig( CHECK(args[offset + 2]->IsObject()); // Key Utf8Value digest(env->isolate(), args[offset + 1]); - params->digest = EVP_get_digestbyname(*digest); - if (params->digest == nullptr) { + params->digest = ncrypto::getDigestByName(digest.ToStringView()); + if (params->digest == nullptr) [[unlikely]] { THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *digest); return Nothing(); } @@ -225,31 +236,29 @@ bool HmacTraits::DeriveBits( Environment* env, const HmacConfig& params, ByteSource* out) { - HMACCtxPointer ctx(HMAC_CTX_new()); + auto ctx = HMACCtxPointer::New(); - if (!ctx || !HMAC_Init_ex(ctx.get(), - params.key.GetSymmetricKey(), - params.key.GetSymmetricKeySize(), - params.digest, - nullptr)) { + ncrypto::Buffer key_buf{ + .data = params.key.GetSymmetricKey(), + .len = params.key.GetSymmetricKeySize(), + }; + if (!ctx.init(key_buf, params.digest)) [[unlikely]] { return false; } - if (!HMAC_Update( - ctx.get(), - params.data.data(), - params.data.size())) { + ncrypto::Buffer buffer{ + .data = params.data.data(), + .len = params.data.size(), + }; + if (!ctx.update(buffer)) [[unlikely]] { return false; } - ByteSource::Builder buf(EVP_MAX_MD_SIZE); - unsigned int len; - - if (!HMAC_Final(ctx.get(), buf.data(), &len)) { + auto buf = ctx.digest(); + if (!buf) [[unlikely]] return false; - } - *out = std::move(buf).release(len); + *out = ByteSource::Allocated(buf.release()); return true; } @@ -258,9 +267,9 @@ MaybeLocal HmacTraits::EncodeOutput(Environment* env, const HmacConfig& params, ByteSource* out) { switch (params.mode) { - case SignConfiguration::kSign: + case SignConfiguration::Mode::Sign: return out->ToArrayBuffer(env); - case SignConfiguration::kVerify: + case SignConfiguration::Mode::Verify: return Boolean::New( env->isolate(), out->size() > 0 && out->size() == params.signature.size() && diff --git a/src/crypto/crypto_keygen.cc b/src/crypto/crypto_keygen.cc index 246191f5d51796..7c3a85e9f8a24d 100644 --- a/src/crypto/crypto_keygen.cc +++ b/src/crypto/crypto_keygen.cc @@ -47,10 +47,8 @@ Maybe NidKeyPairGenTraits::AdditionalConfig( } EVPKeyCtxPointer NidKeyPairGenTraits::Setup(NidKeyPairGenConfig* params) { - EVPKeyCtxPointer ctx = - EVPKeyCtxPointer(EVP_PKEY_CTX_new_id(params->params.id, nullptr)); - if (!ctx || EVP_PKEY_keygen_init(ctx.get()) <= 0) return {}; - + auto ctx = EVPKeyCtxPointer::NewFromID(params->params.id); + if (!ctx || !ctx.initForKeygen()) return {}; return ctx; } diff --git a/src/crypto/crypto_keys.cc b/src/crypto/crypto_keys.cc index bedcf04d036478..2c55828facc35b 100644 --- a/src/crypto/crypto_keys.cc +++ b/src/crypto/crypto_keys.cc @@ -349,7 +349,7 @@ KeyObjectData::GetPrivateKeyEncodingFromJs( if (context != kKeyContextInput) { if (args[*offset]->IsString()) { Utf8Value cipher_name(env->isolate(), args[*offset]); - config.cipher = EVP_get_cipherbyname(*cipher_name); + config.cipher = ncrypto::getCipherByName(cipher_name.ToStringView()); if (config.cipher == nullptr) { THROW_ERR_CRYPTO_UNKNOWN_CIPHER(env); return Nothing(); @@ -597,7 +597,7 @@ bool KeyObjectHandle::HasInstance(Environment* env, Local value) { return !t.IsEmpty() && t->HasInstance(value); } -v8::Local KeyObjectHandle::Initialize(Environment* env) { +Local KeyObjectHandle::Initialize(Environment* env) { Local templ = env->crypto_key_object_handle_constructor(); if (templ.IsEmpty()) { Isolate* isolate = env->isolate(); @@ -958,14 +958,10 @@ bool KeyObjectHandle::CheckEcKeyData() const { CHECK_EQ(key.id(), EVP_PKEY_EC); if (data_.GetKeyType() == kKeyTypePrivate) { - return EVP_PKEY_check(ctx.get()) == 1; + return ctx.privateCheck(); } -#if OPENSSL_VERSION_MAJOR >= 3 - return EVP_PKEY_public_check_quick(ctx.get()) == 1; -#else - return EVP_PKEY_public_check(ctx.get()) == 1; -#endif + return ctx.publicCheck(); } void KeyObjectHandle::CheckEcKeyData(const FunctionCallbackInfo& args) { @@ -1202,6 +1198,9 @@ void Initialize(Environment* env, Local target) { constexpr int kKeyFormatJWK = static_cast(EVPKeyPointer::PKFormatType::JWK); + constexpr auto kSigEncDER = DSASigEnc::DER; + constexpr auto kSigEncP1363 = DSASigEnc::P1363; + NODE_DEFINE_CONSTANT(target, kWebCryptoKeyFormatRaw); NODE_DEFINE_CONSTANT(target, kWebCryptoKeyFormatPKCS8); NODE_DEFINE_CONSTANT(target, kWebCryptoKeyFormatSPKI); diff --git a/src/crypto/crypto_pbkdf2.cc b/src/crypto/crypto_pbkdf2.cc index dcaa430aacd3d7..1a0dff8238d938 100644 --- a/src/crypto/crypto_pbkdf2.cc +++ b/src/crypto/crypto_pbkdf2.cc @@ -88,20 +88,20 @@ Maybe PBKDF2Traits::AdditionalConfig( CHECK(args[offset + 4]->IsString()); // digest_name params->iterations = args[offset + 2].As()->Value(); - if (params->iterations < 0) { + if (params->iterations < 0) [[unlikely]] { THROW_ERR_OUT_OF_RANGE(env, "iterations must be <= %d", INT_MAX); return Nothing(); } params->length = args[offset + 3].As()->Value(); - if (params->length < 0) { + if (params->length < 0) [[unlikely]] { THROW_ERR_OUT_OF_RANGE(env, "length must be <= %d", INT_MAX); return Nothing(); } Utf8Value name(args.GetIsolate(), args[offset + 4]); params->digest = ncrypto::getDigestByName(name.ToStringView()); - if (params->digest == nullptr) { + if (params->digest == nullptr) [[unlikely]] { THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *name); return Nothing(); } diff --git a/src/crypto/crypto_random.cc b/src/crypto/crypto_random.cc index cb96698aa644c3..4e9ff906c06571 100644 --- a/src/crypto/crypto_random.cc +++ b/src/crypto/crypto_random.cc @@ -14,7 +14,6 @@ namespace node { using ncrypto::BignumPointer; using ncrypto::ClearErrorOnReturn; using v8::ArrayBuffer; -using v8::BackingStore; using v8::Boolean; using v8::FunctionCallbackInfo; using v8::Int32; @@ -25,6 +24,7 @@ using v8::MaybeLocal; using v8::Nothing; using v8::Object; using v8::Uint32; +using v8::Undefined; using v8::Value; namespace crypto { @@ -39,7 +39,7 @@ BignumPointer::PrimeCheckCallback getPrimeCheckCallback(Environment* env) { } // namespace MaybeLocal RandomBytesTraits::EncodeOutput( Environment* env, const RandomBytesConfig& params, ByteSource* unused) { - return v8::Undefined(env->isolate()); + return Undefined(env->isolate()); } Maybe RandomBytesTraits::AdditionalConfig( @@ -78,14 +78,13 @@ void RandomPrimeConfig::MemoryInfo(MemoryTracker* tracker) const { MaybeLocal RandomPrimeTraits::EncodeOutput( Environment* env, const RandomPrimeConfig& params, ByteSource* unused) { size_t size = params.prime.byteLength(); - std::shared_ptr store = - ArrayBuffer::NewBackingStore(env->isolate(), size); + auto store = ArrayBuffer::NewBackingStore(env->isolate(), size); CHECK_EQ(size, BignumPointer::EncodePaddedInto( params.prime.get(), reinterpret_cast(store->Data()), size)); - return ArrayBuffer::New(env->isolate(), store); + return ArrayBuffer::New(env->isolate(), std::move(store)); } Maybe RandomPrimeTraits::AdditionalConfig( @@ -104,7 +103,7 @@ Maybe RandomPrimeTraits::AdditionalConfig( if (!args[offset + 2]->IsUndefined()) { ArrayBufferOrViewContents add(args[offset + 2]); params->add.reset(add.data(), add.size()); - if (!params->add) { + if (!params->add) [[unlikely]] { THROW_ERR_CRYPTO_OPERATION_FAILED(env, "could not generate prime"); return Nothing(); } @@ -113,7 +112,7 @@ Maybe RandomPrimeTraits::AdditionalConfig( if (!args[offset + 3]->IsUndefined()) { ArrayBufferOrViewContents rem(args[offset + 3]); params->rem.reset(rem.data(), rem.size()); - if (!params->rem) { + if (!params->rem) [[unlikely]] { THROW_ERR_CRYPTO_OPERATION_FAILED(env, "could not generate prime"); return Nothing(); } @@ -124,7 +123,7 @@ Maybe RandomPrimeTraits::AdditionalConfig( CHECK_GT(bits, 0); if (params->add) { - if (BignumPointer::GetBitCount(params->add.get()) > bits) { + if (BignumPointer::GetBitCount(params->add.get()) > bits) [[unlikely]] { // If we allowed this, the best case would be returning a static prime // that wasn't generated randomly. The worst case would be an infinite // loop within OpenSSL, blocking the main thread or one of the threads @@ -133,7 +132,7 @@ Maybe RandomPrimeTraits::AdditionalConfig( return Nothing(); } - if (params->rem && params->add <= params->rem) { + if (params->rem && params->add <= params->rem) [[unlikely]] { // This would definitely lead to an infinite loop if allowed since // OpenSSL does not check this condition. THROW_ERR_OUT_OF_RANGE(env, "invalid options.rem"); @@ -144,7 +143,7 @@ Maybe RandomPrimeTraits::AdditionalConfig( params->bits = bits; params->safe = safe; params->prime = BignumPointer::NewSecure(); - if (!params->prime) { + if (!params->prime) [[unlikely]] { THROW_ERR_CRYPTO_OPERATION_FAILED(env, "could not generate prime"); return Nothing(); } @@ -195,7 +194,8 @@ bool CheckPrimeTraits::DeriveBits( const CheckPrimeConfig& params, ByteSource* out) { int ret = params.candidate.isPrime(params.checks, getPrimeCheckCallback(env)); - if (ret < 0) return false; + if (ret < 0) [[unlikely]] + return false; ByteSource::Builder buf(1); buf.data()[0] = ret; *out = std::move(buf).release(); diff --git a/src/crypto/crypto_rsa.cc b/src/crypto/crypto_rsa.cc index 05a3882c7e17d7..f0e0f9fd5f94a1 100644 --- a/src/crypto/crypto_rsa.cc +++ b/src/crypto/crypto_rsa.cc @@ -15,13 +15,15 @@ namespace node { using ncrypto::BignumPointer; +using ncrypto::DataPointer; using ncrypto::EVPKeyCtxPointer; using ncrypto::EVPKeyPointer; using ncrypto::RSAPointer; using v8::ArrayBuffer; -using v8::BackingStore; +using v8::BackingStoreInitializationMode; using v8::FunctionCallbackInfo; using v8::Int32; +using v8::Integer; using v8::JustVoid; using v8::Local; using v8::Maybe; @@ -34,37 +36,28 @@ using v8::Value; namespace crypto { EVPKeyCtxPointer RsaKeyGenTraits::Setup(RsaKeyPairGenConfig* params) { - EVPKeyCtxPointer ctx( - EVP_PKEY_CTX_new_id( - params->params.variant == kKeyVariantRSA_PSS - ? EVP_PKEY_RSA_PSS - : EVP_PKEY_RSA, - nullptr)); - - if (EVP_PKEY_keygen_init(ctx.get()) <= 0) - return EVPKeyCtxPointer(); - - if (EVP_PKEY_CTX_set_rsa_keygen_bits( - ctx.get(), - params->params.modulus_bits) <= 0) { - return EVPKeyCtxPointer(); + auto ctx = EVPKeyCtxPointer::NewFromID( + params->params.variant == kKeyVariantRSA_PSS ? EVP_PKEY_RSA_PSS + : EVP_PKEY_RSA); + + if (!ctx.initForKeygen() || + !ctx.setRsaKeygenBits(params->params.modulus_bits)) { + return {}; } // 0x10001 is the default RSA exponent. - if (params->params.exponent != 0x10001) { + if (params->params.exponent != EVPKeyCtxPointer::kDefaultRsaExponent) { auto bn = BignumPointer::New(); - CHECK(bn.setWord(params->params.exponent)); - // EVP_CTX accepts ownership of bn on success. - if (EVP_PKEY_CTX_set_rsa_keygen_pubexp(ctx.get(), bn.get()) <= 0) - return EVPKeyCtxPointer(); - - bn.release(); + if (!bn.setWord(params->params.exponent) || + !ctx.setRsaKeygenPubExp(std::move(bn))) { + return {}; + } } if (params->params.variant == kKeyVariantRSA_PSS) { if (params->params.md != nullptr && - EVP_PKEY_CTX_set_rsa_pss_keygen_md(ctx.get(), params->params.md) <= 0) { - return EVPKeyCtxPointer(); + !ctx.setRsaPssKeygenMd(params->params.md)) { + return {}; } // TODO(tniessen): This appears to only be necessary in OpenSSL 3, while @@ -76,11 +69,8 @@ EVPKeyCtxPointer RsaKeyGenTraits::Setup(RsaKeyPairGenConfig* params) { mgf1_md = params->params.md; } - if (mgf1_md != nullptr && - EVP_PKEY_CTX_set_rsa_pss_keygen_mgf1_md( - ctx.get(), - mgf1_md) <= 0) { - return EVPKeyCtxPointer(); + if (mgf1_md != nullptr && !ctx.setRsaPssKeygenMgf1Md(mgf1_md)) { + return {}; } int saltlen = params->params.saltlen; @@ -88,11 +78,8 @@ EVPKeyCtxPointer RsaKeyGenTraits::Setup(RsaKeyPairGenConfig* params) { saltlen = EVP_MD_size(params->params.md); } - if (saltlen >= 0 && - EVP_PKEY_CTX_set_rsa_pss_keygen_saltlen( - ctx.get(), - saltlen) <= 0) { - return EVPKeyCtxPointer(); + if (saltlen >= 0 && !ctx.setRsaPssSaltlen(saltlen)) { + return {}; } } @@ -154,7 +141,7 @@ Maybe RsaKeyGenTraits::AdditionalConfig( if (!args[*offset]->IsUndefined()) { CHECK(args[*offset]->IsString()); Utf8Value digest(env->isolate(), args[*offset]); - params->params.md = EVP_get_digestbyname(*digest); + params->params.md = ncrypto::getDigestByName(digest.ToStringView()); if (params->params.md == nullptr) { THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *digest); return Nothing(); @@ -164,7 +151,7 @@ Maybe RsaKeyGenTraits::AdditionalConfig( if (!args[*offset + 1]->IsUndefined()) { CHECK(args[*offset + 1]->IsString()); Utf8Value digest(env->isolate(), args[*offset + 1]); - params->params.mgf1_md = EVP_get_digestbyname(*digest); + params->params.mgf1_md = ncrypto::getDigestByName(digest.ToStringView()); if (params->params.mgf1_md == nullptr) { THROW_ERR_CRYPTO_INVALID_DIGEST( env, "Invalid MGF1 digest: %s", *digest); @@ -196,8 +183,11 @@ WebCryptoKeyExportStatus RSA_JWK_Export(const KeyObjectData& key_data, return WebCryptoKeyExportStatus::FAILED; } -template +using Cipher_t = DataPointer(const EVPKeyPointer& key, + const ncrypto::Rsa::CipherParams& params, + const ncrypto::Buffer in); + +template WebCryptoCipherStatus RSA_Cipher(Environment* env, const KeyObjectData& key_data, const RSACipherConfig& params, @@ -206,45 +196,16 @@ WebCryptoCipherStatus RSA_Cipher(Environment* env, CHECK_NE(key_data.GetKeyType(), kKeyTypeSecret); Mutex::ScopedLock lock(key_data.mutex()); const auto& m_pkey = key_data.GetAsymmetricKey(); + const ncrypto::Rsa::CipherParams nparams{ + .padding = params.padding, + .digest = params.digest, + .label = params.label, + }; - EVPKeyCtxPointer ctx = m_pkey.newCtx(); - - if (!ctx || init(ctx.get()) <= 0) - return WebCryptoCipherStatus::FAILED; - - if (EVP_PKEY_CTX_set_rsa_padding(ctx.get(), params.padding) <= 0) { - return WebCryptoCipherStatus::FAILED; - } - - if (params.digest != nullptr && - (EVP_PKEY_CTX_set_rsa_oaep_md(ctx.get(), params.digest) <= 0 || - EVP_PKEY_CTX_set_rsa_mgf1_md(ctx.get(), params.digest) <= 0)) { - return WebCryptoCipherStatus::FAILED; - } - - if (!SetRsaOaepLabel(ctx, params.label)) return WebCryptoCipherStatus::FAILED; - - size_t out_len = 0; - if (cipher( - ctx.get(), - nullptr, - &out_len, - in.data(), - in.size()) <= 0) { - return WebCryptoCipherStatus::FAILED; - } - - ByteSource::Builder buf(out_len); - - if (cipher(ctx.get(), - buf.data(), - &out_len, - in.data(), - in.size()) <= 0) { - return WebCryptoCipherStatus::FAILED; - } + auto data = cipher(m_pkey, nparams, in); + if (!data) return WebCryptoCipherStatus::FAILED; - *out = std::move(buf).release(out_len); + *out = ByteSource::Allocated(data.release()); return WebCryptoCipherStatus::OK; } } // namespace @@ -316,7 +277,7 @@ Maybe RSACipherTraits::AdditionalConfig( CHECK(args[offset + 1]->IsString()); // digest Utf8Value digest(env->isolate(), args[offset + 1]); - params->digest = EVP_get_digestbyname(*digest); + params->digest = ncrypto::getDigestByName(digest.ToStringView()); if (params->digest == nullptr) { THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *digest); return Nothing(); @@ -349,12 +310,10 @@ WebCryptoCipherStatus RSACipherTraits::DoCipher(Environment* env, switch (cipher_mode) { case kWebCryptoCipherEncrypt: CHECK_EQ(key_data.GetKeyType(), kKeyTypePublic); - return RSA_Cipher( - env, key_data, params, in, out); + return RSA_Cipher(env, key_data, params, in, out); case kWebCryptoCipherDecrypt: CHECK_EQ(key_data.GetKeyType(), kKeyTypePrivate); - return RSA_Cipher( - env, key_data, params, in, out); + return RSA_Cipher(env, key_data, params, in, out); } return WebCryptoCipherStatus::FAILED; } @@ -364,50 +323,37 @@ Maybe ExportJWKRsaKey(Environment* env, Local target) { Mutex::ScopedLock lock(key.mutex()); const auto& m_pkey = key.GetAsymmetricKey(); - int type = m_pkey.id(); - CHECK(type == EVP_PKEY_RSA || type == EVP_PKEY_RSA_PSS); - // TODO(tniessen): Remove the "else" branch once we drop support for OpenSSL - // versions older than 1.1.1e via FIPS / dynamic linking. - const RSA* rsa; - if (OpenSSL_version_num() >= 0x1010105fL) { - rsa = EVP_PKEY_get0_RSA(m_pkey.get()); - } else { - rsa = static_cast(EVP_PKEY_get0(m_pkey.get())); - } - CHECK_NOT_NULL(rsa); - - const BIGNUM* n; - const BIGNUM* e; - const BIGNUM* d; - const BIGNUM* p; - const BIGNUM* q; - const BIGNUM* dp; - const BIGNUM* dq; - const BIGNUM* qi; - RSA_get0_key(rsa, &n, &e, &d); - - if (target->Set( - env->context(), - env->jwk_kty_string(), - env->jwk_rsa_string()).IsNothing()) { + const ncrypto::Rsa rsa = m_pkey; + if (!rsa || + target->Set(env->context(), env->jwk_kty_string(), env->jwk_rsa_string()) + .IsNothing()) { return Nothing(); } - if (SetEncodedValue(env, target, env->jwk_n_string(), n).IsNothing() || - SetEncodedValue(env, target, env->jwk_e_string(), e).IsNothing()) { + auto pub_key = rsa.getPublicKey(); + + if (SetEncodedValue(env, target, env->jwk_n_string(), pub_key.n) + .IsNothing() || + SetEncodedValue(env, target, env->jwk_e_string(), pub_key.e) + .IsNothing()) { return Nothing(); } if (key.GetKeyType() == kKeyTypePrivate) { - RSA_get0_factors(rsa, &p, &q); - RSA_get0_crt_params(rsa, &dp, &dq, &qi); - if (SetEncodedValue(env, target, env->jwk_d_string(), d).IsNothing() || - SetEncodedValue(env, target, env->jwk_p_string(), p).IsNothing() || - SetEncodedValue(env, target, env->jwk_q_string(), q).IsNothing() || - SetEncodedValue(env, target, env->jwk_dp_string(), dp).IsNothing() || - SetEncodedValue(env, target, env->jwk_dq_string(), dq).IsNothing() || - SetEncodedValue(env, target, env->jwk_qi_string(), qi).IsNothing()) { + auto pvt_key = rsa.getPrivateKey(); + if (SetEncodedValue(env, target, env->jwk_d_string(), pub_key.d) + .IsNothing() || + SetEncodedValue(env, target, env->jwk_p_string(), pvt_key.p) + .IsNothing() || + SetEncodedValue(env, target, env->jwk_q_string(), pvt_key.q) + .IsNothing() || + SetEncodedValue(env, target, env->jwk_dp_string(), pvt_key.dp) + .IsNothing() || + SetEncodedValue(env, target, env->jwk_dq_string(), pvt_key.dq) + .IsNothing() || + SetEncodedValue(env, target, env->jwk_qi_string(), pvt_key.qi) + .IsNothing()) { return Nothing(); } } @@ -440,15 +386,12 @@ KeyObjectData ImportJWKRsaKey(Environment* env, KeyType type = d_value->IsString() ? kKeyTypePrivate : kKeyTypePublic; RSAPointer rsa(RSA_new()); + ncrypto::Rsa rsa_view(rsa.get()); ByteSource n = ByteSource::FromEncodedString(env, n_value.As()); ByteSource e = ByteSource::FromEncodedString(env, e_value.As()); - if (!RSA_set0_key( - rsa.get(), - n.ToBN().release(), - e.ToBN().release(), - nullptr)) { + if (!rsa_view.setPublicKey(n.ToBN(), e.ToBN())) { THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK RSA key"); return {}; } @@ -485,20 +428,15 @@ KeyObjectData ImportJWKRsaKey(Environment* env, ByteSource dq = ByteSource::FromEncodedString(env, dq_value.As()); ByteSource qi = ByteSource::FromEncodedString(env, qi_value.As()); - if (!RSA_set0_key(rsa.get(), nullptr, nullptr, d.ToBN().release()) || - !RSA_set0_factors(rsa.get(), p.ToBN().release(), q.ToBN().release()) || - !RSA_set0_crt_params( - rsa.get(), - dp.ToBN().release(), - dq.ToBN().release(), - qi.ToBN().release())) { + if (!rsa_view.setPrivateKey( + d.ToBN(), q.ToBN(), p.ToBN(), dp.ToBN(), dq.ToBN(), qi.ToBN())) { THROW_ERR_CRYPTO_INVALID_JWK(env, "Invalid JWK RSA key"); return {}; } } - auto pkey = EVPKeyPointer::New(); - CHECK_EQ(EVP_PKEY_set1_RSA(pkey.get(), rsa.get()), 1); + auto pkey = EVPKeyPointer::NewRSA(std::move(rsa)); + if (!pkey) return {}; return KeyObjectData::CreateAsymmetric(type, std::move(pkey)); } @@ -506,43 +444,32 @@ KeyObjectData ImportJWKRsaKey(Environment* env, Maybe GetRsaKeyDetail(Environment* env, const KeyObjectData& key, Local target) { - const BIGNUM* e; // Public Exponent - const BIGNUM* n; // Modulus - Mutex::ScopedLock lock(key.mutex()); const auto& m_pkey = key.GetAsymmetricKey(); - int type = m_pkey.id(); - CHECK(type == EVP_PKEY_RSA || type == EVP_PKEY_RSA_PSS); // TODO(tniessen): Remove the "else" branch once we drop support for OpenSSL // versions older than 1.1.1e via FIPS / dynamic linking. - const RSA* rsa; - if (OpenSSL_version_num() >= 0x1010105fL) { - rsa = EVP_PKEY_get0_RSA(m_pkey.get()); - } else { - rsa = static_cast(EVP_PKEY_get0(m_pkey.get())); - } - CHECK_NOT_NULL(rsa); + const ncrypto::Rsa rsa = m_pkey; + if (!rsa) return Nothing(); - RSA_get0_key(rsa, &n, &e, nullptr); + auto pub_key = rsa.getPublicKey(); if (target ->Set(env->context(), env->modulus_length_string(), - Number::New(env->isolate(), - static_cast(BignumPointer::GetBitCount(n)))) + Number::New( + env->isolate(), + static_cast(BignumPointer::GetBitCount(pub_key.n)))) .IsNothing()) { return Nothing(); } - std::unique_ptr public_exponent; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - public_exponent = ArrayBuffer::NewBackingStore( - env->isolate(), BignumPointer::GetByteCount(e)); - } + auto public_exponent = ArrayBuffer::NewBackingStore( + env->isolate(), + BignumPointer::GetByteCount(pub_key.e), + BackingStoreInitializationMode::kUninitialized); CHECK_EQ(BignumPointer::EncodePaddedInto( - e, + pub_key.e, static_cast(public_exponent->Data()), public_exponent->ByteLength()), public_exponent->ByteLength()); @@ -555,7 +482,7 @@ Maybe GetRsaKeyDetail(Environment* env, return Nothing(); } - if (type == EVP_PKEY_RSA_PSS) { + if (m_pkey.id() == EVP_PKEY_RSA_PSS) { // Due to the way ASN.1 encoding works, default values are omitted when // encoding the data structure. However, there are also RSA-PSS keys for // which no parameters are set. In that case, the ASN.1 RSASSA-PSS-params @@ -565,64 +492,34 @@ Maybe GetRsaKeyDetail(Environment* env, // In that case, RSA_get0_pss_params does not return nullptr but all fields // of the returned RSA_PSS_PARAMS will be set to nullptr. - const RSA_PSS_PARAMS* params = RSA_get0_pss_params(rsa); - if (params != nullptr) { - int hash_nid = NID_sha1; - int mgf_nid = NID_mgf1; - int mgf1_hash_nid = NID_sha1; - int64_t salt_length = 20; - - if (params->hashAlgorithm != nullptr) { - const ASN1_OBJECT* hash_obj; - X509_ALGOR_get0(&hash_obj, nullptr, nullptr, params->hashAlgorithm); - hash_nid = OBJ_obj2nid(hash_obj); - } - + auto maybe_params = rsa.getPssParams(); + if (maybe_params.has_value()) { + auto& params = maybe_params.value(); if (target - ->Set( - env->context(), - env->hash_algorithm_string(), - OneByteString(env->isolate(), OBJ_nid2ln(hash_nid))) + ->Set(env->context(), + env->hash_algorithm_string(), + OneByteString(env->isolate(), params.digest)) .IsNothing()) { return Nothing(); } - if (params->maskGenAlgorithm != nullptr) { - const ASN1_OBJECT* mgf_obj; - X509_ALGOR_get0(&mgf_obj, nullptr, nullptr, params->maskGenAlgorithm); - mgf_nid = OBJ_obj2nid(mgf_obj); - if (mgf_nid == NID_mgf1) { - const ASN1_OBJECT* mgf1_hash_obj; - X509_ALGOR_get0(&mgf1_hash_obj, nullptr, nullptr, params->maskHash); - mgf1_hash_nid = OBJ_obj2nid(mgf1_hash_obj); - } - } - // If, for some reason, the MGF is not MGF1, then the MGF1 hash function // is intentionally not added to the object. - if (mgf_nid == NID_mgf1) { + if (params.mgf1_digest.has_value()) { + auto digest = params.mgf1_digest.value(); if (target - ->Set( - env->context(), - env->mgf1_hash_algorithm_string(), - OneByteString(env->isolate(), OBJ_nid2ln(mgf1_hash_nid))) + ->Set(env->context(), + env->mgf1_hash_algorithm_string(), + OneByteString(env->isolate(), digest)) .IsNothing()) { return Nothing(); } } - if (params->saltLength != nullptr) { - if (ASN1_INTEGER_get_int64(&salt_length, params->saltLength) != 1) { - ThrowCryptoError(env, ERR_get_error(), "ASN1_INTEGER_get_in64 error"); - return Nothing(); - } - } - if (target - ->Set( - env->context(), - env->salt_length_string(), - Number::New(env->isolate(), static_cast(salt_length))) + ->Set(env->context(), + env->salt_length_string(), + Integer::New(env->isolate(), params.salt_length)) .IsNothing()) { return Nothing(); } diff --git a/src/crypto/crypto_sig.cc b/src/crypto/crypto_sig.cc index abb8a804c1b508..175a8e92ef437f 100644 --- a/src/crypto/crypto_sig.cc +++ b/src/crypto/crypto_sig.cc @@ -14,20 +14,20 @@ namespace node { using ncrypto::BignumPointer; using ncrypto::ClearErrorOnReturn; +using ncrypto::DataPointer; using ncrypto::ECDSASigPointer; -using ncrypto::ECKeyPointer; using ncrypto::EVPKeyCtxPointer; using ncrypto::EVPKeyPointer; using ncrypto::EVPMDCtxPointer; using v8::ArrayBuffer; using v8::BackingStore; +using v8::BackingStoreInitializationMode; using v8::Boolean; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::HandleScope; using v8::Int32; using v8::Isolate; -using v8::Just; using v8::JustVoid; using v8::Local; using v8::Maybe; @@ -39,44 +39,40 @@ using v8::Value; namespace crypto { namespace { -bool ValidateDSAParameters(EVP_PKEY* key) { - /* Validate DSA2 parameters from FIPS 186-4 */ - auto id = EVPKeyPointer::base_id(key); -#if OPENSSL_VERSION_MAJOR >= 3 - if (EVP_default_properties_is_fips_enabled(nullptr) && EVP_PKEY_DSA == id) { -#else - if (FIPS_mode() && EVP_PKEY_DSA == id) { -#endif - const DSA* dsa = EVP_PKEY_get0_DSA(key); - const BIGNUM* p; - const BIGNUM* q; - DSA_get0_pqg(dsa, &p, &q, nullptr); - int L = BignumPointer::GetBitCount(p); - int N = BignumPointer::GetBitCount(q); - - return (L == 1024 && N == 160) || - (L == 2048 && N == 224) || - (L == 2048 && N == 256) || - (L == 3072 && N == 256); +int GetPaddingFromJS(const EVPKeyPointer& key, Local val) { + int padding = key.getDefaultSignPadding(); + if (!val->IsUndefined()) [[likely]] { + CHECK(val->IsInt32()); + padding = val.As()->Value(); } + return padding; +} - return true; +std::optional GetSaltLenFromJS(Local val) { + std::optional salt_len; + if (!val->IsUndefined()) [[likely]] { + CHECK(val->IsInt32()); + salt_len = val.As()->Value(); + } + return salt_len; +} + +DSASigEnc GetDSASigEncFromJS(Local val) { + CHECK(val->IsInt32()); + int i = val.As()->Value(); + if (i < 0 || i >= static_cast(DSASigEnc::Invalid)) [[unlikely]] { + return DSASigEnc::Invalid; + } + return static_cast(val.As()->Value()); } bool ApplyRSAOptions(const EVPKeyPointer& pkey, EVP_PKEY_CTX* pkctx, int padding, - const Maybe& salt_len) { - int id = pkey.id(); - if (id == EVP_PKEY_RSA || id == EVP_PKEY_RSA2 || id == EVP_PKEY_RSA_PSS) { - if (EVP_PKEY_CTX_set_rsa_padding(pkctx, padding) <= 0) - return false; - if (padding == RSA_PKCS1_PSS_PADDING && salt_len.IsJust()) { - if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkctx, salt_len.FromJust()) <= 0) - return false; - } + std::optional salt_len) { + if (pkey.isRsaVariant()) { + return EVPKeyCtxPointer::setRsaPadding(pkctx, padding, salt_len); } - return true; } @@ -84,38 +80,31 @@ std::unique_ptr Node_SignFinal(Environment* env, EVPMDCtxPointer&& mdctx, const EVPKeyPointer& pkey, int padding, - Maybe pss_salt_len) { - unsigned char m[EVP_MAX_MD_SIZE]; - unsigned int m_len; - - if (!EVP_DigestFinal_ex(mdctx.get(), m, &m_len)) + std::optional pss_salt_len) { + auto data = mdctx.digestFinal(mdctx.getExpectedSize()); + if (!data) [[unlikely]] return nullptr; - size_t sig_len = pkey.size(); - std::unique_ptr sig; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - sig = ArrayBuffer::NewBackingStore(env->isolate(), sig_len); - } + auto sig = ArrayBuffer::NewBackingStore(env->isolate(), pkey.size()); + ncrypto::Buffer sig_buf{ + .data = static_cast(sig->Data()), + .len = pkey.size(), + }; + EVPKeyCtxPointer pkctx = pkey.newCtx(); - if (pkctx && EVP_PKEY_sign_init(pkctx.get()) > 0 && + if (pkctx.initForSign() > 0 && ApplyRSAOptions(pkey, pkctx.get(), padding, pss_salt_len) && - EVP_PKEY_CTX_set_signature_md(pkctx.get(), EVP_MD_CTX_md(mdctx.get())) > - 0 && - EVP_PKEY_sign(pkctx.get(), - static_cast(sig->Data()), - &sig_len, - m, - m_len) > 0) { - CHECK_LE(sig_len, sig->ByteLength()); - if (sig_len == 0) { - sig = ArrayBuffer::NewBackingStore(env->isolate(), 0); - } else if (sig_len != sig->ByteLength()) { - std::unique_ptr old_sig = std::move(sig); - sig = ArrayBuffer::NewBackingStore(env->isolate(), sig_len); - memcpy(static_cast(sig->Data()), - static_cast(old_sig->Data()), - sig_len); + pkctx.setSignatureMd(mdctx) && pkctx.signInto(data, &sig_buf)) + [[likely]] { + CHECK_LE(sig_buf.len, sig->ByteLength()); + if (sig_buf.len < sig->ByteLength()) { + auto new_sig = ArrayBuffer::NewBackingStore(env->isolate(), sig_buf.len); + if (sig_buf.len > 0) [[likely]] { + memcpy(static_cast(new_sig->Data()), + static_cast(sig->Data()), + sig_buf.len); + } + sig = std::move(new_sig); } return sig; } @@ -123,62 +112,26 @@ std::unique_ptr Node_SignFinal(Environment* env, return nullptr; } -int GetDefaultSignPadding(const EVPKeyPointer& m_pkey) { - return m_pkey.id() == EVP_PKEY_RSA_PSS ? RSA_PKCS1_PSS_PADDING - : RSA_PKCS1_PADDING; -} - -unsigned int GetBytesOfRS(const EVPKeyPointer& pkey) { - int bits, base_id = pkey.base_id(); - - if (base_id == EVP_PKEY_DSA) { - const DSA* dsa_key = EVP_PKEY_get0_DSA(pkey.get()); - // Both r and s are computed mod q, so their width is limited by that of q. - bits = BignumPointer::GetBitCount(DSA_get0_q(dsa_key)); - } else if (base_id == EVP_PKEY_EC) { - bits = EC_GROUP_order_bits(ECKeyPointer::GetGroup(pkey)); - } else { - return kNoDsaSignature; - } - - return (bits + 7) / 8; -} - -bool ExtractP1363( - const unsigned char* sig_data, - unsigned char* out, - size_t len, - size_t n) { - ncrypto::Buffer sig_buffer{ - .data = sig_data, - .len = len, - }; - auto asn1_sig = ECDSASigPointer::Parse(sig_buffer); - if (!asn1_sig) - return false; - - return BignumPointer::EncodePaddedInto(asn1_sig.r(), out, n) > 0 && - BignumPointer::EncodePaddedInto(asn1_sig.s(), out + n, n) > 0; -} - // Returns the maximum size of each of the integers (r, s) of the DSA signature. std::unique_ptr ConvertSignatureToP1363( Environment* env, const EVPKeyPointer& pkey, std::unique_ptr&& signature) { - unsigned int n = GetBytesOfRS(pkey); - if (n == kNoDsaSignature) - return std::move(signature); + uint32_t n = pkey.getBytesOfRS().value_or(kNoDsaSignature); + if (n == kNoDsaSignature) return std::move(signature); - std::unique_ptr buf; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - buf = ArrayBuffer::NewBackingStore(env->isolate(), 2 * n); - } - if (!ExtractP1363(static_cast(signature->Data()), - static_cast(buf->Data()), - signature->ByteLength(), n)) + auto buf = ArrayBuffer::NewBackingStore( + env->isolate(), 2 * n, BackingStoreInitializationMode::kUninitialized); + + ncrypto::Buffer sig_buffer{ + .data = static_cast(signature->Data()), + .len = signature->ByteLength(), + }; + + if (!ncrypto::extractP1363( + sig_buffer, static_cast(buf->Data()), n)) { return std::move(signature); + } return buf; } @@ -187,30 +140,33 @@ std::unique_ptr ConvertSignatureToP1363( ByteSource ConvertSignatureToP1363(Environment* env, const EVPKeyPointer& pkey, const ByteSource& signature) { - unsigned int n = GetBytesOfRS(pkey); - if (n == kNoDsaSignature) - return ByteSource(); + unsigned int n = pkey.getBytesOfRS().value_or(kNoDsaSignature); + if (n == kNoDsaSignature) [[unlikely]] + return {}; - const unsigned char* sig_data = signature.data(); + auto data = DataPointer::Alloc(n * 2); + if (!data) [[unlikely]] + return {}; + unsigned char* out = static_cast(data.get()); - ByteSource::Builder out(n * 2); - memset(out.data(), 0, n * 2); + // Extracting the signature may not actually use all of the allocated space. + // We need to ensure that the buffer is zeroed out before use. + data.zero(); - if (!ExtractP1363(sig_data, out.data(), signature.size(), n)) - return ByteSource(); + if (!ncrypto::extractP1363(signature, out, n)) [[unlikely]] { + return {}; + } - return std::move(out).release(); + return ByteSource::Allocated(data.release()); } ByteSource ConvertSignatureToDER(const EVPKeyPointer& pkey, ByteSource&& out) { - unsigned int n = GetBytesOfRS(pkey); - if (n == kNoDsaSignature) - return std::move(out); + unsigned int n = pkey.getBytesOfRS().value_or(kNoDsaSignature); + if (n == kNoDsaSignature) return std::move(out); const unsigned char* sig_data = out.data(); - if (out.size() != 2 * n) - return ByteSource(); + if (out.size() != 2 * n) return {}; auto asn1_sig = ECDSASigPointer::New(); CHECK(asn1_sig); @@ -221,7 +177,8 @@ ByteSource ConvertSignatureToDER(const EVPKeyPointer& pkey, ByteSource&& out) { CHECK(asn1_sig.setParams(std::move(r), std::move(s))); auto buf = asn1_sig.encode(); - if (buf.len <= 0) return ByteSource(); + if (buf.len <= 0) [[unlikely]] + return {}; CHECK_NOT_NULL(buf.data); return ByteSource::Allocated(buf); @@ -231,95 +188,87 @@ void CheckThrow(Environment* env, SignBase::Error error) { HandleScope scope(env->isolate()); switch (error) { - case SignBase::Error::kSignUnknownDigest: + case SignBase::Error::UnknownDigest: return THROW_ERR_CRYPTO_INVALID_DIGEST(env); - case SignBase::Error::kSignNotInitialised: + case SignBase::Error::NotInitialised: return THROW_ERR_CRYPTO_INVALID_STATE(env, "Not initialised"); - case SignBase::Error::kSignMalformedSignature: + case SignBase::Error::MalformedSignature: return THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Malformed signature"); - case SignBase::Error::kSignInit: - case SignBase::Error::kSignUpdate: - case SignBase::Error::kSignPrivateKey: - case SignBase::Error::kSignPublicKey: - { - unsigned long err = ERR_get_error(); // NOLINT(runtime/int) - if (err) - return ThrowCryptoError(env, err); - switch (error) { - case SignBase::Error::kSignInit: - return THROW_ERR_CRYPTO_OPERATION_FAILED(env, - "EVP_SignInit_ex failed"); - case SignBase::Error::kSignUpdate: - return THROW_ERR_CRYPTO_OPERATION_FAILED(env, - "EVP_SignUpdate failed"); - case SignBase::Error::kSignPrivateKey: - return THROW_ERR_CRYPTO_OPERATION_FAILED(env, - "PEM_read_bio_PrivateKey failed"); - case SignBase::Error::kSignPublicKey: - return THROW_ERR_CRYPTO_OPERATION_FAILED(env, - "PEM_read_bio_PUBKEY failed"); - default: - ABORT(); - } + case SignBase::Error::Init: + case SignBase::Error::Update: + case SignBase::Error::PrivateKey: + case SignBase::Error::PublicKey: { + unsigned long err = ERR_get_error(); // NOLINT(runtime/int) + if (err) return ThrowCryptoError(env, err); + switch (error) { + case SignBase::Error::Init: + return THROW_ERR_CRYPTO_OPERATION_FAILED(env, + "EVP_SignInit_ex failed"); + case SignBase::Error::Update: + return THROW_ERR_CRYPTO_OPERATION_FAILED(env, + "EVP_SignUpdate failed"); + case SignBase::Error::PrivateKey: + return THROW_ERR_CRYPTO_OPERATION_FAILED( + env, "PEM_read_bio_PrivateKey failed"); + case SignBase::Error::PublicKey: + return THROW_ERR_CRYPTO_OPERATION_FAILED( + env, "PEM_read_bio_PUBKEY failed"); + default: + ABORT(); } + } - case SignBase::Error::kSignOk: + case SignBase::Error::Ok: return; } } -bool IsOneShot(const EVPKeyPointer& key) { - return key.id() == EVP_PKEY_ED25519 || key.id() == EVP_PKEY_ED448; -} - -bool UseP1363Encoding(const EVPKeyPointer& key, const DSASigEnc& dsa_encoding) { - return (key.id() == EVP_PKEY_EC || key.id() == EVP_PKEY_DSA) && - dsa_encoding == kSigEncP1363; +bool UseP1363Encoding(const EVPKeyPointer& key, const DSASigEnc dsa_encoding) { + return key.isSigVariant() && dsa_encoding == DSASigEnc::P1363; } } // namespace -SignBase::Error SignBase::Init(const char* sign_type) { +SignBase::Error SignBase::Init(std::string_view digest) { CHECK_NULL(mdctx_); - // Historically, "dss1" and "DSS1" were DSA aliases for SHA-1 - // exposed through the public API. - if (strcmp(sign_type, "dss1") == 0 || - strcmp(sign_type, "DSS1") == 0) { - sign_type = "SHA1"; - } - const EVP_MD* md = EVP_get_digestbyname(sign_type); - if (md == nullptr) - return kSignUnknownDigest; - - mdctx_.reset(EVP_MD_CTX_new()); - if (!mdctx_ || !EVP_DigestInit_ex(mdctx_.get(), md, nullptr)) { + auto md = ncrypto::getDigestByName(digest); + if (md == nullptr) [[unlikely]] + return Error::UnknownDigest; + + mdctx_ = EVPMDCtxPointer::New(); + + if (!mdctx_.digestInit(md)) [[unlikely]] { mdctx_.reset(); - return kSignInit; + return Error::Init; } - return kSignOk; + return Error::Ok; } SignBase::Error SignBase::Update(const char* data, size_t len) { - if (mdctx_ == nullptr) - return kSignNotInitialised; - if (!EVP_DigestUpdate(mdctx_.get(), data, len)) - return kSignUpdate; - return kSignOk; + if (mdctx_ == nullptr) [[unlikely]] + return Error::NotInitialised; + + ncrypto::Buffer buf{ + .data = data, + .len = len, + }; + + return mdctx_.digestUpdate(buf) ? Error::Ok : Error::Update; } SignBase::SignBase(Environment* env, Local wrap) - : BaseObject(env, wrap) {} + : BaseObject(env, wrap) { + MakeWeak(); +} void SignBase::MemoryInfo(MemoryTracker* tracker) const { tracker->TrackFieldWithSize("mdctx", mdctx_ ? kSizeOf_EVP_MD_CTX : 0); } -Sign::Sign(Environment* env, Local wrap) : SignBase(env, wrap) { - MakeWeak(); -} +Sign::Sign(Environment* env, Local wrap) : SignBase(env, wrap) {} void Sign::Initialize(Environment* env, Local target) { Isolate* isolate = env->isolate(); @@ -335,8 +284,13 @@ void Sign::Initialize(Environment* env, Local target) { SignJob::Initialize(env, target); - constexpr int kSignJobModeSign = SignConfiguration::kSign; - constexpr int kSignJobModeVerify = SignConfiguration::kVerify; + constexpr int kSignJobModeSign = + static_cast(SignConfiguration::Mode::Sign); + constexpr int kSignJobModeVerify = + static_cast(SignConfiguration::Mode::Verify); + + constexpr auto kSigEncDER = DSASigEnc::DER; + constexpr auto kSigEncP1363 = DSASigEnc::P1363; NODE_DEFINE_CONSTANT(target, kSignJobModeSign); NODE_DEFINE_CONSTANT(target, kSignJobModeVerify); @@ -363,8 +317,8 @@ void Sign::SignInit(const FunctionCallbackInfo& args) { Sign* sign; ASSIGN_OR_RETURN_UNWRAP(&sign, args.This()); - const node::Utf8Value sign_type(args.GetIsolate(), args[0]); - crypto::CheckThrow(env, sign->Init(*sign_type)); + const node::Utf8Value sign_type(env->isolate(), args[0]); + crypto::CheckThrow(env, sign->Init(sign_type.ToStringView())); } void Sign::SignUpdate(const FunctionCallbackInfo& args) { @@ -380,20 +334,22 @@ void Sign::SignUpdate(const FunctionCallbackInfo& args) { Sign::SignResult Sign::SignFinal(const EVPKeyPointer& pkey, int padding, - const Maybe& salt_len, + std::optional salt_len, DSASigEnc dsa_sig_enc) { - if (!mdctx_) - return SignResult(kSignNotInitialised); + if (!mdctx_) [[unlikely]] { + return SignResult(Error::NotInitialised); + } EVPMDCtxPointer mdctx = std::move(mdctx_); - if (!ValidateDSAParameters(pkey.get())) - return SignResult(kSignPrivateKey); + if (!pkey.validateDsaParameters()) { + return SignResult(Error::PrivateKey); + } - std::unique_ptr buffer = + auto buffer = Node_SignFinal(env(), std::move(mdctx), pkey, padding, salt_len); - Error error = buffer ? kSignOk : kSignPrivateKey; - if (error == kSignOk && dsa_sig_enc == kSigEncP1363) { + Error error = buffer ? Error::Ok : Error::PrivateKey; + if (error == Error::Ok && dsa_sig_enc == DSASigEnc::P1363) { buffer = ConvertSignatureToP1363(env(), pkey, std::move(buffer)); CHECK_NOT_NULL(buffer->Data()); } @@ -412,49 +368,34 @@ void Sign::SignFinal(const FunctionCallbackInfo& args) { if (!data) [[unlikely]] return; const auto& key = data.GetAsymmetricKey(); - if (!key) + if (!key) [[unlikely]] return; - if (IsOneShot(key)) { + if (key.isOneShotVariant()) [[unlikely]] { THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env); return; } - int padding = GetDefaultSignPadding(key); - if (!args[offset]->IsUndefined()) { - CHECK(args[offset]->IsInt32()); - padding = args[offset].As()->Value(); - } - - Maybe salt_len = Nothing(); - if (!args[offset + 1]->IsUndefined()) { - CHECK(args[offset + 1]->IsInt32()); - salt_len = Just(args[offset + 1].As()->Value()); + int padding = GetPaddingFromJS(key, args[offset]); + std::optional salt_len = GetSaltLenFromJS(args[offset + 1]); + DSASigEnc dsa_sig_enc = GetDSASigEncFromJS(args[offset + 2]); + if (dsa_sig_enc == DSASigEnc::Invalid) [[unlikely]] { + THROW_ERR_OUT_OF_RANGE(env, "invalid signature encoding"); + return; } - CHECK(args[offset + 2]->IsInt32()); - DSASigEnc dsa_sig_enc = - static_cast(args[offset + 2].As()->Value()); + SignResult ret = sign->SignFinal(key, padding, salt_len, dsa_sig_enc); - SignResult ret = sign->SignFinal( - key, - padding, - salt_len, - dsa_sig_enc); - - if (ret.error != kSignOk) + if (ret.error != Error::Ok) [[unlikely]] { return crypto::CheckThrow(env, ret.error); + } - Local ab = - ArrayBuffer::New(env->isolate(), std::move(ret.signature)); + auto ab = ArrayBuffer::New(env->isolate(), std::move(ret.signature)); args.GetReturnValue().Set( Buffer::New(env, ab, 0, ab->ByteLength()).FromMaybe(Local())); } -Verify::Verify(Environment* env, Local wrap) - : SignBase(env, wrap) { - MakeWeak(); -} +Verify::Verify(Environment* env, Local wrap) : SignBase(env, wrap) {} void Verify::Initialize(Environment* env, Local target) { Isolate* isolate = env->isolate(); @@ -486,8 +427,8 @@ void Verify::VerifyInit(const FunctionCallbackInfo& args) { Verify* verify; ASSIGN_OR_RETURN_UNWRAP(&verify, args.This()); - const node::Utf8Value verify_type(args.GetIsolate(), args[0]); - crypto::CheckThrow(env, verify->Init(*verify_type)); + const node::Utf8Value verify_type(env->isolate(), args[0]); + crypto::CheckThrow(env, verify->Init(verify_type.ToStringView())); } void Verify::VerifyUpdate(const FunctionCallbackInfo& args) { @@ -495,8 +436,9 @@ void Verify::VerifyUpdate(const FunctionCallbackInfo& args) { const FunctionCallbackInfo& args, const char* data, size_t size) { Environment* env = Environment::GetCurrent(args); - if (size > INT_MAX) [[unlikely]] + if (size > INT_MAX) [[unlikely]] { return THROW_ERR_OUT_OF_RANGE(env, "data is too long"); + } Error err = verify->Update(data, size); crypto::CheckThrow(verify->env(), err); }); @@ -505,35 +447,30 @@ void Verify::VerifyUpdate(const FunctionCallbackInfo& args) { SignBase::Error Verify::VerifyFinal(const EVPKeyPointer& pkey, const ByteSource& sig, int padding, - const Maybe& saltlen, + std::optional saltlen, bool* verify_result) { - if (!mdctx_) - return kSignNotInitialised; + if (!mdctx_) [[unlikely]] + return Error::NotInitialised; - unsigned char m[EVP_MAX_MD_SIZE]; - unsigned int m_len; *verify_result = false; EVPMDCtxPointer mdctx = std::move(mdctx_); - if (!EVP_DigestFinal_ex(mdctx.get(), m, &m_len)) - return kSignPublicKey; + auto data = mdctx.digestFinal(mdctx.getExpectedSize()); + if (!data) [[unlikely]] + return Error::PublicKey; EVPKeyCtxPointer pkctx = pkey.newCtx(); - if (pkctx) { - const int init_ret = EVP_PKEY_verify_init(pkctx.get()); - if (init_ret == -2) { - return kSignPublicKey; - } + if (pkctx) [[likely]] { + const int init_ret = pkctx.initForVerify(); + if (init_ret == -2) [[unlikely]] + return Error::PublicKey; if (init_ret > 0 && ApplyRSAOptions(pkey, pkctx.get(), padding, saltlen) && - EVP_PKEY_CTX_set_signature_md(pkctx.get(), EVP_MD_CTX_md(mdctx.get())) > - 0) { - const unsigned char* s = sig.data(); - const int r = EVP_PKEY_verify(pkctx.get(), s, sig.size(), m, m_len); - *verify_result = r == 1; + pkctx.setSignatureMd(mdctx)) { + *verify_result = pkctx.verify(sig, data); } } - return kSignOk; + return Error::Ok; } void Verify::VerifyFinal(const FunctionCallbackInfo& args) { @@ -545,47 +482,42 @@ void Verify::VerifyFinal(const FunctionCallbackInfo& args) { unsigned int offset = 0; auto data = KeyObjectData::GetPublicOrPrivateKeyFromJs(args, &offset); - if (!data) return; - const auto& pkey = data.GetAsymmetricKey(); - if (!pkey) + if (!data) [[unlikely]] + return; + const auto& key = data.GetAsymmetricKey(); + if (!key) [[unlikely]] return; - if (IsOneShot(pkey)) { + if (key.isOneShotVariant()) [[unlikely]] { THROW_ERR_CRYPTO_UNSUPPORTED_OPERATION(env); return; } ArrayBufferOrViewContents hbuf(args[offset]); - if (!hbuf.CheckSizeInt32()) [[unlikely]] + if (!hbuf.CheckSizeInt32()) [[unlikely]] { return THROW_ERR_OUT_OF_RANGE(env, "buffer is too big"); - - int padding = GetDefaultSignPadding(pkey); - if (!args[offset + 1]->IsUndefined()) { - CHECK(args[offset + 1]->IsInt32()); - padding = args[offset + 1].As()->Value(); } - Maybe salt_len = Nothing(); - if (!args[offset + 2]->IsUndefined()) { - CHECK(args[offset + 2]->IsInt32()); - salt_len = Just(args[offset + 2].As()->Value()); + int padding = GetPaddingFromJS(key, args[offset + 1]); + std::optional salt_len = GetSaltLenFromJS(args[offset + 2]); + DSASigEnc dsa_sig_enc = GetDSASigEncFromJS(args[offset + 3]); + if (dsa_sig_enc == DSASigEnc::Invalid) [[unlikely]] { + THROW_ERR_OUT_OF_RANGE(env, "invalid signature encoding"); + return; } - CHECK(args[offset + 3]->IsInt32()); - DSASigEnc dsa_sig_enc = - static_cast(args[offset + 3].As()->Value()); - ByteSource signature = hbuf.ToByteSource(); - if (dsa_sig_enc == kSigEncP1363) { - signature = ConvertSignatureToDER(pkey, hbuf.ToByteSource()); - if (signature.data() == nullptr) - return crypto::CheckThrow(env, Error::kSignMalformedSignature); + if (dsa_sig_enc == DSASigEnc::P1363) { + signature = ConvertSignatureToDER(key, hbuf.ToByteSource()); + if (signature.data() == nullptr) [[unlikely]] { + return crypto::CheckThrow(env, Error::MalformedSignature); + } } bool verify_result; - Error err = verify->VerifyFinal(pkey, signature, padding, - salt_len, &verify_result); - if (err != kSignOk) + Error err = + verify->VerifyFinal(key, signature, padding, salt_len, &verify_result); + if (err != Error::Ok) [[unlikely]] return crypto::CheckThrow(env, err); args.GetReturnValue().Set(verify_result); } @@ -633,7 +565,7 @@ Maybe SignTraits::AdditionalConfig( static_cast(args[offset].As()->Value()); unsigned int keyParamOffset = offset + 1; - if (params->mode == SignConfiguration::kVerify) { + if (params->mode == SignConfiguration::Mode::Verify) { auto data = KeyObjectData::GetPublicOrPrivateKeyFromJs(args, &keyParamOffset); if (!data) return Nothing(); @@ -655,8 +587,8 @@ Maybe SignTraits::AdditionalConfig( if (args[offset + 6]->IsString()) { Utf8Value digest(env->isolate(), args[offset + 6]); - params->digest = EVP_get_digestbyname(*digest); - if (params->digest == nullptr) { + params->digest = ncrypto::getDigestByName(digest.ToStringView()); + if (params->digest == nullptr) [[unlikely]] { THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *digest); return Nothing(); } @@ -664,24 +596,24 @@ Maybe SignTraits::AdditionalConfig( if (args[offset + 7]->IsInt32()) { // Salt length params->flags |= SignConfiguration::kHasSaltLength; - params->salt_length = args[offset + 7].As()->Value(); + params->salt_length = + GetSaltLenFromJS(args[offset + 7]).value_or(params->salt_length); } if (args[offset + 8]->IsUint32()) { // Padding params->flags |= SignConfiguration::kHasPadding; - params->padding = args[offset + 8].As()->Value(); + params->padding = + GetPaddingFromJS(params->key.GetAsymmetricKey(), args[offset + 8]); } if (args[offset + 9]->IsUint32()) { // DSA Encoding - params->dsa_encoding = - static_cast(args[offset + 9].As()->Value()); - if (params->dsa_encoding != kSigEncDER && - params->dsa_encoding != kSigEncP1363) { + params->dsa_encoding = GetDSASigEncFromJS(args[offset + 9]); + if (params->dsa_encoding == DSASigEnc::Invalid) [[unlikely]] { THROW_ERR_OUT_OF_RANGE(env, "invalid signature encoding"); return Nothing(); } } - if (params->mode == SignConfiguration::kVerify) { + if (params->mode == SignConfiguration::Mode::Verify) { ArrayBufferOrViewContents signature(args[offset + 10]); if (!signature.CheckSizeInt32()) [[unlikely]] { THROW_ERR_OUT_OF_RANGE(env, "signature is too big"); @@ -708,97 +640,69 @@ bool SignTraits::DeriveBits( const SignConfiguration& params, ByteSource* out) { ClearErrorOnReturn clear_error_on_return; - EVPMDCtxPointer context(EVP_MD_CTX_new()); - EVP_PKEY_CTX* ctx = nullptr; - + auto context = EVPMDCtxPointer::New(); + if (!context) [[unlikely]] + return false; const auto& key = params.key.GetAsymmetricKey(); - switch (params.mode) { - case SignConfiguration::kSign: - if (!EVP_DigestSignInit( - context.get(), &ctx, params.digest, nullptr, key.get())) { - crypto::CheckThrow(env, SignBase::Error::kSignInit); - return false; - } - break; - case SignConfiguration::kVerify: - if (!EVP_DigestVerifyInit( - context.get(), &ctx, params.digest, nullptr, key.get())) { - crypto::CheckThrow(env, SignBase::Error::kSignInit); - return false; - } - break; + auto ctx = ([&] { + switch (params.mode) { + case SignConfiguration::Mode::Sign: + return context.signInit(key, params.digest); + case SignConfiguration::Mode::Verify: + return context.verifyInit(key, params.digest); + } + UNREACHABLE(); + })(); + + if (!ctx.has_value()) [[unlikely]] { + crypto::CheckThrow(env, SignBase::Error::Init); + return false; } int padding = params.flags & SignConfiguration::kHasPadding ? params.padding - : GetDefaultSignPadding(key); + : key.getDefaultSignPadding(); - Maybe salt_length = params.flags & SignConfiguration::kHasSaltLength - ? Just(params.salt_length) : Nothing(); + std::optional salt_length = + params.flags & SignConfiguration::kHasSaltLength + ? std::optional(params.salt_length) + : std::nullopt; - if (!ApplyRSAOptions(key, ctx, padding, salt_length)) { - crypto::CheckThrow(env, SignBase::Error::kSignPrivateKey); + if (!ApplyRSAOptions(key, *ctx, padding, salt_length)) { + crypto::CheckThrow(env, SignBase::Error::PrivateKey); return false; } switch (params.mode) { - case SignConfiguration::kSign: { - if (IsOneShot(key)) { - size_t len; - if (!EVP_DigestSign( - context.get(), - nullptr, - &len, - params.data.data(), - params.data.size())) { - crypto::CheckThrow(env, SignBase::Error::kSignPrivateKey); - return false; - } - ByteSource::Builder buf(len); - if (!EVP_DigestSign(context.get(), - buf.data(), - &len, - params.data.data(), - params.data.size())) { - crypto::CheckThrow(env, SignBase::Error::kSignPrivateKey); + case SignConfiguration::Mode::Sign: { + if (key.isOneShotVariant()) { + auto data = context.signOneShot(params.data); + if (!data) [[unlikely]] { + crypto::CheckThrow(env, SignBase::Error::PrivateKey); return false; } - *out = std::move(buf).release(len); + *out = ByteSource::Allocated(data.release()); } else { - size_t len; - if (!EVP_DigestSignUpdate( - context.get(), - params.data.data(), - params.data.size()) || - !EVP_DigestSignFinal(context.get(), nullptr, &len)) { - crypto::CheckThrow(env, SignBase::Error::kSignPrivateKey); - return false; - } - ByteSource::Builder buf(len); - if (!EVP_DigestSignFinal( - context.get(), buf.data(), &len)) { - crypto::CheckThrow(env, SignBase::Error::kSignPrivateKey); + auto data = context.sign(params.data); + if (!data) [[unlikely]] { + crypto::CheckThrow(env, SignBase::Error::PrivateKey); return false; } + auto bs = ByteSource::Allocated(data.release()); if (UseP1363Encoding(key, params.dsa_encoding)) { - *out = ConvertSignatureToP1363(env, key, std::move(buf).release()); + *out = ConvertSignatureToP1363(env, key, std::move(bs)); } else { - *out = std::move(buf).release(len); + *out = std::move(bs); } } break; } - case SignConfiguration::kVerify: { + case SignConfiguration::Mode::Verify: { ByteSource::Builder buf(1); buf.data()[0] = 0; - if (EVP_DigestVerify( - context.get(), - params.signature.data(), - params.signature.size(), - params.data.data(), - params.data.size()) == 1) { + if (context.verify(params.data, params.signature)) { buf.data()[0] = 1; } *out = std::move(buf).release(); @@ -812,9 +716,9 @@ MaybeLocal SignTraits::EncodeOutput(Environment* env, const SignConfiguration& params, ByteSource* out) { switch (params.mode) { - case SignConfiguration::kSign: + case SignConfiguration::Mode::Sign: return out->ToArrayBuffer(env); - case SignConfiguration::kVerify: + case SignConfiguration::Mode::Verify: return Boolean::New(env->isolate(), out->data()[0] == 1); } UNREACHABLE(); diff --git a/src/crypto/crypto_sig.h b/src/crypto/crypto_sig.h index 3a3c27b4e8e748..36c51b07bb5692 100644 --- a/src/crypto/crypto_sig.h +++ b/src/crypto/crypto_sig.h @@ -13,27 +13,24 @@ namespace node { namespace crypto { static const unsigned int kNoDsaSignature = static_cast(-1); -enum DSASigEnc { - kSigEncDER, - kSigEncP1363 -}; +enum class DSASigEnc { DER, P1363, Invalid }; class SignBase : public BaseObject { public: - enum Error { - kSignOk, - kSignUnknownDigest, - kSignInit, - kSignNotInitialised, - kSignUpdate, - kSignPrivateKey, - kSignPublicKey, - kSignMalformedSignature + enum class Error { + Ok, + UnknownDigest, + Init, + NotInitialised, + Update, + PrivateKey, + PublicKey, + MalformedSignature }; SignBase(Environment* env, v8::Local wrap); - Error Init(const char* sign_type); + Error Init(std::string_view digest); Error Update(const char* data, size_t len); // TODO(joyeecheung): track the memory used by OpenSSL types @@ -45,7 +42,7 @@ class SignBase : public BaseObject { ncrypto::EVPMDCtxPointer mdctx_; }; -class Sign : public SignBase { +class Sign final : public SignBase { public: static void Initialize(Environment* env, v8::Local target); static void RegisterExternalReferences(ExternalReferenceRegistry* registry); @@ -54,15 +51,14 @@ class Sign : public SignBase { Error error; std::unique_ptr signature; - explicit SignResult( - Error err, - std::unique_ptr&& sig = nullptr) - : error(err), signature(std::move(sig)) {} + inline explicit SignResult( + Error err, std::unique_ptr&& sig = nullptr) + : error(err), signature(std::move(sig)) {} }; SignResult SignFinal(const ncrypto::EVPKeyPointer& pkey, int padding, - const v8::Maybe& saltlen, + std::optional saltlen, DSASigEnc dsa_sig_enc); static void SignSync(const v8::FunctionCallbackInfo& args); @@ -76,7 +72,7 @@ class Sign : public SignBase { Sign(Environment* env, v8::Local wrap); }; -class Verify : public SignBase { +class Verify final : public SignBase { public: static void Initialize(Environment* env, v8::Local target); static void RegisterExternalReferences(ExternalReferenceRegistry* registry); @@ -84,7 +80,7 @@ class Verify : public SignBase { Error VerifyFinal(const ncrypto::EVPKeyPointer& key, const ByteSource& sig, int padding, - const v8::Maybe& saltlen, + std::optional saltlen, bool* verify_result); static void VerifySync(const v8::FunctionCallbackInfo& args); @@ -99,10 +95,7 @@ class Verify : public SignBase { }; struct SignConfiguration final : public MemoryRetainer { - enum Mode { - kSign, - kVerify - }; + enum class Mode { Sign, Verify }; enum Flags { kHasNone = 0, kHasSaltLength = 1, @@ -118,7 +111,7 @@ struct SignConfiguration final : public MemoryRetainer { int flags = SignConfiguration::kHasNone; int padding = 0; int salt_length = 0; - DSASigEnc dsa_encoding = kSigEncDER; + DSASigEnc dsa_encoding = DSASigEnc::DER; SignConfiguration() = default; @@ -135,8 +128,6 @@ struct SignTraits final { using AdditionalParameters = SignConfiguration; static constexpr const char* JobName = "SignJob"; -// TODO(@jasnell): Sign request vs. Verify request - static constexpr AsyncWrap::ProviderType Provider = AsyncWrap::PROVIDER_SIGNREQUEST; diff --git a/src/crypto/crypto_tls.cc b/src/crypto/crypto_tls.cc index 0f1defef16661a..7104ee19a6dd79 100644 --- a/src/crypto/crypto_tls.cc +++ b/src/crypto/crypto_tls.cc @@ -46,6 +46,7 @@ using v8::Array; using v8::ArrayBuffer; using v8::ArrayBufferView; using v8::BackingStore; +using v8::BackingStoreInitializationMode; using v8::Boolean; using v8::Context; using v8::DontDelete; @@ -60,6 +61,7 @@ using v8::Isolate; using v8::Local; using v8::MaybeLocal; using v8::Null; +using v8::Number; using v8::Object; using v8::PropertyAttribute; using v8::ReadOnly; @@ -161,7 +163,7 @@ int NewSessionCallback(SSL* s, SSL_SESSION* sess) { HandleScope handle_scope(env->isolate()); Context::Scope context_scope(env->context()); - if (!w->has_session_callbacks()) + if (!w->has_session_callbacks()) [[unlikely]] return 0; // Check if session is small enough to be stored @@ -225,10 +227,9 @@ int SSLCertCallback(SSL* s, void* arg) { auto servername = SSLPointer::GetServerName(s); Local servername_str = - !servername.has_value() ? String::Empty(env->isolate()) - : OneByteString(env->isolate(), - servername.value().data(), - servername.value().length()); + !servername.has_value() + ? String::Empty(env->isolate()) + : OneByteString(env->isolate(), servername.value()); Local ocsp = Boolean::New( env->isolate(), SSL_get_tlsext_status_type(s) == TLSEXT_STATUSTYPE_ocsp); @@ -257,30 +258,27 @@ int SelectALPNCallback( Environment* env = w->env(); HandleScope handle_scope(env->isolate()); - Local callback_arg = - Buffer::Copy(env, reinterpret_cast(in), inlen) - .ToLocalChecked(); + Local callback_arg; + Local callback_result; - MaybeLocal maybe_callback_result = - w->MakeCallback(env->alpn_callback_string(), 1, &callback_arg); - - if (maybe_callback_result.IsEmpty()) [[unlikely]] { - // Implies the callback didn't return, because some exception was thrown - // during processing, e.g. if callback returned an invalid ALPN value. + if (!Buffer::Copy(env, reinterpret_cast(in), inlen) + .ToLocal(&callback_arg)) { return SSL_TLSEXT_ERR_ALERT_FATAL; } - Local callback_result = maybe_callback_result.ToLocalChecked(); + if (!w->MakeCallback(env->alpn_callback_string(), 1, &callback_arg) + .ToLocal(&callback_result)) { + return SSL_TLSEXT_ERR_ALERT_FATAL; + } - if (callback_result->IsUndefined()) { + if (callback_result->IsUndefined() && !callback_result->IsNumber()) { // If you set an ALPN callback, but you return undefined for an ALPN // request, you're rejecting all proposed ALPN protocols, and so we send // a fatal alert: return SSL_TLSEXT_ERR_ALERT_FATAL; } - CHECK(callback_result->IsNumber()); - unsigned int result_int = callback_result.As()->Value(); + unsigned int result_int = callback_result.As()->Value(); // The callback returns an offset into the given buffer, for the selected // protocol that should be returned. We then set outlen & out to point @@ -1087,10 +1085,10 @@ int TLSWrap::DoWrite(WriteWrap* w, // and copying it when it could just be used. if (nonempty_count != 1) { - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env()->isolate_data()); - bs = ArrayBuffer::NewBackingStore(env()->isolate(), length); - } + bs = ArrayBuffer::NewBackingStore( + env()->isolate(), + length, + BackingStoreInitializationMode::kUninitialized); size_t offset = 0; for (i = 0; i < count; i++) { memcpy(static_cast(bs->Data()) + offset, @@ -1107,8 +1105,10 @@ int TLSWrap::DoWrite(WriteWrap* w, written = SSL_write(ssl_.get(), buf->base, buf->len); if (written == -1) { - NoArrayBufferZeroFillScope no_zero_fill_scope(env()->isolate_data()); - bs = ArrayBuffer::NewBackingStore(env()->isolate(), length); + bs = ArrayBuffer::NewBackingStore( + env()->isolate(), + length, + BackingStoreInitializationMode::kUninitialized); memcpy(bs->Data(), buf->base, buf->len); } } @@ -1750,11 +1750,8 @@ void TLSWrap::GetFinished(const FunctionCallbackInfo& args) { if (len == 0) return; - std::unique_ptr bs; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - bs = ArrayBuffer::NewBackingStore(env->isolate(), len); - } + auto bs = ArrayBuffer::NewBackingStore( + env->isolate(), len, BackingStoreInitializationMode::kUninitialized); CHECK_EQ(bs->ByteLength(), SSL_get_finished(w->ssl_.get(), bs->Data(), bs->ByteLength())); @@ -1781,11 +1778,8 @@ void TLSWrap::GetPeerFinished(const FunctionCallbackInfo& args) { if (len == 0) return; - std::unique_ptr bs; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - bs = ArrayBuffer::NewBackingStore(env->isolate(), len); - } + auto bs = ArrayBuffer::NewBackingStore( + env->isolate(), len, BackingStoreInitializationMode::kUninitialized); CHECK_EQ(bs->ByteLength(), SSL_get_peer_finished(w->ssl_.get(), bs->Data(), bs->ByteLength())); @@ -1810,11 +1804,8 @@ void TLSWrap::GetSession(const FunctionCallbackInfo& args) { if (slen <= 0) return; // Invalid or malformed session. - std::unique_ptr bs; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - bs = ArrayBuffer::NewBackingStore(env->isolate(), slen); - } + auto bs = ArrayBuffer::NewBackingStore( + env->isolate(), slen, BackingStoreInitializationMode::kUninitialized); unsigned char* p = static_cast(bs->Data()); CHECK_LT(0, i2d_SSL_SESSION(sess, &p)); @@ -1997,11 +1988,8 @@ void TLSWrap::ExportKeyingMaterial(const FunctionCallbackInfo& args) { uint32_t olen = args[0].As()->Value(); Utf8Value label(env->isolate(), args[1]); - std::unique_ptr bs; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - bs = ArrayBuffer::NewBackingStore(env->isolate(), olen); - } + auto bs = ArrayBuffer::NewBackingStore( + env->isolate(), olen, BackingStoreInitializationMode::kUninitialized); ByteSource context; bool use_context = !args[2]->IsUndefined(); diff --git a/src/crypto/crypto_tls.h b/src/crypto/crypto_tls.h index ed1ee337b42ec1..f02c37182ff189 100644 --- a/src/crypto/crypto_tls.h +++ b/src/crypto/crypto_tls.h @@ -58,15 +58,17 @@ class TLSWrap : public AsyncWrap, ~TLSWrap() override; - bool is_cert_cb_running() const { return cert_cb_running_; } - bool is_waiting_cert_cb() const { return cert_cb_ != nullptr; } - bool has_session_callbacks() const { return session_callbacks_; } - void set_cert_cb_running(bool on = true) { cert_cb_running_ = on; } - void set_awaiting_new_session(bool on = true) { awaiting_new_session_ = on; } - void enable_session_callbacks() { session_callbacks_ = true; } - bool is_server() const { return kind_ == Kind::kServer; } - bool is_client() const { return kind_ == Kind::kClient; } - bool is_awaiting_new_session() const { return awaiting_new_session_; } + inline bool is_cert_cb_running() const { return cert_cb_running_; } + inline bool is_waiting_cert_cb() const { return cert_cb_ != nullptr; } + inline bool has_session_callbacks() const { return session_callbacks_; } + inline void set_cert_cb_running(bool on = true) { cert_cb_running_ = on; } + inline void set_awaiting_new_session(bool on = true) { + awaiting_new_session_ = on; + } + inline void enable_session_callbacks() { session_callbacks_ = true; } + inline bool is_server() const { return kind_ == Kind::kServer; } + inline bool is_client() const { return kind_ == Kind::kClient; } + inline bool is_awaiting_new_session() const { return awaiting_new_session_; } // Implement StreamBase: bool IsAlive() override; @@ -128,7 +130,7 @@ class TLSWrap : public AsyncWrap, // Alternative to StreamListener::stream(), that returns a StreamBase instead // of a StreamResource. - StreamBase* underlying_stream() const { + inline StreamBase* underlying_stream() const { return static_cast(stream()); } diff --git a/src/crypto/crypto_util.cc b/src/crypto/crypto_util.cc index 60610b1b795c9b..61f8cbf6703b50 100644 --- a/src/crypto/crypto_util.cc +++ b/src/crypto/crypto_util.cc @@ -630,17 +630,12 @@ Maybe SetEncodedValue(Environment* env, : Nothing(); } -bool SetRsaOaepLabel(const EVPKeyCtxPointer& ctx, const ByteSource& label) { +bool SetRsaOaepLabel(EVPKeyCtxPointer* ctx, const ByteSource& label) { if (label.size() != 0) { // OpenSSL takes ownership of the label, so we need to create a copy. - void* label_copy = OPENSSL_memdup(label.data(), label.size()); - CHECK_NOT_NULL(label_copy); - int ret = EVP_PKEY_CTX_set0_rsa_oaep_label( - ctx.get(), static_cast(label_copy), label.size()); - if (ret <= 0) { - OPENSSL_free(label_copy); - return false; - } + auto dup = ncrypto::DataPointer::Copy(label); + if (!dup) return false; + return ctx->setRsaOaepLabel(std::move(dup)); } return true; } diff --git a/src/crypto/crypto_util.h b/src/crypto/crypto_util.h index a5967c7d24b836..e3c9a82c17b382 100644 --- a/src/crypto/crypto_util.h +++ b/src/crypto/crypto_util.h @@ -61,10 +61,9 @@ void InitCryptoOnce(); void InitCrypto(v8::Local target); -extern void UseExtraCaCerts(const std::string& file); +extern void UseExtraCaCerts(std::string_view file); int PasswordCallback(char* buf, int size, int rwflag, void* u); - int NoPasswordCallback(char* buf, int size, int rwflag, void* u); // Decode is used by the various stream-based crypto utilities to decode @@ -165,7 +164,7 @@ T* MallocOpenSSL(size_t count) { // A helper class representing a read-only byte array. When deallocated, its // contents are zeroed. -class ByteSource { +class ByteSource final { public: class Builder { public: @@ -224,17 +223,25 @@ class ByteSource { ByteSource& operator=(const ByteSource&) = delete; template - const T* data() const { + inline const T* data() const { return reinterpret_cast(data_); } - size_t size() const { return size_; } + template + operator ncrypto::Buffer() const { + return ncrypto::Buffer{ + .data = data(), + .len = size(), + }; + } + + inline size_t size() const { return size_; } - bool empty() const { return size_ == 0; } + inline bool empty() const { return size_ == 0; } - operator bool() const { return data_ != nullptr; } + inline operator bool() const { return data_ != nullptr; } - ncrypto::BignumPointer ToBN() const { + inline ncrypto::BignumPointer ToBN() const { return ncrypto::BignumPointer(data(), size()); } @@ -518,7 +525,7 @@ void ThrowCryptoError(Environment* env, unsigned long err, // NOLINT(runtime/int) const char* message = nullptr); -class CipherPushContext { +class CipherPushContext final { public: inline explicit CipherPushContext(Environment* env) : list_(env->isolate()), env_(env) {} @@ -546,16 +553,13 @@ void array_push_back(const TypeName* evp_ref, const char* from, const char* to, void* arg) { - if (!from) - return; + if (!from) return; const TypeName* real_instance = getbyname(from); - if (!real_instance) - return; + if (!real_instance) return; const char* real_name = getname(real_instance); - if (!real_name) - return; + if (!real_name) return; // EVP_*_fetch() does not support alias names, so we need to pass it the // real/original algorithm name. @@ -564,8 +568,7 @@ void array_push_back(const TypeName* evp_ref, // algorithms are used internally by OpenSSL and are also passed to this // callback). TypeName* fetched = fetch_type(nullptr, real_name, nullptr); - if (!fetched) - return; + if (!fetched) return; free_type(fetched); static_cast(arg)->push_back(from); @@ -576,8 +579,7 @@ void array_push_back(const TypeName* evp_ref, const char* from, const char* to, void* arg) { - if (!from) - return; + if (!from) return; static_cast(arg)->push_back(from); } #endif @@ -590,7 +592,7 @@ inline bool IsAnyBufferSource(v8::Local arg) { } template -class ArrayBufferOrViewContents { +class ArrayBufferOrViewContents final { public: ArrayBufferOrViewContents() = default; ArrayBufferOrViewContents(const ArrayBufferOrViewContents&) = delete; @@ -707,8 +709,7 @@ v8::Maybe SetEncodedValue(Environment* env, const BIGNUM* bn, int size = 0); -bool SetRsaOaepLabel(const ncrypto::EVPKeyCtxPointer& rsa, - const ByteSource& label); +bool SetRsaOaepLabel(ncrypto::EVPKeyCtxPointer* rsa, const ByteSource& label); namespace Util { void Initialize(Environment* env, v8::Local target); diff --git a/src/crypto/crypto_x509.cc b/src/crypto/crypto_x509.cc index 3465454e4de4a7..782eced277518d 100644 --- a/src/crypto/crypto_x509.cc +++ b/src/crypto/crypto_x509.cc @@ -21,13 +21,12 @@ using ncrypto::ClearErrorOnReturn; using ncrypto::DataPointer; using ncrypto::ECKeyPointer; using ncrypto::SSLPointer; -using ncrypto::StackOfASN1; using ncrypto::X509Pointer; using ncrypto::X509View; using v8::Array; using v8::ArrayBuffer; using v8::ArrayBufferView; -using v8::BackingStore; +using v8::BackingStoreInitializationMode; using v8::Boolean; using v8::Context; using v8::Date; @@ -38,6 +37,7 @@ using v8::FunctionTemplate; using v8::Integer; using v8::Isolate; using v8::Local; +using v8::LocalVector; using v8::MaybeLocal; using v8::NewStringType; using v8::Object; @@ -55,14 +55,13 @@ ManagedX509::ManagedX509(const ManagedX509& that) { ManagedX509& ManagedX509::operator=(const ManagedX509& that) { cert_.reset(that.get()); - - if (cert_) + if (cert_) [[likely]] X509_up_ref(cert_.get()); - return *this; } void ManagedX509::MemoryInfo(MemoryTracker* tracker) const { + if (!cert_) return; // This is an approximation based on the der encoding size. int size = i2d_X509(cert_.get(), nullptr); tracker->TrackFieldWithSize("cert", size); @@ -94,7 +93,8 @@ void Fingerprint(const FunctionCallbackInfo& args) { } MaybeLocal ToV8Value(Local context, BIOPointer&& bio) { - if (!bio) return {}; + if (!bio) [[unlikely]] + return {}; BUF_MEM* mem = bio; Local ret; if (!String::NewFromUtf8(context->GetIsolate(), @@ -135,7 +135,7 @@ MaybeLocal ToV8Value(Local context, const ASN1_STRING* str) { // not escape anything. unsigned char* value_str; int value_str_size = ASN1_STRING_to_UTF8(&value_str, str); - if (value_str_size < 0) { + if (value_str_size < 0) [[unlikely]] { return Undefined(context->GetIsolate()); } DataPointer free_value_str(value_str, value_str_size); @@ -152,7 +152,8 @@ MaybeLocal ToV8Value(Local context, const ASN1_STRING* str) { } MaybeLocal ToV8Value(Local context, const BIOPointer& bio) { - if (!bio) return {}; + if (!bio) [[unlikely]] + return {}; BUF_MEM* mem = bio; Local ret; if (!String::NewFromUtf8(context->GetIsolate(), @@ -165,7 +166,8 @@ MaybeLocal ToV8Value(Local context, const BIOPointer& bio) { } MaybeLocal ToBuffer(Environment* env, BIOPointer* bio) { - if (bio == nullptr || !*bio) return {}; + if (bio == nullptr || !*bio) [[unlikely]] + return {}; BUF_MEM* mem = *bio; auto backing = ArrayBuffer::NewBackingStore( mem->data, @@ -183,7 +185,8 @@ MaybeLocal ToBuffer(Environment* env, BIOPointer* bio) { MaybeLocal GetDer(Environment* env, const X509View& view) { Local ret; auto bio = view.toDER(); - if (!bio) return Undefined(env->isolate()); + if (!bio) [[unlikely]] + return Undefined(env->isolate()); if (!ToBuffer(env, &bio).ToLocal(&ret)) { return {}; } @@ -194,7 +197,8 @@ MaybeLocal GetSubjectAltNameString(Environment* env, const X509View& view) { Local ret; auto bio = view.getSubjectAltName(); - if (!bio) return Undefined(env->isolate()); + if (!bio) [[unlikely]] + return Undefined(env->isolate()); if (!ToV8Value(env->context(), bio).ToLocal(&ret)) return {}; return ret; } @@ -202,7 +206,8 @@ MaybeLocal GetSubjectAltNameString(Environment* env, MaybeLocal GetInfoAccessString(Environment* env, const X509View& view) { Local ret; auto bio = view.getInfoAccess(); - if (!bio) return Undefined(env->isolate()); + if (!bio) [[unlikely]] + return Undefined(env->isolate()); if (!ToV8Value(env->context(), bio).ToLocal(&ret)) { return {}; } @@ -212,7 +217,8 @@ MaybeLocal GetInfoAccessString(Environment* env, const X509View& view) { MaybeLocal GetValidFrom(Environment* env, const X509View& view) { Local ret; auto bio = view.getValidFrom(); - if (!bio) return Undefined(env->isolate()); + if (!bio) [[unlikely]] + return Undefined(env->isolate()); if (!ToV8Value(env->context(), bio).ToLocal(&ret)) { return {}; } @@ -222,7 +228,8 @@ MaybeLocal GetValidFrom(Environment* env, const X509View& view) { MaybeLocal GetValidTo(Environment* env, const X509View& view) { Local ret; auto bio = view.getValidTo(); - if (!bio) return Undefined(env->isolate()); + if (!bio) [[unlikely]] + return Undefined(env->isolate()); if (!ToV8Value(env->context(), bio).ToLocal(&ret)) { return {}; } @@ -248,25 +255,12 @@ MaybeLocal GetSerialNumber(Environment* env, const X509View& view) { } MaybeLocal GetKeyUsage(Environment* env, const X509View& cert) { - StackOfASN1 eku(static_cast( - X509_get_ext_d2i(cert.get(), NID_ext_key_usage, nullptr, nullptr))); - if (eku) { - const int count = sk_ASN1_OBJECT_num(eku.get()); - MaybeStackBuffer, 16> ext_key_usage(count); - char buf[256]; - - int j = 0; - for (int i = 0; i < count; i++) { - if (OBJ_obj2txt( - buf, sizeof(buf), sk_ASN1_OBJECT_value(eku.get(), i), 1) >= 0) { - ext_key_usage[j++] = OneByteString(env->isolate(), buf); - } - } - - return Array::New(env->isolate(), ext_key_usage.out(), count); - } - - return Undefined(env->isolate()); + LocalVector vec(env->isolate()); + bool res = cert.enumUsages([&](std::string_view view) { + vec.push_back(OneByteString(env->isolate(), view)); + }); + if (!res) return Undefined(env->isolate()); + return Array::New(env->isolate(), vec.data(), vec.size()); } void Pem(const FunctionCallbackInfo& args) { @@ -387,7 +381,7 @@ void PublicKey(const FunctionCallbackInfo& args) { // TODO(tniessen): consider checking X509_get_pubkey() when the // X509Certificate object is being created. auto result = cert->view().getPublicKey(); - if (!result.value) { + if (!result.value) [[unlikely]] { ThrowCryptoError(env, result.error.value_or(0)); return; } @@ -547,7 +541,9 @@ void Parse(const FunctionCallbackInfo& args) { .len = buf.length(), }); - if (!result.value) return ThrowCryptoError(env, result.error.value_or(0)); + if (!result.value) [[unlikely]] { + return ThrowCryptoError(env, result.error.value_or(0)); + } if (X509Certificate::New(env, std::move(result.value)).ToLocal(&cert)) { args.GetReturnValue().Set(cert); @@ -571,7 +567,8 @@ bool Set(Environment* env, Local name, MaybeLocal maybe_value) { Local value; - if (!maybe_value.ToLocal(&value)) return false; + if (!maybe_value.ToLocal(&value)) [[unlikely]] + return false; // Undefined is ignored, but still considered successful if (value->IsUndefined()) return true; @@ -585,7 +582,8 @@ bool Set(Environment* env, uint32_t index, MaybeLocal maybe_value) { Local value; - if (!maybe_value.ToLocal(&value)) return false; + if (!maybe_value.ToLocal(&value)) [[unlikely]] + return false; // Undefined is ignored, but still considered successful if (value->IsUndefined()) return true; @@ -664,33 +662,32 @@ static MaybeLocal GetX509NameObject(Environment* env, return result; } -MaybeLocal GetPubKey(Environment* env, OSSL3_CONST RSA* rsa) { +MaybeLocal GetPubKey(Environment* env, const ncrypto::Rsa& rsa) { int size = i2d_RSA_PUBKEY(rsa, nullptr); CHECK_GE(size, 0); - std::unique_ptr bs; - { - NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data()); - bs = ArrayBuffer::NewBackingStore(env->isolate(), size); - } + auto bs = ArrayBuffer::NewBackingStore( + env->isolate(), size, BackingStoreInitializationMode::kUninitialized); - unsigned char* serialized = reinterpret_cast(bs->Data()); + auto serialized = reinterpret_cast(bs->Data()); CHECK_GE(i2d_RSA_PUBKEY(rsa, &serialized), 0); - Local ab = ArrayBuffer::New(env->isolate(), std::move(bs)); + auto ab = ArrayBuffer::New(env->isolate(), std::move(bs)); return Buffer::New(env, ab, 0, ab->ByteLength()).FromMaybe(Local()); } MaybeLocal GetModulusString(Environment* env, const BIGNUM* n) { auto bio = BIOPointer::New(n); - if (!bio) return {}; + if (!bio) [[unlikely]] + return {}; return ToV8Value(env->context(), bio); } MaybeLocal GetExponentString(Environment* env, const BIGNUM* e) { uint64_t exponent_word = static_cast(BignumPointer::GetWord(e)); auto bio = BIOPointer::NewMem(); - if (!bio) return {}; + if (!bio) [[unlikely]] + return {}; BIO_printf(bio.get(), "0x%" PRIx64, exponent_word); return ToV8Value(env->context(), bio); } @@ -699,14 +696,16 @@ MaybeLocal GetECPubKey(Environment* env, const EC_GROUP* group, OSSL3_CONST EC_KEY* ec) { const auto pubkey = ECKeyPointer::GetPublicKey(ec); - if (pubkey == nullptr) return Undefined(env->isolate()); + if (pubkey == nullptr) [[unlikely]] + return Undefined(env->isolate()); return ECPointToBuffer(env, group, pubkey, EC_KEY_get_conv_form(ec), nullptr) .FromMaybe(Local()); } MaybeLocal GetECGroupBits(Environment* env, const EC_GROUP* group) { - if (group == nullptr) return Undefined(env->isolate()); + if (group == nullptr) [[unlikely]] + return Undefined(env->isolate()); int bits = EC_GROUP_order_bits(group); if (bits <= 0) return Undefined(env->isolate()); @@ -716,10 +715,9 @@ MaybeLocal GetECGroupBits(Environment* env, const EC_GROUP* group) { template MaybeLocal GetCurveName(Environment* env, const int nid) { - const char* name = nid2string(nid); - return name != nullptr - ? MaybeLocal(OneByteString(env->isolate(), name)) - : MaybeLocal(Undefined(env->isolate())); + std::string_view name = nid2string(nid); + return name.size() ? MaybeLocal(OneByteString(env->isolate(), name)) + : MaybeLocal(Undefined(env->isolate())); } MaybeLocal X509ToObject(Environment* env, const X509View& cert) { @@ -745,68 +743,68 @@ MaybeLocal X509ToObject(Environment* env, const X509View& cert) { !Set(env, info, env->ca_string(), - Boolean::New(env->isolate(), cert.isCA()))) { + Boolean::New(env->isolate(), cert.isCA()))) [[unlikely]] { return {}; } - OSSL3_CONST EVP_PKEY* pkey = X509_get0_pubkey(cert.get()); - OSSL3_CONST RSA* rsa = nullptr; - OSSL3_CONST EC_KEY* ec = nullptr; - if (pkey != nullptr) { - switch (EVP_PKEY_id(pkey)) { - case EVP_PKEY_RSA: - rsa = EVP_PKEY_get0_RSA(pkey); - break; - case EVP_PKEY_EC: - ec = EVP_PKEY_get0_EC_KEY(pkey); - break; - } + if (!cert.ifRsa([&](const ncrypto::Rsa& rsa) { + auto pub_key = rsa.getPublicKey(); + if (!Set(env, + info, + env->modulus_string(), + GetModulusString(env, pub_key.n)) || + !Set(env, + info, + env->bits_string(), + Integer::New(env->isolate(), + BignumPointer::GetBitCount(pub_key.n))) || + !Set(env, + info, + env->exponent_string(), + GetExponentString(env, pub_key.e)) || + !Set(env, info, env->pubkey_string(), GetPubKey(env, rsa))) + [[unlikely]] { + return false; + } + return true; + })) [[unlikely]] { + return {}; } - if (rsa) { - const BIGNUM* n; - const BIGNUM* e; - RSA_get0_key(rsa, &n, &e, nullptr); - if (!Set( - env, info, env->modulus_string(), GetModulusString(env, n)) || - !Set( - env, - info, - env->bits_string(), - Integer::New(env->isolate(), BignumPointer::GetBitCount(n))) || - !Set( - env, info, env->exponent_string(), GetExponentString(env, e)) || - !Set(env, info, env->pubkey_string(), GetPubKey(env, rsa))) { - return {}; - } - } else if (ec) { - const auto group = ECKeyPointer::GetGroup(ec); + if (!cert.ifEc([&](const ncrypto::Ec& ec) { + const auto group = ec.getGroup(); - if (!Set( - env, info, env->bits_string(), GetECGroupBits(env, group)) || - !Set( - env, info, env->pubkey_string(), GetECPubKey(env, group, ec))) { - return {}; - } + if (!Set( + env, info, env->bits_string(), GetECGroupBits(env, group)) || + !Set( + env, info, env->pubkey_string(), GetECPubKey(env, group, ec))) + [[unlikely]] { + return false; + } - const int nid = EC_GROUP_get_curve_name(group); - if (nid != 0) { - // Curve is well-known, get its OID and NIST nick-name (if it has one). - - if (!Set(env, - info, - env->asn1curve_string(), - GetCurveName(env, nid)) || - !Set(env, - info, - env->nistcurve_string(), - GetCurveName(env, nid))) { - return {}; - } - } else { - // Unnamed curves can be described by their mathematical properties, - // but aren't used much (at all?) with X.509/TLS. Support later if needed. - } + const int nid = ec.getCurve(); + if (nid != 0) [[likely]] { + // Curve is well-known, get its OID and NIST nick-name (if it has + // one). + + if (!Set(env, + info, + env->asn1curve_string(), + GetCurveName(env, nid)) || + !Set(env, + info, + env->nistcurve_string(), + GetCurveName(env, nid))) + [[unlikely]] { + return false; + } + } + // Unnamed curves can be described by their mathematical properties, + // but aren't used much (at all?) with X.509/TLS. Support later if + // needed. + return true; + })) [[unlikely]] { + return {}; } if (!Set( @@ -828,7 +826,8 @@ MaybeLocal X509ToObject(Environment* env, const X509View& cert) { env, info, env->ext_key_usage_string(), GetKeyUsage(env, cert)) || !Set( env, info, env->serial_number_string(), GetSerialNumber(env, cert)) || - !Set(env, info, env->raw_string(), GetDer(env, cert))) { + !Set(env, info, env->raw_string(), GetDer(env, cert))) + [[unlikely]] { return {}; } @@ -910,7 +909,8 @@ MaybeLocal X509Certificate::New(Environment* env, MaybeLocal X509Certificate::GetCert(Environment* env, const SSLPointer& ssl) { auto cert = X509View::From(ssl); - if (!cert) return {}; + if (!cert) [[unlikely]] + return {}; return New(env, cert.clone()); } @@ -930,7 +930,7 @@ MaybeLocal X509Certificate::GetPeerCert(Environment* env, if (!cert && (ssl_certs == nullptr || sk_X509_num(ssl_certs) == 0)) return MaybeLocal(); - if (!cert) { + if (!cert) [[unlikely]] { cert.reset(sk_X509_value(ssl_certs, 0)); sk_X509_delete(ssl_certs, 0); } @@ -945,7 +945,8 @@ v8::MaybeLocal X509Certificate::toObject(Environment* env) { v8::MaybeLocal X509Certificate::toObject(Environment* env, const X509View& cert) { - if (!cert) return {}; + if (!cert) [[unlikely]] + return {}; return X509ToObject(env, cert).FromMaybe(Local()); } @@ -979,7 +980,7 @@ X509Certificate::X509CertificateTransferData::Deserialize( Environment* env, Local context, std::unique_ptr self) { - if (context != env->context()) { + if (context != env->context()) [[unlikely]] { THROW_ERR_MESSAGE_TARGET_CONTEXT_UNAVAILABLE(env); return {}; } diff --git a/src/crypto/crypto_x509.h b/src/crypto/crypto_x509.h index 54f4b2a40732d2..39f1878be1925f 100644 --- a/src/crypto/crypto_x509.h +++ b/src/crypto/crypto_x509.h @@ -25,10 +25,10 @@ class ManagedX509 final : public MemoryRetainer { ManagedX509(const ManagedX509& that); ManagedX509& operator=(const ManagedX509& that); - operator bool() const { return !!cert_; } - X509* get() const { return cert_.get(); } - ncrypto::X509View view() const { return cert_; } - operator ncrypto::X509View() const { return cert_; } + inline operator bool() const { return !!cert_; } + inline X509* get() const { return cert_.get(); } + inline ncrypto::X509View view() const { return cert_; } + inline operator ncrypto::X509View() const { return cert_; } void MemoryInfo(MemoryTracker* tracker) const override; SET_MEMORY_INFO_NAME(ManagedX509) @@ -77,7 +77,7 @@ class X509Certificate final : public BaseObject { } inline ncrypto::X509View view() const { return *cert_; } - X509* get() { return cert_->get(); } + inline X509* get() { return cert_->get(); } v8::MaybeLocal toObject(Environment* env); static v8::MaybeLocal toObject(Environment* env, From 1c7c32f96128ac2ae5472dd2da425d9cf638b453 Mon Sep 17 00:00:00 2001 From: Burkov Egor Date: Wed, 22 Jan 2025 16:16:31 +0300 Subject: [PATCH 24/38] src: add nullptr handling from X509_STORE_new() In openssl we should check result of X509_STORE_new() for nullptr Refs: https://github.com/nodejs/node/issues/56694 PR-URL: https://github.com/nodejs/node/pull/56700 Reviewed-By: James M Snell Reviewed-By: Luigi Pinca --- src/crypto/crypto_context.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/crypto/crypto_context.cc b/src/crypto/crypto_context.cc index da5cebb87a3d51..af7ac2e5e8eba9 100644 --- a/src/crypto/crypto_context.cc +++ b/src/crypto/crypto_context.cc @@ -272,6 +272,7 @@ X509_STORE* NewRootCertStore() { } X509_STORE* store = X509_STORE_new(); + CHECK_NOT_NULL(store); if (*system_cert_path != '\0') { ERR_set_mark(); X509_STORE_load_locations(store, system_cert_path, nullptr); From 80c70ee84520b6f489e63c1e4298c3a296f00aab Mon Sep 17 00:00:00 2001 From: Luigi Pinca Date: Sun, 26 Jan 2025 17:41:21 +0100 Subject: [PATCH 25/38] test: do not use common.isMainThread MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `common.isMainThread` was removed in https://github.com/nodejs/node/commit/8caa1dcee63b2c6fd7a9, use the `isMainThread` export of the `worker_threads` module instead. PR-URL: https://github.com/nodejs/node/pull/56768 Reviewed-By: Michaël Zasso Reviewed-By: James M Snell Reviewed-By: Chengzhong Wu Reviewed-By: Matteo Collina --- test/parallel/test-require-resolve-opts-paths-relative.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/parallel/test-require-resolve-opts-paths-relative.js b/test/parallel/test-require-resolve-opts-paths-relative.js index 522a1fdbce82a4..13d17d478b753d 100644 --- a/test/parallel/test-require-resolve-opts-paths-relative.js +++ b/test/parallel/test-require-resolve-opts-paths-relative.js @@ -3,8 +3,9 @@ const common = require('../common'); const assert = require('assert'); const fixtures = require('../common/fixtures'); +const { isMainThread } = require('worker_threads'); -if (!common.isMainThread) +if (!isMainThread) common.skip('process.chdir is not available in Workers'); const subdir = fixtures.path('module-require', 'relative', 'subdir'); From ea7ab162d67b4122510d23e197a80ec8c5a2ecde Mon Sep 17 00:00:00 2001 From: James M Snell Date: Sun, 26 Jan 2025 09:19:39 -0800 Subject: [PATCH 26/38] test: cleanup and simplify test-crypto-aes-wrap * Add comment explaining purpose of the test * Eliminate duplicative/extraneous buffer allocations PR-URL: https://github.com/nodejs/node/pull/56748 Reviewed-By: Yagiz Nizipli Reviewed-By: Richard Lau Reviewed-By: Luigi Pinca Reviewed-By: Matteo Collina --- test/parallel/test-crypto-aes-wrap.js | 58 +++++++++++++-------------- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/test/parallel/test-crypto-aes-wrap.js b/test/parallel/test-crypto-aes-wrap.js index 6fe35258f7d6b2..21d48d8a3fbae7 100644 --- a/test/parallel/test-crypto-aes-wrap.js +++ b/test/parallel/test-crypto-aes-wrap.js @@ -1,62 +1,60 @@ 'use strict'; const common = require('../common'); -if (!common.hasCrypto) +if (!common.hasCrypto) { common.skip('missing crypto'); +} + +// Tests that the AES wrap and unwrap functions are working correctly. const assert = require('assert'); const crypto = require('crypto'); -const test = [ +const ivShort = Buffer.from('3fd838af', 'hex'); +const ivLong = Buffer.from('3fd838af4093d749', 'hex'); +const key1 = Buffer.from('b26f309fbe57e9b3bb6ae5ef31d54450', 'hex'); +const key2 = Buffer.from('40978085d68091f7dfca0d7dfc7a5ee76d2cc7f2f345a304', 'hex'); +const key3 = Buffer.from('29c9eab5ed5ad44134a1437fe2e673b4d88a5b7c72e68454fea08721392b7323', 'hex'); + +[ { algorithm: 'aes128-wrap', - key: 'b26f309fbe57e9b3bb6ae5ef31d54450', - iv: '3fd838af4093d749', + key: key1, + iv: ivLong, text: '12345678123456781234567812345678' }, { algorithm: 'id-aes128-wrap-pad', - key: 'b26f309fbe57e9b3bb6ae5ef31d54450', - iv: '3fd838af', + key: key1, + iv: ivShort, text: '12345678123456781234567812345678123' }, { algorithm: 'aes192-wrap', - key: '40978085d68091f7dfca0d7dfc7a5ee76d2cc7f2f345a304', - iv: '3fd838af4093d749', + key: key2, + iv: ivLong, text: '12345678123456781234567812345678' }, { algorithm: 'id-aes192-wrap-pad', - key: '40978085d68091f7dfca0d7dfc7a5ee76d2cc7f2f345a304', - iv: '3fd838af', + key: key2, + iv: ivShort, text: '12345678123456781234567812345678123' }, { algorithm: 'aes256-wrap', - key: '29c9eab5ed5ad44134a1437fe2e673b4d88a5b7c72e68454fea08721392b7323', - iv: '3fd838af4093d749', + key: key3, + iv: ivLong, text: '12345678123456781234567812345678' }, { algorithm: 'id-aes256-wrap-pad', - key: '29c9eab5ed5ad44134a1437fe2e673b4d88a5b7c72e68454fea08721392b7323', - iv: '3fd838af', + key: key3, + iv: ivShort, text: '12345678123456781234567812345678123' }, -]; - -test.forEach((data) => { - const cipher = crypto.createCipheriv( - data.algorithm, - Buffer.from(data.key, 'hex'), - Buffer.from(data.iv, 'hex')); - const ciphertext = cipher.update(data.text, 'utf8'); - - const decipher = crypto.createDecipheriv( - data.algorithm, - Buffer.from(data.key, 'hex'), - Buffer.from(data.iv, 'hex')); - const msg = decipher.update(ciphertext, 'buffer', 'utf8'); - - assert.strictEqual(msg, data.text, `${data.algorithm} test case failed`); +].forEach(({ algorithm, key, iv, text }) => { + const cipher = crypto.createCipheriv(algorithm, key, iv); + const decipher = crypto.createDecipheriv(algorithm, key, iv); + const msg = decipher.update(cipher.update(text, 'utf8'), 'buffer', 'utf8'); + assert.strictEqual(msg, text, `${algorithm} test case failed`); }); From a6c5ce27d39d52e8bfe04ed8d747b19eb8970960 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Sun, 26 Jan 2025 19:31:35 +0100 Subject: [PATCH 27/38] doc: improve accessibility of expandable lists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/56749 Reviewed-By: James M Snell Reviewed-By: Ulises Gascón Reviewed-By: Claudio Wunder --- doc/api_assets/api.js | 6 +++++- doc/api_assets/style.css | 17 ++++++----------- doc/template.html | 6 +++--- tools/doc/html.mjs | 12 ++++++------ 4 files changed, 20 insertions(+), 21 deletions(-) diff --git a/doc/api_assets/api.js b/doc/api_assets/api.js index 394b5ba990946c..e86f110e0346bf 100644 --- a/doc/api_assets/api.js +++ b/doc/api_assets/api.js @@ -41,6 +41,7 @@ function closeAllPickers() { for (const picker of pickers) { picker.parentNode.classList.remove('expanded'); + picker.ariaExpanded = false; } window.removeEventListener('click', closeAllPickers); @@ -58,6 +59,7 @@ for (const picker of pickers) { const parentNode = picker.parentNode; + picker.ariaExpanded = parentNode.classList.contains('expanded'); picker.addEventListener('click', function(e) { e.preventDefault(); @@ -65,7 +67,7 @@ closeAllPickers as window event trigger already closed all the pickers, if it already closed there is nothing else to do here */ - if (parentNode.classList.contains('expanded')) { + if (picker.ariaExpanded === 'true') { return; } @@ -75,9 +77,11 @@ */ requestAnimationFrame(function() { + picker.ariaExpanded = true; parentNode.classList.add('expanded'); window.addEventListener('click', closeAllPickers); window.addEventListener('keydown', onKeyDown); + parentNode.querySelector('.picker a').focus(); }); }); } diff --git a/doc/api_assets/style.css b/doc/api_assets/style.css index 28a284e3b975b8..a40990a39252a4 100644 --- a/doc/api_assets/style.css +++ b/doc/api_assets/style.css @@ -182,22 +182,15 @@ li.picker-header .picker-arrow { height: .6rem; border-top: .3rem solid transparent; border-bottom: .3rem solid transparent; - border-left: .6rem solid var(--color-links); + border-left: .6rem solid currentColor; border-right: none; margin: 0 .2rem .05rem 0; } -li.picker-header a:focus .picker-arrow, -li.picker-header a:active .picker-arrow, -li.picker-header a:hover .picker-arrow { - border-left: .6rem solid var(--white); -} - -li.picker-header.expanded a:focus .picker-arrow, -li.picker-header.expanded a:active .picker-arrow, -li.picker-header.expanded a:hover .picker-arrow, +li.picker-header.expanded .picker-arrow, +:root:not(.has-js) li.picker-header:focus-within .picker-arrow, :root:not(.has-js) li.picker-header:hover .picker-arrow { - border-top: .6rem solid var(--white); + border-top: .6rem solid currentColor; border-bottom: none; border-left: .35rem solid transparent; border-right: .35rem solid transparent; @@ -205,11 +198,13 @@ li.picker-header.expanded a:hover .picker-arrow, } li.picker-header.expanded > a, +:root:not(.has-js) li.picker-header:focus-within > a, :root:not(.has-js) li.picker-header:hover > a { border-radius: 2px 2px 0 0; } li.picker-header.expanded > .picker, +:root:not(.has-js) li.picker-header:focus-within > .picker, :root:not(.has-js) li.picker-header:hover > .picker { display: block; z-index: 1; diff --git a/doc/template.html b/doc/template.html index ab8be0e747f492..51e789b7e6168c 100644 --- a/doc/template.html +++ b/doc/template.html @@ -59,13 +59,13 @@

Node.js __VERSION__ documentation

__GTOC_PICKER__ __ALTDOCS__
  • - + Options -
    -
      +
      +
      • View on single page
      • diff --git a/tools/doc/html.mjs b/tools/doc/html.mjs index 68762d89e048ce..d61d335c7b8957 100644 --- a/tools/doc/html.mjs +++ b/tools/doc/html.mjs @@ -527,11 +527,11 @@ function altDocs(filename, docCreated, versions) { return list ? `
      • - + Other versions -
          ${list}
        +
          ${list}
      • ` : ''; } @@ -557,12 +557,12 @@ function gtocPicker(id) { return `
      • - + Index -
        ${gtoc}
        +
        ${gtoc}
      • `; } @@ -574,12 +574,12 @@ function tocPicker(id, content) { return `
      • - + Table of contents -
        ${content.tocPicker}
        +
        ${content.tocPicker.replace('
      • `; } From f1196ee3bbd115dbf0937c3a48c7b47da0274e34 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Sun, 26 Jan 2025 19:41:45 +0100 Subject: [PATCH 28/38] doc: add "Skip to content" button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/56750 Reviewed-By: James M Snell Reviewed-By: Ulises Gascón Reviewed-By: Claudio Wunder --- doc/api_assets/style.css | 13 +++++++++++++ doc/template.html | 1 + 2 files changed, 14 insertions(+) diff --git a/doc/api_assets/style.css b/doc/api_assets/style.css index a40990a39252a4..35c216bb0523fc 100644 --- a/doc/api_assets/style.css +++ b/doc/api_assets/style.css @@ -122,6 +122,19 @@ a.type { font-size: .9em; } +.skip-to-content { + position: fixed; + top: -300%; +} +.skip-to-content:focus { + display: block; + top: 0; + left: 0; + background-color: var(--green1); + padding: 1rem; + z-index: 999999; +} + #content { position: relative; } diff --git a/doc/template.html b/doc/template.html index 51e789b7e6168c..34edf068df5c8d 100644 --- a/doc/template.html +++ b/doc/template.html @@ -26,6 +26,7 @@ __JS_FLAVORED_DYNAMIC_CSS__ + Skip to content