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 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. . 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': '