mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
fix(websocket): accept rejectUnauthorized at top level of options
For Node.js ws library compatibility, accept rejectUnauthorized both at the top level and nested inside the tls object. The tls object takes precedence if both are specified. Fixes #22870 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -267,6 +267,16 @@ static inline JSC::EncodedJSValue constructJSWebSocket3(JSGlobalObject* lexicalG
|
||||
RETURN_IF_EXCEPTION(throwScope, {});
|
||||
}
|
||||
|
||||
// Check for rejectUnauthorized at top level for Node.js ws library compatibility
|
||||
// Only check if it wasn't already set from the tls object
|
||||
if (rejectUnauthorized == -1) {
|
||||
auto rejectUnauthorizedValue = Bun::getOwnPropertyIfExists(globalObject, options, PropertyName(Identifier::fromString(vm, "rejectUnauthorized"_s)));
|
||||
RETURN_IF_EXCEPTION(throwScope, {});
|
||||
if (rejectUnauthorizedValue && !rejectUnauthorizedValue.isUndefinedOrNull() && rejectUnauthorizedValue.isBoolean()) {
|
||||
rejectUnauthorized = rejectUnauthorizedValue.asBoolean() ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse proxy option - can be string or { url, headers }
|
||||
auto proxyValue = Bun::getOwnPropertyIfExists(globalObject, options, PropertyName(Identifier::fromString(vm, "proxy"_s)));
|
||||
RETURN_IF_EXCEPTION(throwScope, {});
|
||||
|
||||
163
test/regression/issue/22870.test.ts
Normal file
163
test/regression/issue/22870.test.ts
Normal file
@@ -0,0 +1,163 @@
|
||||
import { describe, expect, it } from "bun:test";
|
||||
import { tls } from "harness";
|
||||
|
||||
// Test for https://github.com/oven-sh/bun/issues/22870
|
||||
// rejectUnauthorized should work at the top level of WebSocket options
|
||||
// for compatibility with Node.js ws library
|
||||
describe("WebSocket rejectUnauthorized option", () => {
|
||||
it("should accept rejectUnauthorized at top level", async () => {
|
||||
// Create a server with self-signed certificate
|
||||
using server = Bun.serve({
|
||||
port: 0,
|
||||
tls,
|
||||
fetch(req, server) {
|
||||
if (server.upgrade(req)) {
|
||||
return;
|
||||
}
|
||||
return new Response("Upgrade failed", { status: 500 });
|
||||
},
|
||||
websocket: {
|
||||
open(ws) {
|
||||
ws.send("hello");
|
||||
},
|
||||
message(ws, message) {
|
||||
ws.send(message);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// This should work with rejectUnauthorized at top level
|
||||
const ws = new WebSocket(server.url.href.replace("https:", "wss:"), {
|
||||
rejectUnauthorized: false,
|
||||
});
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
ws.onopen = () => resolve();
|
||||
ws.onerror = e => reject(new Error(`WebSocket error: ${e}`));
|
||||
});
|
||||
|
||||
const closed = new Promise(resolve => {
|
||||
ws.onclose = resolve;
|
||||
});
|
||||
ws.close();
|
||||
await closed;
|
||||
});
|
||||
|
||||
it("should still accept rejectUnauthorized nested in tls object", async () => {
|
||||
// Create a server with self-signed certificate
|
||||
using server = Bun.serve({
|
||||
port: 0,
|
||||
tls,
|
||||
fetch(req, server) {
|
||||
if (server.upgrade(req)) {
|
||||
return;
|
||||
}
|
||||
return new Response("Upgrade failed", { status: 500 });
|
||||
},
|
||||
websocket: {
|
||||
open(ws) {
|
||||
ws.send("hello");
|
||||
},
|
||||
message(ws, message) {
|
||||
ws.send(message);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// The nested tls object should still work
|
||||
const ws = new WebSocket(server.url.href.replace("https:", "wss:"), {
|
||||
tls: { rejectUnauthorized: false },
|
||||
});
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
ws.onopen = () => resolve();
|
||||
ws.onerror = e => reject(new Error(`WebSocket error: ${e}`));
|
||||
});
|
||||
|
||||
const closed = new Promise(resolve => {
|
||||
ws.onclose = resolve;
|
||||
});
|
||||
ws.close();
|
||||
await closed;
|
||||
});
|
||||
|
||||
it("should prefer tls.rejectUnauthorized over top-level rejectUnauthorized", async () => {
|
||||
// Create a server with self-signed certificate
|
||||
using server = Bun.serve({
|
||||
port: 0,
|
||||
tls,
|
||||
fetch(req, server) {
|
||||
if (server.upgrade(req)) {
|
||||
return;
|
||||
}
|
||||
return new Response("Upgrade failed", { status: 500 });
|
||||
},
|
||||
websocket: {
|
||||
open(ws) {
|
||||
ws.send("hello");
|
||||
},
|
||||
message(ws, message) {
|
||||
ws.send(message);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// When both are specified, tls.rejectUnauthorized should take precedence
|
||||
// Here tls.rejectUnauthorized: false should allow connection even though top-level says true
|
||||
const ws = new WebSocket(server.url.href.replace("https:", "wss:"), {
|
||||
rejectUnauthorized: true,
|
||||
tls: { rejectUnauthorized: false },
|
||||
});
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
ws.onopen = () => resolve();
|
||||
ws.onerror = e => reject(new Error(`WebSocket error: ${e}`));
|
||||
});
|
||||
|
||||
const closed = new Promise(resolve => {
|
||||
ws.onclose = resolve;
|
||||
});
|
||||
ws.close();
|
||||
await closed;
|
||||
});
|
||||
|
||||
it("should fail with rejectUnauthorized: true against self-signed cert", async () => {
|
||||
// Create a server with self-signed certificate
|
||||
using server = Bun.serve({
|
||||
port: 0,
|
||||
tls,
|
||||
fetch(req, server) {
|
||||
if (server.upgrade(req)) {
|
||||
return;
|
||||
}
|
||||
return new Response("Upgrade failed", { status: 500 });
|
||||
},
|
||||
websocket: {
|
||||
open(ws) {
|
||||
ws.send("hello");
|
||||
},
|
||||
message(ws, message) {
|
||||
ws.send(message);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// With rejectUnauthorized: true (or default), self-signed cert should be rejected
|
||||
const ws = new WebSocket(server.url.href.replace("https:", "wss:"), {
|
||||
rejectUnauthorized: true,
|
||||
});
|
||||
|
||||
const errored = await new Promise<boolean>(resolve => {
|
||||
ws.onopen = () => {
|
||||
ws.close();
|
||||
resolve(false);
|
||||
};
|
||||
ws.onerror = () => {
|
||||
ws.close();
|
||||
resolve(true);
|
||||
};
|
||||
});
|
||||
|
||||
expect(errored).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user