Skip to content

Commit b136a76

Browse files
committed
Merge remote-tracking branch 'local' into loader/experimental-raw-imports
2 parents 7258ff0 + 89bf654 commit b136a76

1,065 files changed

Lines changed: 1364602 additions & 1772650 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
name: Test Linux (with QUIC)
2+
3+
on:
4+
workflow_dispatch:
5+
pull_request:
6+
paths:
7+
- .github/workflows/test-linux-quic.yml
8+
- configure.py
9+
- node.gyp
10+
- node.gypi
11+
- deps/ngtcp2/**
12+
- deps/nghttp3/**
13+
- deps/openssl/**
14+
- src/quic/**
15+
- src/node_bob*
16+
- lib/quic.js
17+
- lib/http3.js
18+
- lib/internal/quic/**
19+
- lib/stream/iter.js
20+
- lib/internal/streams/iter/**
21+
- test/cctest/test_quic_*
22+
- test/common/quic*
23+
- test/common/quic/**
24+
- test/parallel/*quic*
25+
- test/parallel/test-stream-iter-*
26+
types: [opened, synchronize, reopened, ready_for_review]
27+
28+
concurrency:
29+
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
30+
cancel-in-progress: true
31+
32+
env:
33+
PYTHON_VERSION: '3.14'
34+
FLAKY_TESTS: keep_retrying
35+
CLANG_VERSION: '19'
36+
CC: ${{ (github.base_ref == 'main' || github.ref_name == 'main') && 'sccache' || '' }} clang-19
37+
CXX: ${{ (github.base_ref == 'main' || github.ref_name == 'main') && 'sccache' || '' }} clang++-19
38+
SCCACHE_GHA_ENABLED: ${{ github.base_ref == 'main' || github.ref_name == 'main' }}
39+
SCCACHE_IDLE_TIMEOUT: '0'
40+
RUSTC_VERSION: '1.82'
41+
42+
permissions:
43+
contents: read
44+
45+
jobs:
46+
test-quic:
47+
if: github.event.pull_request.draft == false
48+
runs-on: ubuntu-24.04-arm
49+
steps:
50+
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
51+
with:
52+
persist-credentials: false
53+
path: node
54+
- name: Install Clang ${{ env.CLANG_VERSION }}
55+
uses: ./node/.github/actions/install-clang
56+
with:
57+
clang-version: ${{ env.CLANG_VERSION }}
58+
- name: Install Rust ${{ env.RUSTC_VERSION }}
59+
run: |
60+
rustup override set "$RUSTC_VERSION"
61+
rustup --version
62+
- name: Set up Python ${{ env.PYTHON_VERSION }}
63+
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
64+
with:
65+
python-version: ${{ env.PYTHON_VERSION }}
66+
allow-prereleases: true
67+
- name: Set up sccache
68+
if: github.base_ref == 'main' || github.ref_name == 'main'
69+
uses: Mozilla-Actions/sccache-action@9e7fa8a12102821edf02ca5dbea1acd0f89a2696 # v0.0.10
70+
with:
71+
version: v0.12.0
72+
- name: Environment Information
73+
run: npx envinfo
74+
- name: Build
75+
working-directory: node
76+
run: make build-ci -j4 V=1 CONFIG_FLAGS="--error-on-warn --v8-enable-temporal-support --experimental-quic"
77+
- name: Test
78+
working-directory: node
79+
run: make test-ci -j1 V=1 TEST_CI_ARGS="-p actions --measure-flakiness 9"
80+
- name: Ensure running tests did not cause any change in the tree
81+
working-directory: node
82+
run: git add -A && git diff --name-only --exit-code --staged

.github/workflows/update-wpt.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,14 @@ jobs:
5252
- name: Update WPT for subsystem ${{ matrix.subsystem }}
5353
run: |
5454
git node wpt "$SUBSYSTEM"
55+
# TODO: Remove this workaround after @node-core/utils stops
56+
# regenerating test/fixtures/wpt/README.md for every subsystem
57+
# update.
58+
# git node wpt currently rewrites test/fixtures/wpt/README.md with a
59+
# generated summary for every subsystem update. versions.json is the
60+
# authoritative metadata, and keeping the README stable avoids
61+
# conflicts between concurrent WPT update PRs.
62+
git restore test/fixtures/wpt/README.md
5563
env:
5664
SUBSYSTEM: ${{ matrix.subsystem }}
5765

BUILDING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -558,7 +558,7 @@ NODE=/path/to/node make doc-only
558558
To read the man page:
559559

560560
```bash
561-
man doc/node.1
561+
man out/doc/node.1
562562
```
563563

564564
If you prefer to read the full documentation in a browser, run the following.

CHANGELOG.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,15 @@ release.
4141
</tr>
4242
<tr>
4343
<td valign="top">
44-
<b><a href="doc/changelogs/CHANGELOG_V26.md#26.3.0">26.3.0</a></b><br/>
44+
<b><a href="doc/changelogs/CHANGELOG_V26.md#26.3.1">26.3.1</a></b><br/>
45+
<a href="doc/changelogs/CHANGELOG_V26.md#26.3.0">26.3.0</a><br/>
4546
<a href="doc/changelogs/CHANGELOG_V26.md#26.2.0">26.2.0</a><br/>
4647
<a href="doc/changelogs/CHANGELOG_V26.md#26.1.0">26.1.0</a><br/>
4748
<a href="doc/changelogs/CHANGELOG_V26.md#26.0.0">26.0.0</a><br/>
4849
</td>
4950
<td valign="top">
50-
<b><a href="doc/changelogs/CHANGELOG_V24.md#24.16.0">24.16.0</a></b><br/>
51+
<b><a href="doc/changelogs/CHANGELOG_V24.md#24.17.0">24.17.0</a></b><br/>
52+
<a href="doc/changelogs/CHANGELOG_V24.md#24.16.0">24.16.0</a><br/>
5153
<a href="doc/changelogs/CHANGELOG_V24.md#24.15.0">24.15.0</a><br/>
5254
<a href="doc/changelogs/CHANGELOG_V24.md#24.14.1">24.14.1</a><br/>
5355
<a href="doc/changelogs/CHANGELOG_V24.md#24.14.0">24.14.0</a><br/>
@@ -72,7 +74,8 @@ release.
7274
<a href="doc/changelogs/CHANGELOG_V24.md#24.0.0">24.0.0</a><br/>
7375
</td>
7476
<td valign="top">
75-
<b><a href="doc/changelogs/CHANGELOG_V22.md#22.22.3">22.22.3</a></b><br/>
77+
<b><a href="doc/changelogs/CHANGELOG_V22.md#22.23.0">22.23.0</a></b><br/>
78+
<a href="doc/changelogs/CHANGELOG_V22.md#22.22.3">22.22.3</a><br/>
7679
<a href="doc/changelogs/CHANGELOG_V22.md#22.22.2">22.22.2</a><br/>
7780
<a href="doc/changelogs/CHANGELOG_V22.md#22.22.1">22.22.1</a><br/>
7881
<a href="doc/changelogs/CHANGELOG_V22.md#22.22.0">22.22.0</a><br/>

Makefile

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ config.gypi: configure configure.py src/node_version.h
196196
fi
197197

198198
.PHONY: install
199-
install: all ## Install node into $PREFIX (default=/usr/local).
199+
install: all out/doc/node.1 ## Install node into $PREFIX (default=/usr/local).
200200
$(PYTHON) tools/install.py $@ --dest-dir '$(DESTDIR)' --prefix '$(PREFIX)'
201201

202202
.PHONY: uninstall
@@ -856,7 +856,7 @@ VERSION=v$(RAWVER)
856856

857857
.PHONY: doc-only
858858
.NOTPARALLEL: doc-only
859-
doc-only: $(apidoc_dirs) $(apidocs_html) $(apidocs_json) out/doc/api/all.html out/doc/api/all.json out/doc/llms.txt out/doc/apilinks.json ## Builds the docs with the local or the global Node.js binary.
859+
doc-only: $(apidoc_dirs) $(apidocs_html) $(apidocs_json) out/doc/node.1 out/doc/api/all.html out/doc/api/all.json out/doc/llms.txt out/doc/apilinks.json ## Builds the docs with the local or the global Node.js binary.
860860

861861
.PHONY: doc
862862
doc: $(NODE_EXE) doc-only ## Build Node.js, and then build the documentation with the new binary.
@@ -932,6 +932,19 @@ out/doc/apilinks.json: $(wildcard lib/*.js) tools/doc/node_modules | out/doc
932932
) \
933933
fi
934934

935+
out/doc/node.1: doc/api/cli.md tools/doc/node_modules | out/doc
936+
@if [ "$(shell $(node_use_openssl_and_icu))" != "true" ]; then \
937+
echo "Skipping $@ (no crypto and/or no ICU)"; \
938+
else \
939+
$(call available-node, \
940+
$(DOC_KIT) generate \
941+
-t man-page \
942+
-i $< \
943+
-o $(@D) \
944+
-v $(VERSION) \
945+
) \
946+
fi
947+
935948
.PHONY: docopen
936949
docopen: doc-only ## Open the documentation in a web browser.
937950
@$(PYTHON) -mwebbrowser file://$(abspath $<)
@@ -1269,7 +1282,7 @@ $(TARBALL): $(TARBALL_DEPS)
12691282
git checkout-index -a -f --prefix=$(TARNAME)/
12701283
ifneq ($(SKIP_SHARED_DEPS), 1)
12711284
mkdir -p $(TARNAME)/doc/api
1272-
cp doc/node.1 $(TARNAME)/doc/node.1
1285+
cp out/doc/node.1 $(TARNAME)/doc/node.1
12731286
cp -r out/doc/api/* $(TARNAME)/doc/api/
12741287
endif
12751288
sed 's/fileset = fileset.intersection (fileset.gitTracked root)/fileset =/' tools/nix/v8.nix > $(TARNAME)/tools/nix/v8.nix

SECURITY.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,21 @@ the community they pose.
374374
responsibility to properly handle errors by attaching appropriate
375375
`'error'` event listeners to EventEmitters that may emit errors.
376376

377+
#### Exceptions Thrown by Application Callbacks (CWE-248)
378+
379+
* Node.js trusts the application code it is asked to run, including callbacks
380+
that are invoked by Node.js APIs. If an application callback throws an
381+
uncaught exception, any resulting crash is not considered a vulnerability in
382+
Node.js.
383+
* For example, [CVE-2026-21637](https://www.cve.org/CVERecord?id=CVE-2026-21637)
384+
was triaged as a Node.js vulnerability, but scenarios that require TLS
385+
callbacks such as `ALPNCallback`, `SNICallback`, or `pskCallback` to throw
386+
are outside the Node.js threat model. Future reports of similar issues,
387+
where the crash depends on application callbacks throwing uncaught
388+
exceptions, will not be treated as Node.js vulnerabilities. It is the
389+
application's responsibility to handle unexpected callback input and report
390+
errors without throwing uncaught exceptions.
391+
377392
#### Permission Model Boundaries (`--permission`)
378393

379394
The Node.js [Permission Model](https://nodejs.org/api/permissions.html)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
'use strict';
2+
const common = require('../common.js');
3+
const { execFile } = require('child_process');
4+
5+
// Isolates stdout accumulation + maxBuffer handling in execFile(). The child
6+
// writes `chunks` blocks of 64 KiB; the parent accumulates them through the
7+
// native pipe read path and the JS buffering in lib/child_process.js until the
8+
// process exits and the result buffer is handed to the callback.
9+
10+
const bench = common.createBenchmark(main, {
11+
// Number of 64 KiB blocks written by the child: 1 MiB, 16 MiB, 64 MiB.
12+
chunks: [16, 256, 1024],
13+
n: [10],
14+
});
15+
16+
function main({ n, chunks }) {
17+
const script =
18+
'const b = Buffer.alloc(65536, 0x61);' +
19+
`for (let i = 0; i < ${chunks}; i++) process.stdout.write(b);`;
20+
const args = ['-e', script];
21+
const options = {
22+
maxBuffer: chunks * 65536 + 65536,
23+
encoding: 'buffer',
24+
};
25+
26+
let left = n;
27+
const run = () => {
28+
execFile(process.execPath, args, options, (err) => {
29+
if (err)
30+
throw err;
31+
if (--left === 0)
32+
return bench.end(n);
33+
run();
34+
});
35+
};
36+
37+
bench.start();
38+
run();
39+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
'use strict';
2+
if (process.argv[2] === 'child') {
3+
// Echo every message straight back to the parent.
4+
process.on('message', (msg) => {
5+
process.send(msg);
6+
});
7+
} else {
8+
const common = require('../common.js');
9+
const bench = common.createBenchmark(main, {
10+
len: [64, 256, 1024, 4096, 16384, 65536],
11+
serialization: ['json', 'advanced'],
12+
dur: [5],
13+
});
14+
const { spawn } = require('child_process');
15+
16+
function main({ dur, len, serialization }) {
17+
const msg = { payload: '.'.repeat(len) };
18+
const options = {
19+
stdio: ['ignore', 'ignore', 'ignore', 'ipc'],
20+
serialization,
21+
};
22+
const child = spawn(process.argv[0],
23+
[process.argv[1], 'child'], options);
24+
25+
let messages = 0;
26+
let finished = false;
27+
28+
child.on('message', () => {
29+
messages++;
30+
// Keep one round-trip in flight per completed one so both the serialize
31+
// (write) and deserialize (read) paths stay saturated on both ends.
32+
if (!finished)
33+
child.send(msg);
34+
});
35+
36+
bench.start();
37+
// Prime a window of in-flight messages so the IPC channel never drains.
38+
for (let i = 0; i < 256; i++)
39+
child.send(msg);
40+
41+
setTimeout(() => {
42+
finished = true;
43+
bench.end(messages);
44+
child.kill();
45+
}, dur * 1000);
46+
}
47+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
'use strict';
2+
const common = require('../common.js');
3+
const { spawn } = require('child_process');
4+
5+
// Isolates the cost of marshaling spawn() options across the JS -> C++ boundary
6+
// (ProcessWrap::Spawn). A trivial, fast-exiting child is spawned repeatedly
7+
// while scaling the number of environment pairs and arguments that have to be
8+
// converted, so the per-spawn option-handling overhead is the dominant cost.
9+
10+
const isWindows = process.platform === 'win32';
11+
const command = isWindows ? 'cmd' : 'true';
12+
const baseArgs = isWindows ? ['/d', '/s', '/c', 'exit'] : [];
13+
14+
const bench = common.createBenchmark(main, {
15+
n: [1000],
16+
envc: [0, 64, 256, 1024],
17+
argc: [0, 8, 64],
18+
});
19+
20+
function main({ n, envc, argc }) {
21+
const env = { ...process.env };
22+
for (let i = 0; i < envc; i++)
23+
env[`NODE_BENCH_VAR_${i}`] = `value_${i}`;
24+
25+
const args = baseArgs.slice();
26+
for (let i = 0; i < argc; i++)
27+
args.push(`arg_${i}`);
28+
29+
const options = { env, stdio: ['ignore', 'ignore', 'ignore'] };
30+
31+
let left = n;
32+
const go = () => {
33+
if (--left < 0)
34+
return bench.end(n);
35+
const child = spawn(command, args, options);
36+
// The exit code is intentionally ignored: the child only exercises the
37+
// option-marshaling path, it is not expected to do any useful work.
38+
child.on('exit', go);
39+
};
40+
41+
bench.start();
42+
go();
43+
}

benchmark/crypto/create-hmac.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
'use strict';
2+
3+
const common = require('../common.js');
4+
const { createHmac } = require('crypto');
5+
const assert = require('assert');
6+
7+
const bench = common.createBenchmark(main, {
8+
n: [1e5],
9+
algo: ['sha1', 'sha256', 'sha512'],
10+
keylen: [0, 16, 64, 1024],
11+
});
12+
13+
function main({ n, algo, keylen }) {
14+
const key = Buffer.alloc(keylen, 'k');
15+
const hmacs = new Array(n);
16+
17+
bench.start();
18+
for (let i = 0; i < n; ++i) {
19+
hmacs[i] = createHmac(algo, key);
20+
}
21+
bench.end(n);
22+
23+
assert.strictEqual(typeof hmacs[n - 1], 'object');
24+
}

0 commit comments

Comments
 (0)