summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.eslintrc.js1
-rw-r--r--doc/api/cli.md20
-rw-r--r--doc/api/globals.md34
-rw-r--r--doc/node.13
-rw-r--r--lib/internal/main/eval_string.js28
-rw-r--r--lib/internal/process/pre_execution.js27
-rw-r--r--src/node_options.cc3
-rw-r--r--src/node_options.h2
-rw-r--r--test/common/index.js4
-rw-r--r--test/known_issues/test-cli-print-var-crypto.js21
-rw-r--r--test/parallel/test-assert-checktag.js7
-rw-r--r--test/parallel/test-bootstrap-modules.js11
-rw-r--r--test/parallel/test-cli-eval.js66
-rw-r--r--test/parallel/test-global-webcrypto-classes.js2
-rw-r--r--test/parallel/test-global-webcrypto-disbled.js10
-rw-r--r--test/parallel/test-global.js1
16 files changed, 203 insertions, 37 deletions
diff --git a/.eslintrc.js b/.eslintrc.js
index 1dedcd1bd7..2a10875dd3 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -322,6 +322,7 @@ module.exports = {
CompressionStream: 'readable',
CountQueuingStrategy: 'readable',
CustomEvent: 'readable',
+ crypto: 'readable',
Crypto: 'readable',
CryptoKey: 'readable',
DecompressionStream: 'readable',
diff --git a/doc/api/cli.md b/doc/api/cli.md
index 18daed918d..b1b0103cfd 100644
--- a/doc/api/cli.md
+++ b/doc/api/cli.md
@@ -351,16 +351,6 @@ added:
Expose the [CustomEvent Web API][] on the global scope.
-### `--experimental-global-webcrypto`
-
-<!-- YAML
-added:
- - v17.6.0
- - v16.15.0
--->
-
-Expose the [Web Crypto API][] on the global scope.
-
### `--experimental-import-meta-resolve`
<!-- YAML
@@ -413,6 +403,14 @@ added: v18.0.0
Disable experimental support for the [Fetch API][].
+### `--no-experimental-global-webcrypto`
+
+<!-- YAML
+added: REPLACEME
+-->
+
+Disable exposition of [Web Crypto API][] on the global scope.
+
### `--no-experimental-repl-await`
<!-- YAML
@@ -1839,7 +1837,6 @@ Node.js options that are allowed are:
* `--enable-source-maps`
* `--experimental-abortcontroller`
* `--experimental-global-customevent`
-* `--experimental-global-webcrypto`
* `--experimental-import-meta-resolve`
* `--experimental-json-modules`
* `--experimental-loader`
@@ -1872,6 +1869,7 @@ Node.js options that are allowed are:
* `--no-addons`
* `--no-deprecation`
* `--no-experimental-fetch`
+* `--no-experimental-global-webcrypto`
* `--no-experimental-repl-await`
* `--no-extra-info-on-fatal-exception`
* `--no-force-async-hooks-checks`
diff --git a/doc/api/globals.md b/doc/api/globals.md
index 3e5cbcf7da..71e9a400cb 100644
--- a/doc/api/globals.md
+++ b/doc/api/globals.md
@@ -345,10 +345,14 @@ A browser-compatible implementation of [`CountQueuingStrategy`][].
added:
- v17.6.0
- v16.15.0
+changes:
+ - version: REPLACEME
+ pr-url: https://github.com/nodejs/node/pull/42083
+ description: No longer behind `--experimental-global-webcrypto` CLI flag.
-->
-> Stability: 1 - Experimental. Enable this API with the
-> [`--experimental-global-webcrypto`][] CLI flag.
+> Stability: 1 - Experimental. Disable this API with the
+> [`--no-experimental-global-webcrypto`][] CLI flag.
A browser-compatible implementation of {Crypto}. This global is available
only if the Node.js binary was compiled with including support for the
@@ -360,10 +364,14 @@ only if the Node.js binary was compiled with including support for the
added:
- v17.6.0
- v16.15.0
+changes:
+ - version: REPLACEME
+ pr-url: https://github.com/nodejs/node/pull/42083
+ description: No longer behind `--experimental-global-webcrypto` CLI flag.
-->
-> Stability: 1 - Experimental. Enable this API with the
-> [`--experimental-global-webcrypto`][] CLI flag.
+> Stability: 1 - Experimental. Disable this API with the
+> [`--no-experimental-global-webcrypto`][] CLI flag.
A browser-compatible implementation of the [Web Crypto API][].
@@ -373,10 +381,14 @@ A browser-compatible implementation of the [Web Crypto API][].
added:
- v17.6.0
- v16.15.0
+changes:
+ - version: REPLACEME
+ pr-url: https://github.com/nodejs/node/pull/42083
+ description: No longer behind `--experimental-global-webcrypto` CLI flag.
-->
-> Stability: 1 - Experimental. Enable this API with the
-> [`--experimental-global-webcrypto`][] CLI flag.
+> Stability: 1 - Experimental. Disable this API with the
+> [`--no-experimental-global-webcrypto`][] CLI flag.
A browser-compatible implementation of {CryptoKey}. This global is available
only if the Node.js binary was compiled with including support for the
@@ -725,10 +737,14 @@ The WHATWG [`structuredClone`][] method.
added:
- v17.6.0
- v16.15.0
+changes:
+ - version: REPLACEME
+ pr-url: https://github.com/nodejs/node/pull/42083
+ description: No longer behind `--experimental-global-webcrypto` CLI flag.
-->
-> Stability: 1 - Experimental. Enable this API with the
-> [`--experimental-global-webcrypto`][] CLI flag.
+> Stability: 1 - Experimental. Disable this API with the
+> [`--no-experimental-global-webcrypto`][] CLI flag.
A browser-compatible implementation of {SubtleCrypto}. This global is available
only if the Node.js binary was compiled with including support for the
@@ -870,8 +886,8 @@ A browser-compatible implementation of [`WritableStreamDefaultWriter`][].
[Web Crypto API]: webcrypto.md
[`--experimental-global-customevent`]: cli.md#--experimental-global-customevent
-[`--experimental-global-webcrypto`]: cli.md#--experimental-global-webcrypto
[`--no-experimental-fetch`]: cli.md#--no-experimental-fetch
+[`--no-experimental-global-webcrypto`]: cli.md#--no-experimental-global-webcrypto
[`AbortController`]: https://developer.mozilla.org/en-US/docs/Web/API/AbortController
[`ByteLengthQueuingStrategy`]: webstreams.md#class-bytelengthqueuingstrategy
[`CompressionStream`]: webstreams.md#class-compressionstream
diff --git a/doc/node.1 b/doc/node.1
index 50a80742c4..3e2602673a 100644
--- a/doc/node.1
+++ b/doc/node.1
@@ -165,6 +165,9 @@ Use this flag to enable ShadowRealm support.
.It Fl -no-experimental-fetch
Disable experimental support for the Fetch API.
.
+.It Fl -no-experimental-global-webcrypto
+Disable exposition of the Web Crypto API on the global scope.
+.
.It Fl -no-experimental-repl-await
Disable top-level await keyword support in REPL.
.
diff --git a/lib/internal/main/eval_string.js b/lib/internal/main/eval_string.js
index e7baef4526..e92e088b8e 100644
--- a/lib/internal/main/eval_string.js
+++ b/lib/internal/main/eval_string.js
@@ -4,6 +4,8 @@
// `--interactive`.
const {
+ ObjectDefineProperty,
+ RegExpPrototypeExec,
globalThis,
} = primordials;
@@ -25,9 +27,31 @@ const print = getOptionValue('--print');
const loadESM = getOptionValue('--import').length > 0;
if (getOptionValue('--input-type') === 'module')
evalModule(source, print);
-else
+else {
+ // For backward compatibility, we want the identifier crypto to be the
+ // `node:crypto` module rather than WebCrypto.
+ const isUsingCryptoIdentifier =
+ getOptionValue('--experimental-global-webcrypto') &&
+ RegExpPrototypeExec(/\bcrypto\b/, source) !== null;
+ const shouldDefineCrypto = isUsingCryptoIdentifier && internalBinding('config').hasOpenSSL;
+
+ if (isUsingCryptoIdentifier && !shouldDefineCrypto) {
+ // This is taken from `addBuiltinLibsToObject`.
+ const object = globalThis;
+ const name = 'crypto';
+ const setReal = (val) => {
+ // Deleting the property before re-assigning it disables the
+ // getter/setter mechanism.
+ delete object[name];
+ object[name] = val;
+ };
+ ObjectDefineProperty(object, name, { __proto__: null, set: setReal });
+ }
evalScript('[eval]',
- source,
+ shouldDefineCrypto ? (
+ print ? `let crypto=require("node:crypto");{${source}}` : `(crypto=>{{${source}}})(require('node:crypto'))`
+ ) : source,
getOptionValue('--inspect-brk'),
print,
loadESM);
+}
diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js
index 23d4dbcf6c..6ad5502b61 100644
--- a/lib/internal/process/pre_execution.js
+++ b/lib/internal/process/pre_execution.js
@@ -25,6 +25,7 @@ const {
const {
ERR_MANIFEST_ASSERT_INTEGRITY,
+ ERR_NO_CRYPTO,
} = require('internal/errors').codes;
const assert = require('internal/assert');
@@ -247,23 +248,29 @@ function setupFetch() {
// removed.
function setupWebCrypto() {
if (process.config.variables.node_no_browser_globals ||
- !getOptionValue('--experimental-global-webcrypto')) {
+ getOptionValue('--no-experimental-global-webcrypto')) {
return;
}
- let webcrypto;
- ObjectDefineProperty(globalThis, 'crypto',
- { __proto__: null, ...ObjectGetOwnPropertyDescriptor({
- get crypto() {
- webcrypto ??= require('internal/crypto/webcrypto');
- return webcrypto.crypto;
- }
- }, 'crypto') });
if (internalBinding('config').hasOpenSSL) {
- webcrypto ??= require('internal/crypto/webcrypto');
+ const webcrypto = require('internal/crypto/webcrypto');
+ ObjectDefineProperty(globalThis, 'crypto',
+ { __proto__: null, ...ObjectGetOwnPropertyDescriptor({
+ get crypto() {
+ return webcrypto.crypto;
+ }
+ }, 'crypto') });
exposeInterface(globalThis, 'Crypto', webcrypto.Crypto);
exposeInterface(globalThis, 'CryptoKey', webcrypto.CryptoKey);
exposeInterface(globalThis, 'SubtleCrypto', webcrypto.SubtleCrypto);
+ } else {
+ ObjectDefineProperty(globalThis, 'crypto',
+ { __proto__: null, ...ObjectGetOwnPropertyDescriptor({
+ get crypto() {
+ throw new ERR_NO_CRYPTO();
+ }
+ }, 'crypto') });
+
}
}
diff --git a/src/node_options.cc b/src/node_options.cc
index d25cc36fea..e5daab8019 100644
--- a/src/node_options.cc
+++ b/src/node_options.cc
@@ -370,7 +370,8 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
AddOption("--experimental-global-webcrypto",
"expose experimental Web Crypto API on the global scope",
&EnvironmentOptions::experimental_global_web_crypto,
- kAllowedInEnvironment);
+ kAllowedInEnvironment,
+ true);
AddOption("--experimental-json-modules", "", NoOp{}, kAllowedInEnvironment);
AddOption("--experimental-loader",
"use the specified module as a custom loader",
diff --git a/src/node_options.h b/src/node_options.h
index 004bcae837..bd0799829a 100644
--- a/src/node_options.h
+++ b/src/node_options.h
@@ -110,7 +110,7 @@ class EnvironmentOptions : public Options {
bool enable_source_maps = false;
bool experimental_fetch = true;
bool experimental_global_customevent = false;
- bool experimental_global_web_crypto = false;
+ bool experimental_global_web_crypto = true;
bool experimental_https_modules = false;
std::string experimental_specifier_resolution;
bool experimental_wasm_modules = false;
diff --git a/test/common/index.js b/test/common/index.js
index 310f0c30dc..b3c063b57a 100644
--- a/test/common/index.js
+++ b/test/common/index.js
@@ -357,7 +357,9 @@ if (process.env.NODE_TEST_KNOWN_GLOBALS !== '0') {
const leaked = [];
for (const val in global) {
- if (!knownGlobals.includes(global[val])) {
+ // globalThis.crypto is a getter that throws if Node.js was compiled
+ // without OpenSSL.
+ if (val !== 'crypto' && !knownGlobals.includes(global[val])) {
leaked.push(val);
}
}
diff --git a/test/known_issues/test-cli-print-var-crypto.js b/test/known_issues/test-cli-print-var-crypto.js
new file mode 100644
index 0000000000..bd81f8102b
--- /dev/null
+++ b/test/known_issues/test-cli-print-var-crypto.js
@@ -0,0 +1,21 @@
+'use strict';
+
+const common = require('../common');
+const assert = require('assert');
+
+if (!common.hasCrypto) {
+ assert.fail('When Node.js is compiled without OpenSSL, overriding the global ' +
+ 'crypto is allowed on string eval');
+}
+
+const child = require('child_process');
+const nodejs = `"${process.execPath}"`;
+
+// Trying to define a variable named `crypto` using `var` triggers an exception.
+
+child.exec(
+ `${nodejs} ` +
+ '-p "var crypto = {randomBytes:1};typeof crypto.randomBytes"',
+ common.mustSucceed((stdout) => {
+ assert.match(stdout, /^number/);
+ }));
diff --git a/test/parallel/test-assert-checktag.js b/test/parallel/test-assert-checktag.js
index c0b80adffc..a8b076049b 100644
--- a/test/parallel/test-assert-checktag.js
+++ b/test/parallel/test-assert-checktag.js
@@ -1,5 +1,10 @@
'use strict';
-require('../common');
+const common = require('../common');
+
+if (!common.hasCrypto) {
+ common.skip('missing crypto');
+}
+
const assert = require('assert');
// Disable colored output to prevent color codes from breaking assertion
diff --git a/test/parallel/test-bootstrap-modules.js b/test/parallel/test-bootstrap-modules.js
index e2fc58cdf2..88a43b5147 100644
--- a/test/parallel/test-bootstrap-modules.js
+++ b/test/parallel/test-bootstrap-modules.js
@@ -206,6 +206,17 @@ if (common.hasIntl) {
expectedModules.add('NativeModule url');
}
+if (common.hasCrypto) {
+ expectedModules.add('Internal Binding crypto')
+ .add('NativeModule internal/crypto/hash')
+ .add('NativeModule internal/crypto/hashnames')
+ .add('NativeModule internal/crypto/keys')
+ .add('NativeModule internal/crypto/random')
+ .add('NativeModule internal/crypto/util')
+ .add('NativeModule internal/crypto/webcrypto')
+ .add('NativeModule internal/streams/lazy_transform');
+}
+
if (process.features.inspector) {
expectedModules.add('Internal Binding inspector');
expectedModules.add('NativeModule internal/inspector_async_hook');
diff --git a/test/parallel/test-cli-eval.js b/test/parallel/test-cli-eval.js
index e95fee008d..dcc2b8fcde 100644
--- a/test/parallel/test-cli-eval.js
+++ b/test/parallel/test-cli-eval.js
@@ -288,3 +288,69 @@ child.exec(
common.mustSucceed((stdout) => {
assert.strictEqual(stdout, '.mjs file\n');
}));
+
+if (common.hasCrypto) {
+ // Assert that calls to crypto utils work without require.
+ child.exec(
+ `${nodejs} ` +
+ '-e "console.log(crypto.randomBytes(16).toString(\'hex\'))"',
+ common.mustSucceed((stdout) => {
+ assert.match(stdout, /[0-9a-f]{32}/i);
+ }));
+ child.exec(
+ `${nodejs} ` +
+ '-p "crypto.randomBytes(16).toString(\'hex\')"',
+ common.mustSucceed((stdout) => {
+ assert.match(stdout, /[0-9a-f]{32}/i);
+ }));
+}
+// Assert that overriding crypto works.
+child.exec(
+ `${nodejs} ` +
+ '-p "crypto=Symbol(\'test\')"',
+ common.mustSucceed((stdout) => {
+ assert.match(stdout, /Symbol\(test\)/i);
+ }));
+child.exec(
+ `${nodejs} ` +
+ '-e "crypto = {};console.log(\'randomBytes\', typeof crypto.randomBytes)"',
+ common.mustSucceed((stdout) => {
+ assert.match(stdout, /randomBytes\sundefined/);
+ }));
+// Assert that overriding crypto with a local variable works.
+child.exec(
+ `${nodejs} ` +
+ '-e "const crypto = {};console.log(\'randomBytes\', typeof crypto.randomBytes)"',
+ common.mustSucceed((stdout) => {
+ assert.match(stdout, /randomBytes\sundefined/);
+ }));
+child.exec(
+ `${nodejs} ` +
+ '-e "let crypto = {};console.log(\'randomBytes\', typeof crypto.randomBytes)"',
+ common.mustSucceed((stdout) => {
+ assert.match(stdout, /randomBytes\sundefined/);
+ }));
+child.exec(
+ `${nodejs} ` +
+ '-e "var crypto = {};console.log(\'randomBytes\', typeof crypto.randomBytes)"',
+ common.mustSucceed((stdout) => {
+ assert.match(stdout, /randomBytes\sundefined/);
+ }));
+child.exec(
+ `${nodejs} ` +
+ '-p "const crypto = {randomBytes:1};typeof crypto.randomBytes"',
+ common.mustSucceed((stdout) => {
+ assert.match(stdout, /^number/);
+ }));
+child.exec(
+ `${nodejs} ` +
+ '-p "let crypto = {randomBytes:1};typeof crypto.randomBytes"',
+ common.mustSucceed((stdout) => {
+ assert.match(stdout, /^number/);
+ }));
+child.exec(
+ `${nodejs} --no-experimental-global-webcrypto ` +
+ '-p "var crypto = {randomBytes:1};typeof crypto.randomBytes"',
+ common.mustSucceed((stdout) => {
+ assert.match(stdout, /^number/);
+ }));
diff --git a/test/parallel/test-global-webcrypto-classes.js b/test/parallel/test-global-webcrypto-classes.js
index 083592bd92..ac42ff1d82 100644
--- a/test/parallel/test-global-webcrypto-classes.js
+++ b/test/parallel/test-global-webcrypto-classes.js
@@ -1,4 +1,4 @@
-// Flags: --experimental-global-webcrypto --expose-internals
+// Flags: --expose-internals
'use strict';
const common = require('../common');
diff --git a/test/parallel/test-global-webcrypto-disbled.js b/test/parallel/test-global-webcrypto-disbled.js
new file mode 100644
index 0000000000..ebbb4afa9a
--- /dev/null
+++ b/test/parallel/test-global-webcrypto-disbled.js
@@ -0,0 +1,10 @@
+// Flags: --no-experimental-global-webcrypto
+'use strict';
+
+require('../common');
+const assert = require('assert');
+
+assert.strictEqual(typeof crypto, 'undefined');
+assert.strictEqual(typeof Crypto, 'undefined');
+assert.strictEqual(typeof CryptoKey, 'undefined');
+assert.strictEqual(typeof SubtleCrypto, 'undefined');
diff --git a/test/parallel/test-global.js b/test/parallel/test-global.js
index 5437751cbc..9ac9b4f728 100644
--- a/test/parallel/test-global.js
+++ b/test/parallel/test-global.js
@@ -57,6 +57,7 @@ builtinModules.forEach((moduleName) => {
'setTimeout',
'structuredClone',
'fetch',
+ 'crypto',
];
assert.deepStrictEqual(new Set(Object.keys(global)), new Set(expected));
}