Compare commits

...

1 Commits

Author SHA1 Message Date
Claude Bot
800ac6e6c2 fix(sql): auto-populate TLS serverName when using tls: true option
When using Bun.SQL with individual connection options and tls: true,
the SNI hostname was not sent during the TLS handshake. This caused
connections to fail on servers that require SNI (like Neon).

The issue was that the code to auto-populate tls.serverName from
the hostname ran before sslMode was promoted from disable to prefer.
This fix swaps the order of these two operations so sslMode is promoted
first, allowing serverName to be properly set.

Fixes #26369

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 19:02:11 +00:00
2 changed files with 110 additions and 4 deletions

View File

@@ -838,6 +838,10 @@ function parseOptions(
}
}
if (tls && sslMode === SSLMode.disable) {
sslMode = SSLMode.prefer;
}
if (sslMode !== SSLMode.disable && !tls?.serverName) {
if (hostname) {
tls = { ...tls, serverName: hostname };
@@ -846,10 +850,6 @@ function parseOptions(
}
}
if (tls && sslMode === SSLMode.disable) {
sslMode = SSLMode.prefer;
}
port = Number(port);
if (!Number.isSafeInteger(port) || port < 1 || port > 65535) {

View File

@@ -0,0 +1,106 @@
import { SQL } from "bun";
import { describe, expect, test } from "bun:test";
describe("Issue #26369 - SNI hostname not sent when using tls: true with individual connection options", () => {
test("tls: true with host option should auto-populate serverName", () => {
const sql = new SQL({
host: "example-host.neon.tech",
username: "user",
password: "pass",
database: "db",
tls: true,
adapter: "postgres",
});
// Verify serverName is automatically set from the hostname
expect(sql.options.tls).toEqual({ serverName: "example-host.neon.tech" });
sql.close();
});
test("tls: true with hostname option should auto-populate serverName", () => {
const sql = new SQL({
hostname: "example-host.neon.tech",
username: "user",
password: "pass",
database: "db",
tls: true,
adapter: "postgres",
});
// Verify serverName is automatically set from the hostname
expect(sql.options.tls).toEqual({ serverName: "example-host.neon.tech" });
sql.close();
});
test("tls with explicit serverName should preserve it", () => {
const sql = new SQL({
host: "example-host.neon.tech",
username: "user",
password: "pass",
database: "db",
tls: { serverName: "custom-server-name.example.com" },
adapter: "postgres",
});
// Verify explicit serverName is preserved
expect(sql.options.tls).toEqual({ serverName: "custom-server-name.example.com" });
sql.close();
});
test("URL with sslmode=require should auto-populate serverName", () => {
const sql = new SQL("postgresql://user:pass@example-host.neon.tech/db?sslmode=require");
// Verify serverName is automatically set from the URL hostname
expect(sql.options.tls).toEqual({ serverName: "example-host.neon.tech" });
sql.close();
});
test("URL without sslmode but with tls: true option should auto-populate serverName", () => {
const sql = new SQL("postgresql://user:pass@example-host.neon.tech/db", {
tls: true,
});
// Verify serverName is automatically set from the URL hostname
expect(sql.options.tls).toEqual({ serverName: "example-host.neon.tech" });
sql.close();
});
test("tls object with other properties should also get serverName", () => {
const sql = new SQL({
host: "example-host.neon.tech",
username: "user",
password: "pass",
database: "db",
tls: { rejectUnauthorized: false },
adapter: "postgres",
});
// Verify serverName is added alongside existing tls properties
expect(sql.options.tls).toEqual({
rejectUnauthorized: false,
serverName: "example-host.neon.tech",
});
sql.close();
});
test("no tls option should not set tls in options", () => {
const sql = new SQL({
host: "example-host.neon.tech",
username: "user",
password: "pass",
database: "db",
adapter: "postgres",
});
// Verify tls is not set when not explicitly enabled
expect(sql.options.tls).toBeUndefined();
sql.close();
});
});