From 22fc52bda16b8cb08d4e7fdf1bb4a693bb5de2db Mon Sep 17 00:00:00 2001 From: Kit Dallege Date: Mon, 23 Mar 2026 10:49:36 +0100 Subject: [PATCH 1/3] doc: remove spawn with shell example from bat/cmd section Remove the suggestion to use child_process.spawn() with the shell option set for running .bat and .cmd files on Windows. Passing arguments through spawn with shell: true is deprecated (DEP0190) due to shell injection risks. Keep the exec() and direct cmd.exe spawn alternatives. Fixes: https://github.com/nodejs/node/issues/58735 PR-URL: https://github.com/nodejs/node/pull/62243 Reviewed-By: Antoine du Hamel Reviewed-By: Stefan Stojanovic --- doc/api/child_process.md | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/doc/api/child_process.md b/doc/api/child_process.md index 6818eec4594b25..2aeb253337f2df 100644 --- a/doc/api/child_process.md +++ b/doc/api/child_process.md @@ -120,23 +120,19 @@ however, `.bat` and `.cmd` files are not executable on their own without a terminal, and therefore cannot be launched using [`child_process.execFile()`][]. When running on Windows, `.bat` and `.cmd` files can be invoked by: -* using [`child_process.spawn()`][] with the `shell` option set, or +* using [`child_process.spawn()`][] with the `shell` option set (not recommended, see [DEP0190][]), or * using [`child_process.exec()`][], or * spawning `cmd.exe` and passing the `.bat` or `.cmd` file as an argument - (which is what the `shell` option and [`child_process.exec()`][] do). + (which is what [`child_process.exec()`][] does internally). In any case, if the script filename contains spaces, it needs to be quoted. ```cjs const { exec, spawn } = require('node:child_process'); -// 1. child_process.spawn() with the shell option set -const myBat = spawn('my.bat', { shell: true }); - -// 2. child_process.exec() exec('my.bat', (err, stdout, stderr) => { /* ... */ }); -// 3. spawning cmd.exe and passing the .bat or .cmd file as an argument +// Or, spawning cmd.exe directly: const bat = spawn('cmd.exe', ['/c', 'my.bat']); // If the script filename contains spaces, it needs to be quoted @@ -146,13 +142,9 @@ exec('"my script.cmd" a b', (err, stdout, stderr) => { /* ... */ }); ```mjs import { exec, spawn } from 'node:child_process'; -// 1. child_process.spawn() with the shell option set -const myBat = spawn('my.bat', { shell: true }); - -// 2. child_process.exec() exec('my.bat', (err, stdout, stderr) => { /* ... */ }); -// 3. spawning cmd.exe and passing the .bat or .cmd file as an argument +// Or, spawning cmd.exe directly: const bat = spawn('cmd.exe', ['/c', 'my.bat']); // If the script filename contains spaces, it needs to be quoted @@ -2364,6 +2356,7 @@ Therefore, this feature requires opting in by setting the or [`child_process.fork()`][]. [Advanced serialization]: #advanced-serialization +[DEP0190]: deprecations.md#DEP0190 [Default Windows shell]: #default-windows-shell [HTML structured clone algorithm]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm [Shell requirements]: #shell-requirements From 2263b4d6f1dbb8c9355184899e4970359aa8ab13 Mon Sep 17 00:00:00 2001 From: Tim Perry <1526883+pimterry@users.noreply.github.com> Date: Mon, 23 Mar 2026 12:27:22 +0100 Subject: [PATCH 2/3] crypto: add crypto::GetSSLCtx API for addon access to OpenSSL contexts This intended to replace usage of the unsupported _external field, offering an official API for native addons to access OpenSSL directly while reducing the JS API and internal field exposure. PR-URL: https://github.com/nodejs/node/pull/62254 Reviewed-By: Anna Henningsen --- src/crypto/crypto_context.cc | 25 ++++++++++ src/node.h | 15 ++++++ test/addons/addons.status | 1 + test/addons/openssl-get-ssl-ctx/binding.cc | 52 +++++++++++++++++++++ test/addons/openssl-get-ssl-ctx/binding.gyp | 29 ++++++++++++ test/addons/openssl-get-ssl-ctx/test.js | 49 +++++++++++++++++++ 6 files changed, 171 insertions(+) create mode 100644 test/addons/openssl-get-ssl-ctx/binding.cc create mode 100644 test/addons/openssl-get-ssl-ctx/binding.gyp create mode 100644 test/addons/openssl-get-ssl-ctx/test.js diff --git a/src/crypto/crypto_context.cc b/src/crypto/crypto_context.cc index 4c5e0c582ff6a0..9e453c5e173ade 100644 --- a/src/crypto/crypto_context.cc +++ b/src/crypto/crypto_context.cc @@ -2443,5 +2443,30 @@ void UseExtraCaCerts(std::string_view file) { extra_root_certs_file = file; } +NODE_EXTERN SSL_CTX* GetSSLCtx(Local context, Local value) { + Environment* env = Environment::GetCurrent(context); + if (env == nullptr) return nullptr; + + // TryCatchto swallow any exceptions from Get() (e.g. failing getters) + v8::TryCatch try_catch(env->isolate()); + + // Unwrap the .context property from the JS SecureContext wrapper + // (as returned by tls.createSecureContext()). + if (value->IsObject()) { + Local inner; + if (!value.As() + ->Get(context, FIXED_ONE_BYTE_STRING(env->isolate(), "context")) + .ToLocal(&inner)) { + return nullptr; + } + value = inner; + } + + if (!SecureContext::HasInstance(env, value)) return nullptr; + SecureContext* sc = BaseObject::FromJSObject(value); + if (sc == nullptr) return nullptr; + return sc->ctx().get(); +} + } // namespace crypto } // namespace node diff --git a/src/node.h b/src/node.h index 5df3efb78a2586..eba7b8698187b1 100644 --- a/src/node.h +++ b/src/node.h @@ -125,6 +125,7 @@ // Forward-declare libuv loop struct uv_loop_s; struct napi_module; +struct ssl_ctx_st; // Forward declaration of SSL_CTX for OpenSSL. // Forward-declare these functions now to stop MSVS from becoming // terminally confused when it's done in node_internals.h @@ -1657,6 +1658,20 @@ NODE_DEPRECATED( v8::Local object, v8::Object::Wrappable* wrappable)); +namespace crypto { + +// Returns the SSL_CTX* from a SecureContext JS object, as returned by +// tls.createSecureContext(). +// Returns nullptr if the value is not a SecureContext instance, +// or if Node.js was built without OpenSSL. +// +// The returned pointer is not owned by the caller and must not be freed. +// It is valid only while the SecureContext JS object remains alive. +NODE_EXTERN struct ssl_ctx_st* GetSSLCtx(v8::Local context, + v8::Local secure_context); + +} // namespace crypto + } // namespace node #endif // SRC_NODE_H_ diff --git a/test/addons/addons.status b/test/addons/addons.status index 7f5fd8f0c2f51f..18b1c2b2157d81 100644 --- a/test/addons/addons.status +++ b/test/addons/addons.status @@ -12,6 +12,7 @@ openssl-binding/test: PASS,FLAKY [$system==ibmi] openssl-binding/test: SKIP +openssl-get-ssl-ctx/test: SKIP openssl-providers/test-default-only-config: SKIP openssl-providers/test-legacy-provider-config: SKIP openssl-providers/test-legacy-provider-inactive-config: SKIP diff --git a/test/addons/openssl-get-ssl-ctx/binding.cc b/test/addons/openssl-get-ssl-ctx/binding.cc new file mode 100644 index 00000000000000..3945ec870fb8b9 --- /dev/null +++ b/test/addons/openssl-get-ssl-ctx/binding.cc @@ -0,0 +1,52 @@ +#include +#include + +namespace { + +// Test: extract SSL_CTX* from a SecureContext object via +// node::crypto::GetSSLCtx. +void GetSSLCtx(const v8::FunctionCallbackInfo& args) { + v8::Isolate* isolate = args.GetIsolate(); + v8::Local context = isolate->GetCurrentContext(); + + SSL_CTX* ctx = node::crypto::GetSSLCtx(context, args[0]); + if (ctx == nullptr) { + isolate->ThrowException(v8::Exception::Error( + v8::String::NewFromUtf8( + isolate, "GetSSLCtx returned nullptr for a valid SecureContext") + .ToLocalChecked())); + return; + } + + // Verify the pointer is a valid SSL_CTX by calling an OpenSSL function. + const SSL_METHOD* method = SSL_CTX_get_ssl_method(ctx); + if (method == nullptr) { + isolate->ThrowException(v8::Exception::Error( + v8::String::NewFromUtf8(isolate, + "SSL_CTX_get_ssl_method returned nullptr") + .ToLocalChecked())); + return; + } + + args.GetReturnValue().Set(true); +} + +// Test: passing a non-SecureContext value returns nullptr. +void GetSSLCtxInvalid(const v8::FunctionCallbackInfo& args) { + v8::Isolate* isolate = args.GetIsolate(); + v8::Local context = isolate->GetCurrentContext(); + + SSL_CTX* ctx = node::crypto::GetSSLCtx(context, args[0]); + args.GetReturnValue().Set(ctx == nullptr); +} + +void Initialize(v8::Local exports, + v8::Local module, + v8::Local context) { + NODE_SET_METHOD(exports, "getSSLCtx", GetSSLCtx); + NODE_SET_METHOD(exports, "getSSLCtxInvalid", GetSSLCtxInvalid); +} + +} // anonymous namespace + +NODE_MODULE_CONTEXT_AWARE(NODE_GYP_MODULE_NAME, Initialize) diff --git a/test/addons/openssl-get-ssl-ctx/binding.gyp b/test/addons/openssl-get-ssl-ctx/binding.gyp new file mode 100644 index 00000000000000..46a558e1203036 --- /dev/null +++ b/test/addons/openssl-get-ssl-ctx/binding.gyp @@ -0,0 +1,29 @@ +{ + 'targets': [ + { + 'target_name': 'binding', + 'includes': ['../common.gypi'], + 'conditions': [ + ['node_use_openssl=="true"', { + 'conditions': [ + ['OS in "aix os400"', { + 'variables': { + # Used to differentiate `AIX` and `OS400`(IBM i). + 'aix_variant_name': ' Date: Mon, 23 Mar 2026 13:39:10 -0600 Subject: [PATCH 3/3] doc: fix guaranteed typo PR-URL: https://github.com/nodejs/node/pull/62374 Reviewed-By: Luigi Pinca Reviewed-By: Aviv Keller Reviewed-By: James M Snell --- doc/api/cli.md | 2 +- doc/node.1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/api/cli.md b/doc/api/cli.md index b1a0d674ca4ded..18205fa6ca790c 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -4074,7 +4074,7 @@ that run in libuv's threadpool will experience degraded performance. In order to mitigate this issue, one potential solution is to increase the size of libuv's threadpool by setting the `'UV_THREADPOOL_SIZE'` environment variable to a value greater than `4` (its current default value). However, setting this from inside -the process using `process.env.UV_THREADPOOL_SIZE=size` is not guranteed to work +the process using `process.env.UV_THREADPOOL_SIZE=size` is not guaranteed to work as the threadpool would have been created as part of the runtime initialisation much before user code is run. For more information, see the [libuv threadpool documentation][]. diff --git a/doc/node.1 b/doc/node.1 index e88c005731b40f..032ca2a12d0bf3 100644 --- a/doc/node.1 +++ b/doc/node.1 @@ -2287,7 +2287,7 @@ that run in libuv's threadpool will experience degraded performance. In order to mitigate this issue, one potential solution is to increase the size of libuv's threadpool by setting the \fB'UV_THREADPOOL_SIZE'\fR environment variable to a value greater than \fB4\fR (its current default value). However, setting this from inside -the process using \fBprocess.env.UV_THREADPOOL_SIZE=size\fR is not guranteed to work +the process using \fBprocess.env.UV_THREADPOOL_SIZE=size\fR is not guaranteed to work as the threadpool would have been created as part of the runtime initialisation much before user code is run. For more information, see the libuv threadpool documentation. .