With this change, the TLS pause mechanisms used in node:tls are now made
generic, and used in QUIC to pause the handshake entirely for the
session event, thereby stopping almost all events being emitted
(everything except qlog) so that all the previously existing deferral
machinery is unnecessary.
This is useful immediately to clean up & simplify things, but also
medium term because this makes the 0RTT flow significantly clearer,
which will help with the upcoming dynamic attach for QUIC (which
requires deferring ticket processing & 0RTT delivery until after
user code).
As part of this, this changes the internal flow of how ALPN & SNI are
resolved. Instead of calculating them synchronously within the OpenSSL
callbacks, we precalculate them at the pause, and then the OpenSSL
callbacks just return the precalculated values.
This is intended to only create visible changes to node:quic. Though it
touches node:tls, it preserves all existing behaviour (including
callback order and other details).
Signed-off-by: Tim Perry <pimterry@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/65522
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
Allow --force-fips to take an optional provider or strict mode. Keep
provider as the current default and explicit compatibility mode.
In strict mode, return failure from the OpenSSL 3.4+ FIPS indicator
callback when it reports a non-approved operation. Install the callback
for enforcement without implicitly enabling diagnostics;
--enable-fips-indicator-events remains the independent observation
opt-in.
Keep the bare form mapped to provider for compatibility, allowing the
default to change to strict in a future major release. Preserve the mode
when forwarding flags to child test processes, and keep the parser's
internal mode storage out of process.allowedNodeEnvironmentFlags.
Document the callback scope limitations and cover provider
compatibility, validation, synchronous crypto, WebCrypto, Workers, and
opt-in event publication.
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/65645
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
OpenSSL 3.4 added an indicator callback for operations that its FIPS
provider allows after an approved-mode check has been relaxed.
Publish these indicators on the crypto.fips.indicator diagnostics
channel when Node.js is started with --enable-fips-indicator-events.
Leave the OpenSSL callback unset otherwise so ordinary cryptographic
operations have no indicator callback overhead.
Keep the channel observation-only so enforcement remains the
responsibility of the provider configuration, and independent diagnostic
subscribers can coexist.
Install the native callback during process initialization so it is not
changed while Workers or thread-pool jobs can be using OpenSSL. Preserve
callbacks installed by embedders, and queue bounded, coalesced messages
for asynchronous delivery on the main environment.
Document the channel as best-effort telemetry that cannot identify the
originating call or establish FIPS validation.
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/65645
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Add a node:vfs provider backed by a node:zlib ZIP archive - a ZipBuffer
held in memory or a ZipFile on disk - that exposes the archive's members
as a virtual filesystem tree. Directories are recognized both explicitly
(a "name/" entry) and implicitly (any entry under "name/"), and a file
opened for writing commits its content as a new archive entry when its
handle is closed. The provider is read-only unless the backing archive
is writable, and offers both asynchronous and synchronous operations.
Available as vfs.ZipProvider.
Signed-off-by: Philipp Dunkel <pip@pipobscure.com>
PR-URL: https://github.com/nodejs/node/pull/64915
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Use a monotonic high-resolution clock for elapsed-time checks and allow
a small tolerance for timer granularity. The previous whole-second clock
could report an interval one second short when a slightly early timer
coincided with a wall-clock boundary.
Assisted-by: Codex
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/65672
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
X509Certificate::GetPeerCert() built the certificate objects by deleting
entries from the stack returned by SSL_get_peer_cert_chain(), which is
owned by the SSL session. The first read emptied it, so any later read
by getPeerCertificate() or getPeerX509Certificate(), on either peer, saw
a truncated chain or nothing. Copy each issuer with X509_dup instead and
leave the session's stack untouched.
onServerSocketSecure() only called getPeerX509Certificate() to check
whether a peer certificate was present, building the whole chain on
every server handshake; that is what first exposed the destructive read.
Use a lightweight hasPeerCertificate() binding for the presence check.
Signed-off-by: Tony Gies <tgies@tgies.net>
PR-URL: https://github.com/nodejs/node/pull/65602
Fixes: https://github.com/nodejs/node/issues/65579
Refs: https://github.com/nodejs/node/pull/64677
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
Reviewed-By: Tim Perry <pimterry@gmail.com>
The test sends Debugger.setAsyncCallStackDepth in the same batch as
Runtime.enable and Debugger.enable, and treats the command response as
proof that async stack recording is on. It is not.
While the target runs JS the command is dispatched from a V8 interrupt,
where Agent::SyncAsyncHookState() cannot call into JS and defers
enabling the async hook to an immediate, answering the command anyway.
The `debugger` in the polling interval then pauses the target before
that immediate runs, and the nested message loop does not process
immediates, so setupTimeoutWithBreak() schedules the timer with the
hook still disabled and Debugger.paused arrives without asyncStackTrace.
Send the command after the first pause instead. Messages dispatched
while the target is paused come from the nested message loop rather
than an interrupt, so the hook is enabled inline and the response is
the barrier the test assumed it to be.
Deferring is deliberate: calling into JS from a V8 interrupt is not
safe. This corrects an assumption of the test, not the runtime.
Signed-off-by: Christian Aurich <christian.aurichzm@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/65584
Refs: https://github.com/nodejs/reliability/blob/main/reports/2026-08-26.md
Refs: https://github.com/nodejs/node/pull/65028
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
The previous deflake of this test, #63390, gave the two restart
waits common.platformTimeout(10_000) and left the first restart()
on the helper's 1000 ms default. That first call is the one still
timing out on CI.
It is also the call that needs the budget most. --watch spawns a
child process to run the script, so reaching the first Completed
running costs two process startups, while the later restarts pay for
one. Wrapping the default in platformTimeout() would not have been
enough on its own: none of its multipliers reaches a factor of ten,
so the budget would still be 1000 ms on these jobs, or 2000 ms in a
debug build.
Give that call the budget the other two already ask for, stated once in
the default rather than three times. The timer bounds how long the test
waits for watch mode to make progress, it is not an assertion about
restart latency, and the assertions on stdout and stderr are untouched.
The same helper is duplicated in test-watch-mode.mjs, with the same
default and nine call sites that take it. That file has carried
PASS, FLAKY since #44898, which is why only this one surfaces in the
reliability reports.
Signed-off-by: Christian Aurich <christian.aurichzm@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/65623
Refs: https://github.com/nodejs/node/pull/63390
Refs: https://github.com/nodejs/reliability/blob/main/reports/2026-08-18.md
Refs: https://github.com/nodejs/reliability/blob/main/reports/2026-08-20.md
Refs: https://github.com/nodejs/reliability/blob/main/reports/2026-08-21.md
Refs: https://github.com/nodejs/node/issues/44898
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
`NewIsolate()` deferred `SetIsolateErrorHandlers()` when snapshot data
was passed, and `CreateEnvironment()` later installed the handlers with
default `IsolateSettings` after deserializing the main context. An
embedder's `fatal_error_callback`, `oom_error_callback`,
`should_abort_on_uncaught_exception_callback` and
`prepare_stack_trace_callback` were therefore dropped whenever a
snapshot was used, and the per-isolate message listener was added even
if `MESSAGE_LISTENER_WITH_ERROR_LEVEL` had been cleared. The only way
to keep custom handlers was to call `SetIsolateUpForNode()` again after
`CreateEnvironment()`.
Install all handlers in `NewIsolate()` regardless of snapshot data, as
its documentation already describes, and stop touching isolate
handlers in `CreateEnvironment()`. The deferral dates from the initial
isolate snapshot work; every handler already copes with a missing
`Environment`, since without a snapshot they are installed before any
context exists, and workers have been calling `SetIsolateUpForNode()`
right after a snapshot `NewIsolate()` anyway.
Refs: https://github.com/nodejs/node/pull/27321
Refs: https://github.com/nodejs/node/pull/45888
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/65407
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
normalizeReference() adds URL and Uint8Array support to has()/drop();
the existing string/Buffer behavior is unchanged. BufferValue replaces
Utf8Value so a Buffer/TypedArray reference is copied as raw bytes
instead of a lossy UTF-8 conversion.
Signed-off-by: seungmin Nam <nhjbest22@g.skku.edu>
PR-URL: https://github.com/nodejs/node/pull/65492
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
The JavaScript recursive watcher used on platforms without a native one
(notably Linux) armed an fs.watch() handle and a stat() for every file
in the tree, and answered every event by stat()ing and re-reading the
whole directory it happened in. A 13k-entry tree cost 13.5k inotify
watches, ~150 ms and ~50 MB to set up, and appending to one file in a
3900-entry directory cost ~9 ms of CPU per event. A file replaced by
rename() (the usual editor save) also stopped being reported, since its
watch stayed on the old inode.
inotify reports changes to the entries of a watched directory, with
their names, so on Linux watch each directory once (symbolic links keep
their own watcher, as before), keep the set of known paths, and resolve
an event with a single stat() of the named entry: unknown names are
added and reported as 'rename', vanished ones are dropped and reported
as 'rename', file changes are reported as 'change'. kqueue and event
ports only report that a directory changed, so on the other platforms
served by this fallback every file keeps its own watcher and a
directory event rescans that directory, as before. unref() and ref()
now reach the underlying handles.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/65486
Reviewed-By: Moshe Atlow <moshe@atlow.co.il>
unofficial.gni special-cased ia32 on current_cpu while every other
architecture used target_cpu. When node is also compiled in a host
toolchain of a cross build (for example node_mksnapshot running in
V8's snapshot toolchain, which is x86-hosted for 32-bit arm targets),
current_cpu is the host's, so NODE_ARCH became "ia32" for an
arm-targeted build and process.arch deserialized as 'ia32' on the
device. Key the special case off target_cpu like the rest.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/65491
Refs: https://github.com/electron/electron/pull/52959
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
This fixes an issue where fs.cp and fs.cpSync (when a filter is
provided) correctly restore mtime and atime on copied files when
preserveTimestamps: true is passed, but skip restoring them for
directories.
The fix applies the existing setDestTimestamps helper to directories,
ensuring it is called after directory creation/copying but before any
modes are restored (since restoring a read-only mode would prevent
timestamp modification).
Note: This fix covers fs.cp() (async, all cases) and fs.cpSync()
when a filter is provided. The native fast path used by cpSync()
without a filter (CpSyncCopyDir in src/node_file.cc) has the
identical gap and is left for a separate follow-up contribution, since
it requires native code changes and a different review path.
Signed-off-by: Abhinandan Kumar <abhi128618@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/65540
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: LiviaMedeiros <livia@cirno.name>
Update the streams Web Platform Tests to upstream commit
4832db47614f5f48cc57374cbf5c1f70937fad48.
Adjust the streams WPT status entries for owning-type tests
that were renamed to tentative files upstream.
Signed-off-by: Jeong SeokChan <starp321@naver.com>
PR-URL: https://github.com/nodejs/node/pull/65638
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Recursive watch inverted the throwIfNoEntry check during setup, causing
the default behavior to suppress ENOENT while the opt-out threw it.
Other setup errors were also silently ignored.
Suppress only ENOENT when throwIfNoEntry is false. Propagate all
other setup failures and close watchers created before a partial
failure.
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
PR-URL: https://github.com/nodejs/node/pull/65635
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Add a thread_local re-entrancy guard to TriggerUncaughtException()
that detects when the JS-level exception handler
(process._fatalException) triggers another exception through the
inspector protocol.
This prevents the infinite loop:
TriggerUncaughtException -> InspectorConsoleCall ->
TriggerUncaughtException -> InspectorConsoleCall -> ...
When re-entrancy is detected, the function prints a diagnostic with the
formatted exception to stderr using FormatCaughtException (which avoids
the inspector path entirely), then aborts the process.
Fixes: https://github.com/nodejs/node/issues/64326
Signed-off-by: Temuulen Undrakhbayar <zerone.offical@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/64327
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
If a 'ready' listener starts a write after reopen(), the reopen path
still emits 'drain' from a nextTick before that write completes, so a
listener reading the file on 'drain' can observe it empty. This is the
race behind the test-fastutf8stream-reopen flake deflaked on the test
side in 2e8a4b1a8c.
Skip the extra emit when a write is in flight: #release() emits the
real 'drain' once the write completes, so no event is lost.
The regression test defers the reopened file's fs.write by one
setImmediate, deterministically landing the write after the nextTick
on which the premature 'drain' used to fire.
Refs: https://github.com/nodejs/node/commit/2e8a4b1a8ce
Signed-off-by: Matteo Collina <hello@matteocollina.com>
PR-URL: https://github.com/nodejs/node/pull/65633
Reviewed-By: Paolo Insogna <paolo@cowtech.it>
Reviewed-By: Robert Nagy <ronagy@icloud.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
The `#sec-using-declarations` fragment does not exist in the explicit
resource management proposal, so the link silently resolves to the top
of the page. The proposal defines `using` under
`#sec-let-and-const-declarations`, and the only `using`-related section
ids are static semantics entries.
Point the reference at the MDN page for `using` instead, matching the
`[`using`]` definition already used in fs.md.
Signed-off-by: Soul Lee <alus20x@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/65632
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
CompileSerializeMain() created new "require", "__filename", and
"__dirname" strings via FIXED_ONE_BYTE_STRING() on every call, even
though these strings are already cached on IsolateData/Environment
as require_string(), __filename_string(), and __dirname_string()
(defined via PER_ISOLATE_STRING_PROPERTIES in src/env_properties.h)
and are reused this way elsewhere (e.g. the sibling
RunEmbedderPreload() already obtains Environment* the same way).
Signed-off-by: agape1225 <49804691+agape1225@users.noreply.github.com>
PR-URL: https://github.com/nodejs/node/pull/65453
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
SQLite runs "PRAGMA table_xinfo" from inside its pre-update hook, while
it is still walking the connection's session list. Deleting a session
from a callback that the PRAGMA triggers frees memory that walk is still
using. Both an authorizer callback and a 'sqlite.db.query' subscriber
reach that window, and either one segfaults.
Reject session.close() and Symbol.dispose when the connection is inside
any callback. An already-closed session stays a no-op so that disposal
remains idempotent.
Fixes: https://github.com/nodejs/node/issues/65428
Signed-off-by: Trevor Burnham <trevorburnham@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/65454
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
`uvimpl::Work::AfterThreadPoolWork()` and the thread-safe function's
`DispatchOne()` and `Finalize()` opened an
`AsyncResource::CallbackScope` with only a `HandleScope`.
`InternalCallbackScope` expects the resource's environment context to
be entered and otherwise asserts that `Environment::GetCurrent(isolate)`
is that environment, so with two environments on one isolate an addon's
async work completion or thread-safe function call aborted the process
whenever the other environment's context was current when the loop ran
the callback.
Enter the node-api env's context first, as `CallFinalizer()` and the
zlib and WebCrypto thread pool callbacks already do. In `Finalize()` the
scope covers only the finalizer call, since `MaybeDelete()` can free
the env whose persistent handle `context()` returns.
Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/65406
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>