Compare commits

..

1 Commits

Author SHA1 Message Date
Cursor Agent
355aca820e Validate secureProtocol in TLS and add tests for protocol methods 2025-05-29 19:54:40 +00:00
5 changed files with 95 additions and 107 deletions

View File

@@ -24,7 +24,6 @@ const Duplex = require("internal/streams/duplex");
const { getDefaultHighWaterMark } = require("internal/streams/state");
const EventEmitter = require("node:events");
let dns: typeof import("node:dns");
const os = require("node:os");
const [addServerName, upgradeDuplexToTLS, isNamedPipeSocket, getBufferedAmount] = $zig(
"socket.zig",
@@ -88,21 +87,8 @@ function isIP(s): 0 | 4 | 6 {
return 0;
}
function isLocalAddress(addr: string): boolean {
if (!addr) return false;
if (addr === "0.0.0.0" || addr === "::") return true;
const interfaces = os.networkInterfaces();
for (const name in interfaces) {
const list = interfaces[name];
if (!list) continue;
for (const info of list) {
if (info && info.address === addr) return true;
}
}
return false;
}
const bunTlsSymbol = Symbol.for("::buntls::");
const bunSocketServerConnections = Symbol.for("::bunnetserverconnections::");
const bunSocketServerOptions = Symbol.for("::bunnetserveroptions::");
const owner_symbol = Symbol("owner_symbol");
@@ -353,7 +339,7 @@ const ServerHandlers: SocketHandler = {
const data = this.data;
if (!data) return;
data.server._connections--;
data.server[bunSocketServerConnections]--;
{
if (!data[kclosed]) {
data[kclosed] = true;
@@ -399,7 +385,7 @@ const ServerHandlers: SocketHandler = {
return;
}
}
if (self.maxConnections != null && self._connections >= self.maxConnections) {
if (self.maxConnections && self[bunSocketServerConnections] >= self.maxConnections) {
const data = {
localAddress: _socket.localAddress,
localPort: _socket.localPort || this.localPort,
@@ -418,7 +404,7 @@ const ServerHandlers: SocketHandler = {
const bunTLS = _socket[bunTlsSymbol];
const isTLS = typeof bunTLS === "function";
self._connections++;
self[bunSocketServerConnections]++;
if (pauseOnConnect) {
_socket.pause();
@@ -1717,23 +1703,13 @@ function internalConnect(self, options, address, port, addressType, localAddress
if (localAddress || localPort) {
if (addressType === 4) {
localAddress ||= DEFAULT_IPV4_ADDR;
if (!isLocalAddress(localAddress)) {
const UV_EADDRNOTAVAIL = -4090;
const ex = new ExceptionWithHostPort(UV_EADDRNOTAVAIL, "bind", localAddress, localPort);
self.destroy(ex);
return;
}
// TODO: bind socket when supported
// TODO:
// err = self._handle.bind(localAddress, localPort);
} else {
// addressType === 6
localAddress ||= DEFAULT_IPV6_ADDR;
if (!isLocalAddress(localAddress)) {
const UV_EADDRNOTAVAIL = -4090;
const ex = new ExceptionWithHostPort(UV_EADDRNOTAVAIL, "bind", localAddress, localPort);
self.destroy(ex);
return;
}
// TODO: bind6 socket when supported
// TODO:
// err = self._handle.bind6(localAddress, localPort, flags);
}
$debug(
"connect: binding to localAddress: %s and localPort: %d (addressType: %d)",
@@ -2099,6 +2075,7 @@ function Server(options?, connectionListener?) {
// https://nodejs.org/api/net.html#netcreateserveroptions-connectionlistener
const {
maxConnections, //
allowHalfOpen = false,
keepAlive = false,
keepAliveInitialDelay = 0,
@@ -2115,6 +2092,7 @@ function Server(options?, connectionListener?) {
this._unref = false;
this.listeningId = 1;
this[bunSocketServerConnections] = 0;
this[bunSocketServerOptions] = undefined;
this.allowHalfOpen = allowHalfOpen;
this.keepAlive = keepAlive;
@@ -2122,6 +2100,7 @@ function Server(options?, connectionListener?) {
this.highWaterMark = highWaterMark;
this.pauseOnConnect = Boolean(pauseOnConnect);
this.noDelay = noDelay;
this.maxConnections = Number.isSafeInteger(maxConnections) && maxConnections > 0 ? maxConnections : 0;
options.connectionListener = connectionListener;
this[bunSocketServerOptions] = options;
@@ -2184,7 +2163,7 @@ Server.prototype[Symbol.asyncDispose] = function () {
};
Server.prototype._emitCloseIfDrained = function _emitCloseIfDrained() {
if (this._handle || this._connections > 0) {
if (this._handle || this[bunSocketServerConnections] > 0) {
return;
}
process.nextTick(() => {
@@ -2213,7 +2192,7 @@ Server.prototype.getConnections = function getConnections(callback) {
//in Bun case we will never error on getConnections
//node only errors if in the middle of the couting the server got disconnected, what never happens in Bun
//if disconnected will only pass null as well and 0 connected
callback(null, this._handle ? this._connections : 0);
callback(null, this._handle ? this[bunSocketServerConnections] : 0);
}
return this;
};

View File

@@ -244,6 +244,31 @@ var InternalSecureContext = class SecureContext {
this.secureOptions = secureOptions;
// Validate secureProtocol if provided
if (options.secureProtocol !== undefined) {
if (typeof options.secureProtocol !== "string") {
throw new TypeError("secureProtocol argument must be a string");
}
// List of valid protocol methods supported by OpenSSL
const validProtocolMethods = [
'TLS_method',
'TLSv1_method',
'TLSv1_1_method',
'TLSv1_2_method',
'TLSv1_3_method',
'SSLv3_method',
'SSLv23_method',
'DTLS_method',
'DTLSv1_method',
'DTLSv1_2_method'
];
if (!validProtocolMethods.includes(options.secureProtocol)) {
throw $ERR_TLS_INVALID_PROTOCOL_METHOD(`Unknown method: ${options.secureProtocol}`);
}
}
if (!$isUndefinedOrNull(options.privateKeyIdentifier)) {
if ($isUndefinedOrNull(options.privateKeyEngine)) {
// prettier-ignore

View File

@@ -1,32 +0,0 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const http = require('http');
const invalidLocalAddress = '1.2.3.4';
const server = http.createServer(function(req, res) {
console.log(`Connect from: ${req.connection.remoteAddress}`);
req.on('end', function() {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end(`You are from: ${req.connection.remoteAddress}`);
});
req.resume();
});
server.listen(0, '127.0.0.1', common.mustCall(function() {
http.request({
host: 'localhost',
port: this.address().port,
path: '/',
method: 'GET',
localAddress: invalidLocalAddress
}, function(res) {
assert.fail('unexpectedly got response from server');
}).on('error', common.mustCall(function(e) {
console.log(`client got error: ${e.message}`);
server.close();
})).end();
}));

View File

@@ -1,41 +0,0 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const net = require('net');
let firstSocket;
const dormantServer = net.createServer(common.mustNotCall());
const server = net.createServer(common.mustCall((socket) => {
firstSocket = socket;
}));
dormantServer.maxConnections = 0;
server.maxConnections = 1;
dormantServer.on('drop', common.mustCall((data) => {
assert.strictEqual(!!data.localAddress, true);
assert.strictEqual(!!data.localPort, true);
assert.strictEqual(!!data.remoteAddress, true);
assert.strictEqual(!!data.remotePort, true);
assert.strictEqual(!!data.remoteFamily, true);
dormantServer.close();
}));
server.on('drop', common.mustCall((data) => {
assert.strictEqual(!!data.localAddress, true);
assert.strictEqual(!!data.localPort, true);
assert.strictEqual(!!data.remoteAddress, true);
assert.strictEqual(!!data.remotePort, true);
assert.strictEqual(!!data.remoteFamily, true);
firstSocket.destroy();
server.close();
}));
dormantServer.listen(0, () => {
net.createConnection(dormantServer.address().port);
});
server.listen(0, () => {
net.createConnection(server.address().port);
net.createConnection(server.address().port);
});

View File

@@ -0,0 +1,57 @@
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const assert = require('assert');
const tls = require('tls');
assert.throws(function() {
tls.createSecureContext({ secureProtocol: 'blargh' });
}, {
code: 'ERR_TLS_INVALID_PROTOCOL_METHOD',
message: 'Unknown method: blargh',
});
const errMessageSSLv2 = /SSLv2 methods disabled/;
assert.throws(function() {
tls.createSecureContext({ secureProtocol: 'SSLv2_method' });
}, errMessageSSLv2);
assert.throws(function() {
tls.createSecureContext({ secureProtocol: 'SSLv2_client_method' });
}, errMessageSSLv2);
assert.throws(function() {
tls.createSecureContext({ secureProtocol: 'SSLv2_server_method' });
}, errMessageSSLv2);
const errMessageSSLv3 = /SSLv3 methods disabled/;
assert.throws(function() {
tls.createSecureContext({ secureProtocol: 'SSLv3_method' });
}, errMessageSSLv3);
assert.throws(function() {
tls.createSecureContext({ secureProtocol: 'SSLv3_client_method' });
}, errMessageSSLv3);
assert.throws(function() {
tls.createSecureContext({ secureProtocol: 'SSLv3_server_method' });
}, errMessageSSLv3);
// Note that SSLv2 and SSLv3 are disallowed but SSLv2_method and friends are
// still accepted. They are OpenSSL's way of saying that all known protocols
// are supported unless explicitly disabled (which we do for SSLv2 and SSLv3.)
tls.createSecureContext({ secureProtocol: 'SSLv23_method' });
tls.createSecureContext({ secureProtocol: 'SSLv23_client_method' });
tls.createSecureContext({ secureProtocol: 'SSLv23_server_method' });
tls.createSecureContext({ secureProtocol: 'TLSv1_method' });
tls.createSecureContext({ secureProtocol: 'TLSv1_client_method' });
tls.createSecureContext({ secureProtocol: 'TLSv1_server_method' });
tls.createSecureContext({ secureProtocol: 'TLSv1_1_method' });
tls.createSecureContext({ secureProtocol: 'TLSv1_1_client_method' });
tls.createSecureContext({ secureProtocol: 'TLSv1_1_server_method' });
tls.createSecureContext({ secureProtocol: 'TLSv1_2_method' });
tls.createSecureContext({ secureProtocol: 'TLSv1_2_client_method' });
tls.createSecureContext({ secureProtocol: 'TLSv1_2_server_method' });