Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

- Honor `BUN_FEATURE_FLAG_DISABLE_SOURCE_MAPS` on transpiler-cache hits and prevent startup crashes from cached modules without maps. Adapts empty-map validation from [oven-sh/bun#42412](https://github.com/oven-sh/bun/pull/42412); thanks @robobun.

- Preserve TLS SNI set with `tlsSocket.setServername()` before connecting, including address-family selection, while continuing to omit implicit SNI for absent, null, or empty names.

- Sync oven-sh/bun through `f2405c382d43b89d91a2e7c90d9c5f53505a1e71`, including native lint/format commands, while reconciling fork TLS, worker, module-resolution, CLI and parser fixes. Pin the qualified WebKit release `f8521bb45429b3e1df6bc6d9b98b8df4376a89c5`.

- Avoid quadratic Worker file-descriptor tracking when many descriptors are open, while preserving automatic cleanup at Worker exit.
Expand Down
2 changes: 1 addition & 1 deletion docs/runtime/nodejs-compat.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ On POSIX, piped and terminal `process.stdout` and `process.stderr` expose `_hand

### [`node:tls`](https://nodejs.org/api/tls.html)

`tls.connect()` sends SNI only for an explicit nonempty `servername`; omitted, `undefined`, `null`, and empty values send no SNI. Certificate identity checks still use the explicit servername or connection host. Established Node TLS sockets report `servername: false` when no SNI was sent. HTTPS agents and HTTP/2 choose their own host-derived names, excluding IP hosts. Explicit IP servernames remain rejected, unlike Node.js 24, which sends them with deprecation warning `DEP0123`.
`tls.connect()` sends SNI only for an explicit nonempty `servername`; omitted, `undefined`, `null`, and empty values send no SNI. A name set with `tlsSocket.setServername()` before connecting is preserved, including with `autoSelectFamily`. Certificate identity checks still use the explicit servername or connection host. Established Node TLS sockets report `servername: false` when no SNI was sent. HTTPS agents and HTTP/2 choose their own host-derived names, excluding IP hosts. Explicit IP servernames remain rejected, unlike Node.js 24, which sends them with deprecation warning `DEP0123`.

Native `fetch()` uses `tls.setDefaultCACertificates()` overrides for new TLS connections. Explicit request or `FetchSession` CAs take precedence; an empty CA list trusts no certificates. Overrides are local to the calling thread, like Node.

Expand Down
2 changes: 1 addition & 1 deletion src/js/node/tls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1026,7 +1026,7 @@ TLSSocket.prototype.getX509Certificate = function getX509Certificate() {
TLSSocket.prototype[buntls] = function () {
const ctx = this[ksecureContext];
// An empty native serverName suppresses its connection-host fallback; tls.connect does not infer SNI.
const servername = this.servername || "";
const servername = this[kservername] ?? (this.servername || "");
return {
ALPNProtocols: this.ALPNProtocols,
checkServerIdentity: this[kcheckServerIdentity],
Expand Down
10 changes: 10 additions & 0 deletions test/js/node/tls/node-tls-connect-hostname-verification.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import { describe, test } from "node:test";
import tls from "node:tls";
import {
expected as expectedServername,
observePending,
observe as observeServername,
pendingCases,
cases as servernameCases,
} from "./node-tls-servername-fixture.mjs";

Expand Down Expand Up @@ -618,4 +620,12 @@ describe("TLS servername selection", () => {
assert.deepStrictEqual(await observeServername(scenario), expectedServername(scenario));
});
}
for (const scenario of pendingCases) {
test(`pre-connect setServername=${JSON.stringify(scenario.servername)} autoSelectFamily=${scenario.autoSelectFamily}`, async () => {
assert.deepStrictEqual(await observePending(scenario), {
servername: scenario.servername || false,
clientServername: scenario.servername || false,
});
});
}
});
6 changes: 2 additions & 4 deletions test/js/node/tls/node-tls-connect.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6152,13 +6152,11 @@ describe("a setServername() made before TLSSocket#connect() is the SNI of the ha
}
}

// Known inherited gap: pre-connect setServername is lost; see the separate TLS SNI follow-up.
it.failing("on a host and port connect", async () => {
it("on a host and port connect", async () => {
expect(await names(port => ({ port, host: "127.0.0.1" }))).toEqual({ client: NAME, server: NAME });
});

// Known inherited gap: pre-connect setServername is lost; see the separate TLS SNI follow-up.
it.failing("on an autoSelectFamily connect, instead of a name derived from the host", async () => {
it("on an autoSelectFamily connect, instead of a name derived from the host", async () => {
const lookup = ((_host, _options, callback) =>
callback(null, [
{ address: "127.0.0.1", family: 4 },
Expand Down
49 changes: 49 additions & 0 deletions test/js/node/tls/node-tls-servername-fixture.mjs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading