mirror of
https://github.com/oven-sh/bun
synced 2026-02-27 03:57:23 +01:00
Compare commits
153 Commits
deps/updat
...
ali/tls-mi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7749bf784d | ||
|
|
2f635c423d | ||
|
|
53dd1f98c4 | ||
|
|
38686f4265 | ||
|
|
372b0c3141 | ||
|
|
3afa13b8e6 | ||
|
|
40427855e6 | ||
|
|
a1124d6888 | ||
|
|
bf87b05272 | ||
|
|
29a8a45140 | ||
|
|
447a9a61b0 | ||
|
|
0acdeddb68 | ||
|
|
00e87016ca | ||
|
|
bdeaea68d3 | ||
|
|
5e267afdb7 | ||
|
|
9317dc2822 | ||
|
|
2d57bb137e | ||
|
|
4a663f306e | ||
|
|
8786ff801a | ||
|
|
6ab9b1c64b | ||
|
|
670d6b5454 | ||
|
|
6154c5d254 | ||
|
|
a1b374b889 | ||
|
|
234fa60dcf | ||
|
|
447216a17f | ||
|
|
f5047e92b5 | ||
|
|
ceca54e1fe | ||
|
|
f893b08ed2 | ||
|
|
3d94247a61 | ||
|
|
1b19c8c90c | ||
|
|
b86a462e38 | ||
|
|
10fc8ad203 | ||
|
|
527e59d966 | ||
|
|
4befd51593 | ||
|
|
0257a24c7e | ||
|
|
99a76fb896 | ||
|
|
18e34437d2 | ||
|
|
5fb30c8122 | ||
|
|
7f2b54e6ec | ||
|
|
2bbac60588 | ||
|
|
7d59378723 | ||
|
|
ff7c0494fc | ||
|
|
881a1f2dc2 | ||
|
|
1fed307743 | ||
|
|
5154101e3a | ||
|
|
f2be87e747 | ||
|
|
9268977b51 | ||
|
|
a7e9b1e782 | ||
|
|
c3bdf0edf8 | ||
|
|
382e848d44 | ||
|
|
7a52e754b1 | ||
|
|
2c91911991 | ||
|
|
0c9208c712 | ||
|
|
4b04480283 | ||
|
|
67a26f0129 | ||
|
|
a257dc2403 | ||
|
|
27dd1e8dfa | ||
|
|
e91d0229d4 | ||
|
|
5f65bf5785 | ||
|
|
5eec63b3b3 | ||
|
|
ace91ce21f | ||
|
|
cb05ed0715 | ||
|
|
f044b44db7 | ||
|
|
aea96977b0 | ||
|
|
763c01763a | ||
|
|
210d4fc32a | ||
|
|
043f24aa4b | ||
|
|
a55e27abe1 | ||
|
|
72eb99654f | ||
|
|
2e6eaaeea0 | ||
|
|
2a36a4a1cb | ||
|
|
28a6ba92ed | ||
|
|
7b64f89f2c | ||
|
|
0c72bb578e | ||
|
|
2cfec429f7 | ||
|
|
3512087684 | ||
|
|
ec5bb50501 | ||
|
|
14fe8df3dc | ||
|
|
36cc379037 | ||
|
|
1ecd74ad2e | ||
|
|
9b05d514b4 | ||
|
|
7d8b741d66 | ||
|
|
f8fd2c2a82 | ||
|
|
ef0c48a23d | ||
|
|
d3fcad833b | ||
|
|
f7fc34dfaa | ||
|
|
f3b769f1b9 | ||
|
|
2d04dcf697 | ||
|
|
5bdc738b91 | ||
|
|
6f398a1924 | ||
|
|
0b9cdc069b | ||
|
|
7d5ae23b2e | ||
|
|
2798379edf | ||
|
|
d72e605ab2 | ||
|
|
dfa4dee851 | ||
|
|
74e369ec85 | ||
|
|
f88344a61d | ||
|
|
be32a2f14a | ||
|
|
df92e99e96 | ||
|
|
9e0bdfadfe | ||
|
|
f728d17346 | ||
|
|
94293cdf51 | ||
|
|
187dc46b71 | ||
|
|
b3b276ba45 | ||
|
|
97324e2593 | ||
|
|
8ef431c4d9 | ||
|
|
0ecf930de2 | ||
|
|
164675cf7a | ||
|
|
2283341efd | ||
|
|
3d25000052 | ||
|
|
20911323df | ||
|
|
a5a8966309 | ||
|
|
416121f6a1 | ||
|
|
629b67721f | ||
|
|
040b00dc7c | ||
|
|
821848f130 | ||
|
|
da62a8984f | ||
|
|
de05784517 | ||
|
|
5986566ab7 | ||
|
|
8380a8fe6f | ||
|
|
0626a0f6e6 | ||
|
|
d60da18a86 | ||
|
|
90be0a596d | ||
|
|
d91ca54eec | ||
|
|
b6530ba6a6 | ||
|
|
8a22127721 | ||
|
|
38ff15ace2 | ||
|
|
4c14c7e838 | ||
|
|
6cb5e1ca80 | ||
|
|
5dbd814fc0 | ||
|
|
4037fe909a | ||
|
|
ef6b9da947 | ||
|
|
82a019a287 | ||
|
|
5b767a9b25 | ||
|
|
23fd3b8910 | ||
|
|
7c57c3c7da | ||
|
|
40980510d8 | ||
|
|
f32b6d0111 | ||
|
|
2baa668b37 | ||
|
|
bd51d5ee87 | ||
|
|
aae850e7e2 | ||
|
|
14ea951eec | ||
|
|
563b8f57fc | ||
|
|
7b0e470c0a | ||
|
|
4f1233eee6 | ||
|
|
033264200c | ||
|
|
90594260d1 | ||
|
|
b6ea92f555 | ||
|
|
37a55d21de | ||
|
|
addc35784b | ||
|
|
87c520dc27 | ||
|
|
35152da184 | ||
|
|
d6d14140a4 |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -183,4 +183,7 @@ codegen-for-zig-team.tar.gz
|
||||
*.sock
|
||||
scratch*.{js,ts,tsx,cjs,mjs}
|
||||
|
||||
*.bun-build
|
||||
*.bun-build
|
||||
**/.claude/settings.local.json
|
||||
|
||||
/.tmp
|
||||
|
||||
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@@ -39,6 +39,7 @@
|
||||
"zig.zls.path": "${workspaceFolder}/vendor/zig/zls.exe",
|
||||
"zig.formattingProvider": "zls",
|
||||
"zig.zls.enableInlayHints": false,
|
||||
|
||||
"[zig]": {
|
||||
"editor.tabSize": 4,
|
||||
"editor.useTabStops": false,
|
||||
|
||||
@@ -408,8 +408,8 @@ src/bun.js/bindings/webcrypto/CryptoAlgorithmSHA1.cpp
|
||||
src/bun.js/bindings/webcrypto/CryptoAlgorithmSHA224.cpp
|
||||
src/bun.js/bindings/webcrypto/CryptoAlgorithmSHA256.cpp
|
||||
src/bun.js/bindings/webcrypto/CryptoAlgorithmSHA384.cpp
|
||||
src/bun.js/bindings/webcrypto/CryptoAlgorithmX25519.cpp
|
||||
src/bun.js/bindings/webcrypto/CryptoAlgorithmSHA512.cpp
|
||||
src/bun.js/bindings/webcrypto/CryptoAlgorithmX25519.cpp
|
||||
src/bun.js/bindings/webcrypto/CryptoDigest.cpp
|
||||
src/bun.js/bindings/webcrypto/CryptoKey.cpp
|
||||
src/bun.js/bindings/webcrypto/CryptoKeyAES.cpp
|
||||
@@ -443,7 +443,6 @@ src/bun.js/bindings/webcrypto/JSHkdfParams.cpp
|
||||
src/bun.js/bindings/webcrypto/JSHmacKeyParams.cpp
|
||||
src/bun.js/bindings/webcrypto/JSJsonWebKey.cpp
|
||||
src/bun.js/bindings/webcrypto/JSPbkdf2Params.cpp
|
||||
src/bun.js/bindings/webcrypto/JSX25519Params.cpp
|
||||
src/bun.js/bindings/webcrypto/JSRsaHashedImportParams.cpp
|
||||
src/bun.js/bindings/webcrypto/JSRsaHashedKeyGenParams.cpp
|
||||
src/bun.js/bindings/webcrypto/JSRsaKeyGenParams.cpp
|
||||
@@ -451,6 +450,7 @@ src/bun.js/bindings/webcrypto/JSRsaOaepParams.cpp
|
||||
src/bun.js/bindings/webcrypto/JSRsaOtherPrimesInfo.cpp
|
||||
src/bun.js/bindings/webcrypto/JSRsaPssParams.cpp
|
||||
src/bun.js/bindings/webcrypto/JSSubtleCrypto.cpp
|
||||
src/bun.js/bindings/webcrypto/JSX25519Params.cpp
|
||||
src/bun.js/bindings/webcrypto/OpenSSLUtilities.cpp
|
||||
src/bun.js/bindings/webcrypto/PhonyWorkQueue.cpp
|
||||
src/bun.js/bindings/webcrypto/SerializedCryptoKeyWrapOpenSSL.cpp
|
||||
|
||||
@@ -164,6 +164,7 @@ src/bun.js/node/node_http_binding.zig
|
||||
src/bun.js/node/node_net_binding.zig
|
||||
src/bun.js/node/node_os.zig
|
||||
src/bun.js/node/node_process.zig
|
||||
src/bun.js/node/node_tls_binding.zig
|
||||
src/bun.js/node/node_util_binding.zig
|
||||
src/bun.js/node/node_zlib_binding.zig
|
||||
src/bun.js/node/nodejs_error_code.zig
|
||||
@@ -581,6 +582,7 @@ src/system_timer.zig
|
||||
src/test/fixtures.zig
|
||||
src/test/recover.zig
|
||||
src/thread_pool.zig
|
||||
src/tls.zig
|
||||
src/tmp.zig
|
||||
src/toml/toml_lexer.zig
|
||||
src/toml/toml_parser.zig
|
||||
|
||||
2
packages/bun-types/overrides.d.ts
vendored
2
packages/bun-types/overrides.d.ts
vendored
@@ -2,7 +2,7 @@ export {};
|
||||
|
||||
declare global {
|
||||
namespace NodeJS {
|
||||
interface ProcessEnv extends Bun.Env, ImportMetaEnv {}
|
||||
interface ProcessEnv extends Bun.Env {}
|
||||
|
||||
interface Process {
|
||||
readonly version: string;
|
||||
|
||||
@@ -66,6 +66,7 @@ struct loop_ssl_data {
|
||||
|
||||
struct us_internal_ssl_socket_context_t {
|
||||
struct us_socket_context_t sc;
|
||||
struct us_bun_socket_context_options_t options;
|
||||
|
||||
// this thing can be shared with other socket contexts via socket transfer!
|
||||
// maybe instead of holding once you hold many, a vector or set
|
||||
@@ -285,7 +286,6 @@ int us_internal_handle_shutdown(struct us_internal_ssl_socket_t *s, int force_fa
|
||||
// we got some error here, but we dont care about it, we are closing the socket
|
||||
int err = SSL_get_error(s->ssl, ret);
|
||||
if (err == SSL_ERROR_SSL || err == SSL_ERROR_SYSCALL) {
|
||||
// clear
|
||||
ERR_clear_error();
|
||||
s->fatal_error = 1;
|
||||
// Fatal error occurred, we should close the socket imeadiatly
|
||||
@@ -326,6 +326,41 @@ int us_internal_ssl_socket_is_closed(struct us_internal_ssl_socket_t *s) {
|
||||
return us_socket_is_closed(0, &s->s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the protocol error if the secure_protocol_method is set. This is to match Node's
|
||||
* behaviour
|
||||
* Will modify the verify_error struct to override the error code and reason if necessary.
|
||||
|
||||
* Returns 1 if the protocol error was overridden, 0 otherwise
|
||||
*/
|
||||
static int should_override_protocol_error(const char *proto, int is_server, int openssl_reason, struct us_bun_verify_error_t *verify_error) {
|
||||
if (!proto) return 0;
|
||||
if (is_server) {
|
||||
if (strcmp(proto, "TLSv1_method") == 0 || strcmp(proto, "TLSv1_1_method") == 0) {
|
||||
if (openssl_reason == SSL_R_WRONG_VERSION_NUMBER) {
|
||||
verify_error->code = "ERR_SSL_WRONG_VERSION_NUMBER";
|
||||
verify_error->reason = "Wrong version number on server";
|
||||
} else if (openssl_reason == SSL_R_UNSUPPORTED_PROTOCOL) {
|
||||
verify_error->code = "ERR_SSL_UNSUPPORTED_PROTOCOL";
|
||||
verify_error->reason = "Unsupported protocol on server";
|
||||
}
|
||||
|
||||
verify_error->error = -1;
|
||||
ERR_clear_error();
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
if (strcmp(proto, "SSLv23_method") == 0) {
|
||||
verify_error->code = "ERR_SSL_UNSUPPORTED_PROTOCOL";
|
||||
verify_error->reason = "Unsupported protocol";
|
||||
verify_error->error = -1;
|
||||
ERR_clear_error();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void us_internal_trigger_handshake_callback_econnreset(struct us_internal_ssl_socket_t *s) {
|
||||
struct us_internal_ssl_socket_context_t *context =
|
||||
@@ -348,6 +383,72 @@ void us_internal_trigger_handshake_callback(struct us_internal_ssl_socket_t *s,
|
||||
|
||||
if (context->on_handshake != NULL) {
|
||||
struct us_bun_verify_error_t verify_error = us_internal_verify_error(s);
|
||||
|
||||
if (!success && (verify_error.code == NULL || verify_error.code[0] == 0)) {
|
||||
const char *proto = context->options.secure_protocol_method;
|
||||
unsigned long err = ERR_peek_error();
|
||||
int reason = ERR_GET_REASON(err);
|
||||
if (should_override_protocol_error(proto, SSL_is_server(s->ssl), reason, &verify_error)) {
|
||||
context->on_handshake(s, success, verify_error, context->handshake_data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (context->options.secure_protocol_method) {
|
||||
if (SSL_is_server(s->ssl)) {
|
||||
unsigned long err = ERR_peek_error();
|
||||
int reason = ERR_GET_REASON(err);
|
||||
if ((strcmp(proto, "TLSv1_1_method") == 0 || strcmp(proto, "TLSv1_method") == 0)) {
|
||||
if (reason == SSL_R_WRONG_VERSION_NUMBER) {
|
||||
verify_error.code = "ERR_SSL_WRONG_VERSION_NUMBER";
|
||||
verify_error.reason = "Wrong version number on server";
|
||||
verify_error.error = -1;
|
||||
ERR_clear_error();
|
||||
context->on_handshake(s, success, verify_error, context->handshake_data);
|
||||
return;
|
||||
} else if (reason == SSL_R_UNSUPPORTED_PROTOCOL) {
|
||||
verify_error.code = "ERR_SSL_UNSUPPORTED_PROTOCOL";
|
||||
verify_error.reason = "Unsupported protocol on server";
|
||||
verify_error.error = -1;
|
||||
ERR_clear_error();
|
||||
context->on_handshake(s, success, verify_error, context->handshake_data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (strcmp(proto, "SSLv23_method") == 0) {
|
||||
verify_error.code = "ERR_SSL_UNSUPPORTED_PROTOCOL";
|
||||
verify_error.reason = "Unsupported protocol";
|
||||
verify_error.error = -1;
|
||||
ERR_clear_error();
|
||||
context->on_handshake(s, success, verify_error, context->handshake_data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (verify_error.error == 0) {
|
||||
verify_error.error = -1;
|
||||
|
||||
unsigned long err = ERR_peek_error();
|
||||
int reason = ERR_GET_REASON(err);
|
||||
|
||||
if (reason == SSL_R_TLSV1_ALERT_PROTOCOL_VERSION) {
|
||||
verify_error.code = "ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION";
|
||||
verify_error.reason = "TLSv1 alert protocol version";
|
||||
} else if (reason == SSL_R_UNSUPPORTED_PROTOCOL) {
|
||||
verify_error.code = "ERR_SSL_UNSUPPORTED_PROTOCOL";
|
||||
verify_error.reason = SSL_is_server(s->ssl) ? "Unsupported protocol on server" : "Unsupported protocol on client";
|
||||
} else if (reason == SSL_R_WRONG_VERSION_NUMBER) {
|
||||
verify_error.code = "ERR_SSL_WRONG_VERSION_NUMBER";
|
||||
verify_error.reason = "Wrong version number on server";
|
||||
} else {
|
||||
verify_error.code = "ERR_SSL_UNSUPPORTED_PROTOCOL";
|
||||
verify_error.reason = "Unsupported protocol";
|
||||
}
|
||||
ERR_clear_error();
|
||||
}
|
||||
}
|
||||
|
||||
context->on_handshake(s, success, verify_error, context->handshake_data);
|
||||
}
|
||||
}
|
||||
@@ -400,8 +501,7 @@ void us_internal_update_handshake(struct us_internal_ssl_socket_t *s) {
|
||||
|
||||
if (us_internal_ssl_socket_is_closed(s) || us_internal_ssl_socket_is_shut_down(s) ||
|
||||
(s->ssl && SSL_get_shutdown(s->ssl) & SSL_RECEIVED_SHUTDOWN)) {
|
||||
|
||||
us_internal_trigger_handshake_callback(s, 0);
|
||||
us_internal_trigger_handshake_callback_econnreset(s);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -507,6 +607,7 @@ restart:
|
||||
|
||||
if (just_read <= 0) {
|
||||
int err = SSL_get_error(s->ssl, just_read);
|
||||
|
||||
// as far as I know these are the only errors we want to handle
|
||||
if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) {
|
||||
if (err == SSL_ERROR_WANT_RENEGOTIATE) {
|
||||
@@ -540,12 +641,11 @@ restart:
|
||||
}
|
||||
|
||||
if (err == SSL_ERROR_SSL || err == SSL_ERROR_SYSCALL) {
|
||||
// clear per thread error queue if it may contain something
|
||||
ERR_clear_error();
|
||||
s->fatal_error = 1;
|
||||
us_internal_trigger_handshake_callback(s, 0);
|
||||
}
|
||||
|
||||
// terminate connection here
|
||||
// Terminate connection after reporting the handshake error
|
||||
us_internal_ssl_socket_close(s, 0, NULL);
|
||||
return NULL; // stop processing data
|
||||
} else {
|
||||
@@ -1140,14 +1240,31 @@ SSL_CTX *create_ssl_context_from_bun_options(
|
||||
/* Create the context */
|
||||
SSL_CTX *ssl_context = SSL_CTX_new(TLS_method());
|
||||
|
||||
|
||||
/* Default options we rely on - changing these will break our logic */
|
||||
SSL_CTX_set_read_ahead(ssl_context, 1);
|
||||
/* we should always accept moving write buffer so we can retry writes with a
|
||||
* buffer allocated in a different address */
|
||||
SSL_CTX_set_mode(ssl_context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
|
||||
if (options.min_tls_version > 0) {
|
||||
if (!SSL_CTX_set_min_proto_version(ssl_context, options.min_tls_version)) {
|
||||
free_ssl_context(ssl_context);
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
|
||||
if (!SSL_CTX_set_min_proto_version(ssl_context, TLS1_2_VERSION)) {
|
||||
free_ssl_context(ssl_context);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Anything below TLS 1.2 is disabled */
|
||||
SSL_CTX_set_min_proto_version(ssl_context, TLS1_2_VERSION);
|
||||
if (options.max_tls_version > 0) {
|
||||
if (!SSL_CTX_set_max_proto_version(ssl_context, options.max_tls_version)) {
|
||||
free_ssl_context(ssl_context);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* The following are helpers. You may easily implement whatever you want by
|
||||
* using the native handle directly */
|
||||
@@ -1545,6 +1662,7 @@ us_internal_bun_create_ssl_socket_context(
|
||||
context->ssl_context =
|
||||
ssl_context; // create_ssl_context_from_options(options);
|
||||
context->is_parent = 1;
|
||||
context->options = options;
|
||||
|
||||
context->on_handshake = NULL;
|
||||
context->handshake_data = NULL;
|
||||
|
||||
@@ -237,6 +237,9 @@ struct us_bun_socket_context_options_t {
|
||||
int request_cert;
|
||||
unsigned int client_renegotiation_limit;
|
||||
unsigned int client_renegotiation_window;
|
||||
unsigned int min_tls_version;
|
||||
unsigned int max_tls_version;
|
||||
const char *secure_protocol_method;
|
||||
};
|
||||
|
||||
/* Return 15-bit timestamp for this context */
|
||||
|
||||
@@ -78,6 +78,9 @@ namespace uWS {
|
||||
int request_cert = 0;
|
||||
unsigned int client_renegotiation_limit = 3;
|
||||
unsigned int client_renegotiation_window = 600;
|
||||
unsigned int min_tls_version = 0;
|
||||
unsigned int max_tls_version = 0;
|
||||
const char **secure_protocol_method = nullptr;
|
||||
|
||||
/* Conversion operator used internally */
|
||||
operator struct us_bun_socket_context_options_t() const {
|
||||
|
||||
@@ -715,6 +715,12 @@ pub const Listener = struct {
|
||||
return globalObject.throwValue(err);
|
||||
};
|
||||
|
||||
if (ssl_enabled and create_err != .none) {
|
||||
const js_err = create_err.toJS(globalObject);
|
||||
uws.us_socket_context_free(1, socket_context);
|
||||
return globalObject.throwValue(js_err);
|
||||
}
|
||||
|
||||
if (ssl_enabled) {
|
||||
if (ssl.?.protos) |p| {
|
||||
protos = p[0..ssl.?.protos_len];
|
||||
@@ -1217,6 +1223,12 @@ pub const Listener = struct {
|
||||
return globalObject.throwValue(err.toErrorInstance(globalObject));
|
||||
};
|
||||
|
||||
if (ssl_enabled and create_err != .none) {
|
||||
const js_err = create_err.toJS(globalObject);
|
||||
uws.us_socket_context_free(1, socket_context);
|
||||
return globalObject.throwValue(js_err);
|
||||
}
|
||||
|
||||
if (ssl_enabled) {
|
||||
if (ssl.?.protos) |p| {
|
||||
protos = p[0..ssl.?.protos_len];
|
||||
|
||||
@@ -282,6 +282,7 @@ pub fn SSLWrapper(comptime T: type) type {
|
||||
if (this.flags.closed_notified) return;
|
||||
|
||||
this.flags.authorized = success;
|
||||
|
||||
// trigger the handshake callback
|
||||
this.handlers.onHandshake(this.handlers.ctx, success, result);
|
||||
}
|
||||
@@ -311,8 +312,25 @@ pub fn SSLWrapper(comptime T: type) type {
|
||||
if (this.isShutdown()) {
|
||||
return .{};
|
||||
}
|
||||
|
||||
const ssl = this.ssl orelse return .{};
|
||||
return uws.us_ssl_socket_verify_error_from_ssl(ssl);
|
||||
|
||||
const peek_err = BoringSSL.ERR_peek_error();
|
||||
var verr = uws.us_ssl_socket_verify_error_from_ssl(ssl);
|
||||
|
||||
// no certificate verification = handshake error
|
||||
if (verr.code == null and peek_err != 0) {
|
||||
const reason_ptr = BoringSSL.ERR_reason_error_string(peek_err);
|
||||
|
||||
verr = uws.us_bun_verify_error_t{
|
||||
.error_no = @intCast(peek_err),
|
||||
.code = reason_ptr,
|
||||
.reason = reason_ptr,
|
||||
};
|
||||
BoringSSL.ERR_clear_error();
|
||||
}
|
||||
|
||||
return verr;
|
||||
}
|
||||
|
||||
/// Update the handshake state
|
||||
|
||||
@@ -693,6 +693,10 @@ pub const ServerConfig = struct {
|
||||
client_renegotiation_limit: u32 = 0,
|
||||
client_renegotiation_window: u32 = 0,
|
||||
|
||||
min_version: ?u16 = null,
|
||||
max_version: ?u16 = null,
|
||||
secure_protocol_method: ?[*:0]const u8 = null,
|
||||
|
||||
const log = Output.scoped(.SSLConfig, false);
|
||||
|
||||
pub fn asUSockets(this: SSLConfig) uws.us_bun_socket_context_options_t {
|
||||
@@ -729,6 +733,18 @@ pub const ServerConfig = struct {
|
||||
ctx_opts.request_cert = this.request_cert;
|
||||
ctx_opts.reject_unauthorized = this.reject_unauthorized;
|
||||
|
||||
if (this.min_version) |version| {
|
||||
ctx_opts.min_tls_version = version;
|
||||
}
|
||||
|
||||
if (this.max_version) |version| {
|
||||
ctx_opts.max_tls_version = version;
|
||||
}
|
||||
|
||||
if (this.secure_protocol_method != null) {
|
||||
ctx_opts.secure_protocol_method = this.secure_protocol_method;
|
||||
}
|
||||
|
||||
return ctx_opts;
|
||||
}
|
||||
|
||||
@@ -743,6 +759,7 @@ pub const ServerConfig = struct {
|
||||
"passphrase",
|
||||
"ssl_ciphers",
|
||||
"protos",
|
||||
"secure_protocol_method",
|
||||
};
|
||||
|
||||
inline for (fields) |field| {
|
||||
@@ -759,7 +776,7 @@ pub const ServerConfig = struct {
|
||||
|
||||
{
|
||||
//numbers
|
||||
const fields = .{ "secure_options", "request_cert", "reject_unauthorized", "low_memory_mode" };
|
||||
const fields = .{ "secure_options", "request_cert", "reject_unauthorized", "low_memory_mode", "min_version", "max_version" };
|
||||
|
||||
inline for (fields) |field| {
|
||||
const lhs = @field(thisConfig, field);
|
||||
@@ -1084,6 +1101,26 @@ pub const ServerConfig = struct {
|
||||
any = true;
|
||||
}
|
||||
|
||||
if (try obj.getTruthy(global, "minVersion")) |min_version| {
|
||||
result.min_version = @as(u16, @intCast(min_version.toInt32()));
|
||||
any = true;
|
||||
}
|
||||
|
||||
if (try obj.getTruthy(global, "maxVersion")) |max_version| {
|
||||
result.max_version = @as(u16, @intCast(max_version.toInt32()));
|
||||
any = true;
|
||||
}
|
||||
|
||||
if (try obj.getTruthy(global, "secureProtocol")) |proto| {
|
||||
var sliced = try proto.toSlice(global, bun.default_allocator);
|
||||
defer sliced.deinit();
|
||||
if (sliced.len > 0) {
|
||||
result.secure_protocol_method = try bun.default_allocator.dupeZ(u8, sliced.slice());
|
||||
any = true;
|
||||
result.requires_custom_request_ctx = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (try obj.getTruthy(global, "ciphers")) |ssl_ciphers| {
|
||||
var sliced = try ssl_ciphers.toSlice(global, bun.default_allocator);
|
||||
defer sliced.deinit();
|
||||
|
||||
@@ -241,6 +241,7 @@ const errors: ErrorCodeMapping = [
|
||||
["ERR_TLS_PSK_SET_IDENTITY_HINT_FAILED", Error],
|
||||
["ERR_TLS_RENEGOTIATION_DISABLED", Error],
|
||||
["ERR_TLS_SNI_FROM_SERVER", Error],
|
||||
["ERR_SSL_UNSUPPORTED_PROTOCOL", Error],
|
||||
["ERR_UNAVAILABLE_DURING_EXIT", Error],
|
||||
["ERR_UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET", Error],
|
||||
["ERR_UNESCAPED_CHARACTERS", TypeError],
|
||||
|
||||
20
src/bun.js/node/node_tls_binding.zig
Normal file
20
src/bun.js/node/node_tls_binding.zig
Normal file
@@ -0,0 +1,20 @@
|
||||
const std = @import("std");
|
||||
|
||||
const bun = @import("bun");
|
||||
const JSC = bun.JSC;
|
||||
|
||||
pub fn getDefaultMinTLSVersionFromCLIFlag(_: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSC.JSValue {
|
||||
if (bun.tls.min_tls_version_from_cli_flag) |version| {
|
||||
return JSC.JSValue.jsNumber(version);
|
||||
}
|
||||
|
||||
return JSC.JSValue.jsNull();
|
||||
}
|
||||
|
||||
pub fn getDefaultMaxTLSVersionFromCLIFlag(_: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JSError!JSC.JSValue {
|
||||
if (bun.tls.max_tls_version_from_cli_flag) |version| {
|
||||
return JSC.JSValue.jsNumber(version);
|
||||
}
|
||||
|
||||
return JSC.JSValue.jsNull();
|
||||
}
|
||||
@@ -599,6 +599,7 @@ pub const Bunfig = @import("./bunfig.zig").Bunfig;
|
||||
|
||||
pub const HTTPThread = @import("./http.zig").HTTPThread;
|
||||
pub const http = @import("./http.zig");
|
||||
pub const tls = @import("./tls.zig");
|
||||
|
||||
pub const Analytics = @import("./analytics/analytics_thread.zig");
|
||||
|
||||
|
||||
27
src/cli.zig
27
src/cli.zig
@@ -247,6 +247,12 @@ pub const Arguments = struct {
|
||||
clap.parseParam("--zero-fill-buffers Boolean to force Buffer.allocUnsafe(size) to be zero-filled.") catch unreachable,
|
||||
clap.parseParam("--redis-preconnect Preconnect to $REDIS_URL at startup") catch unreachable,
|
||||
clap.parseParam("--no-addons Throw an error if process.dlopen is called, and disable export condition \"node-addons\"") catch unreachable,
|
||||
clap.parseParam("--tls-max-v1.2 Set the maximum TLS version to 1.2") catch unreachable,
|
||||
clap.parseParam("--tls-max-v1.3 Set the maximum TLS version to 1.3") catch unreachable,
|
||||
clap.parseParam("--tls-min-v1.0 Set the minimum TLS version to 1") catch unreachable,
|
||||
clap.parseParam("--tls-min-v1.1 Set the minimum TLS version to 1.1") catch unreachable,
|
||||
clap.parseParam("--tls-min-v1.2 Set the minimum TLS version to 1.2") catch unreachable,
|
||||
clap.parseParam("--tls-min-v1.3 Set the minimum TLS version to 1.3") catch unreachable,
|
||||
};
|
||||
|
||||
const auto_or_run_params = [_]ParamType{
|
||||
@@ -754,6 +760,27 @@ pub const Arguments = struct {
|
||||
}
|
||||
}
|
||||
|
||||
// TLS version flags here are specified in ascending order for MAX, and descending order for MIN
|
||||
// because Node will use the maximum value for --tls-max and the minimum value for --tls-min
|
||||
// See comments on:
|
||||
// - https://bun.sh/reference/node/tls/DEFAULT_MAX_VERSION
|
||||
// - https://bun.sh/reference/node/tls/DEFAULT_MIN_VERSION
|
||||
|
||||
// if (args.flag("--tls-max-v1.0")) bun.tls.max_tls_version = 0x0301;
|
||||
// if (args.flag("--tls-max-v1.1")) bun.tls.max_tls_version = 0x0302;
|
||||
if (args.flag("--tls-max-v1.2")) bun.tls.max_tls_version_from_cli_flag = 0x0303;
|
||||
if (args.flag("--tls-max-v1.3")) bun.tls.max_tls_version_from_cli_flag = 0x0304;
|
||||
|
||||
if (args.flag("--tls-min-v1.3")) bun.tls.min_tls_version_from_cli_flag = 0x0304;
|
||||
if (args.flag("--tls-min-v1.2")) bun.tls.min_tls_version_from_cli_flag = 0x0303;
|
||||
if (args.flag("--tls-min-v1.1")) bun.tls.min_tls_version_from_cli_flag = 0x0302;
|
||||
if (args.flag("--tls-min-v1.0")) bun.tls.min_tls_version_from_cli_flag = 0x0301;
|
||||
|
||||
if (bun.tls.min_tls_version_from_cli_flag != null and bun.tls.max_tls_version_from_cli_flag != null) {
|
||||
Output.errGeneric("either --tls-min-v1.x or --tls-max-v1.x can be used, not both", .{});
|
||||
Global.exit(1);
|
||||
}
|
||||
|
||||
ctx.debug.offline_mode_setting = if (args.flag("--prefer-offline"))
|
||||
Bunfig.OfflineMode.offline
|
||||
else if (args.flag("--prefer-latest"))
|
||||
|
||||
@@ -2552,6 +2552,9 @@ pub const us_bun_socket_context_options_t = extern struct {
|
||||
request_cert: i32 = 0,
|
||||
client_renegotiation_limit: u32 = 3,
|
||||
client_renegotiation_window: u32 = 600,
|
||||
min_tls_version: u32 = 0,
|
||||
max_tls_version: u32 = 0,
|
||||
secure_protocol_method: [*c]const u8 = null,
|
||||
};
|
||||
|
||||
pub const create_bun_socket_error_t = enum(c_int) {
|
||||
@@ -2581,15 +2584,32 @@ pub const us_bun_verify_error_t = extern struct {
|
||||
reason: [*c]const u8 = null,
|
||||
|
||||
pub fn toJS(this: *const us_bun_verify_error_t, globalObject: *JSC.JSGlobalObject) JSC.JSValue {
|
||||
const code = if (this.code == null) "" else this.code[0..bun.len(this.code)];
|
||||
const reason = if (this.reason == null) "" else this.reason[0..bun.len(this.reason)];
|
||||
const message_slice = if (this.reason != null and bun.len(this.reason) > 0)
|
||||
this.reason[0..bun.len(this.reason)]
|
||||
else if (this.code != null and bun.len(this.code) > 0)
|
||||
this.code[0..bun.len(this.code)]
|
||||
else
|
||||
"TLS Error";
|
||||
|
||||
const fallback = JSC.SystemError{
|
||||
.code = bun.String.createUTF8(code),
|
||||
.message = bun.String.createUTF8(reason),
|
||||
const code_slice = if (this.code != null and bun.len(this.code) > 0)
|
||||
this.code[0..bun.len(this.code)]
|
||||
else
|
||||
"";
|
||||
|
||||
const sys_error_details = JSC.SystemError{
|
||||
.message = bun.String.createUTF8(message_slice),
|
||||
.code = bun.String.createUTF8(code_slice),
|
||||
.errno = this.error_no,
|
||||
};
|
||||
|
||||
return fallback.toErrorInstance(globalObject);
|
||||
const js_error_value = sys_error_details.toErrorInstance(globalObject);
|
||||
|
||||
if (code_slice.len > 0) {
|
||||
const js_error_obj = js_error_value.toObject(globalObject) catch bun.outOfMemory();
|
||||
js_error_obj.put(globalObject, JSC.ZigString.static("code"), bun.String.createUTF8ForJS(globalObject, code_slice)) catch bun.outOfMemory();
|
||||
}
|
||||
|
||||
return js_error_value;
|
||||
}
|
||||
};
|
||||
pub extern fn us_ssl_socket_verify_error_from_ssl(ssl: *BoringSSL.SSL) us_bun_verify_error_t;
|
||||
|
||||
3
src/js/builtins.d.ts
vendored
3
src/js/builtins.d.ts
vendored
@@ -682,6 +682,7 @@ declare function $makeAbortError(message?: string, options?: { cause: Error }):
|
||||
*/
|
||||
declare function $ERR_INVALID_ARG_TYPE(argName: string, expectedType: string, actualValue: any): TypeError;
|
||||
declare function $ERR_INVALID_ARG_TYPE(argName: string, expectedTypes: string[], actualValue: any): TypeError;
|
||||
declare function $ERR_INVALID_ARG_TYPE(message: string): TypeError;
|
||||
declare function $ERR_INVALID_ARG_VALUE(name: string, value: any, reason?: string): TypeError;
|
||||
declare function $ERR_UNKNOWN_ENCODING(enc: string): TypeError;
|
||||
declare function $ERR_STREAM_DESTROYED(method: string): Error;
|
||||
@@ -695,7 +696,7 @@ declare function $ERR_MISSING_ARGS(...args: [string, ...string[]]): TypeError;
|
||||
*/
|
||||
declare function $ERR_MISSING_ARGS(oneOf: string[]): TypeError;
|
||||
declare function $ERR_INVALID_RETURN_VALUE(expected_type: string, name: string, actual_value: any): TypeError;
|
||||
declare function $ERR_TLS_INVALID_PROTOCOL_VERSION(a: string, b: string): TypeError;
|
||||
declare function $ERR_TLS_INVALID_PROTOCOL_VERSION(a: import("tls").SecureVersion, b: "maximum" | "minimum"): TypeError;
|
||||
declare function $ERR_TLS_PROTOCOL_VERSION_CONFLICT(a: string, b: string): TypeError;
|
||||
declare function $ERR_INVALID_IP_ADDRESS(ip: any): TypeError;
|
||||
declare function $ERR_INVALID_ADDRESS_FAMILY(addressType, host, port): RangeError;
|
||||
|
||||
@@ -1,5 +1,33 @@
|
||||
const { isTypedArray, isArrayBuffer } = require("node:util/types");
|
||||
|
||||
const getDefaultMinTLSVersionFromCLIFlag = $newZigFunction(
|
||||
"node_tls_binding.zig",
|
||||
"getDefaultMinTLSVersionFromCLIFlag",
|
||||
0,
|
||||
) as () => number | null;
|
||||
|
||||
const getDefaultMaxTLSVersionFromCLIFlag = $newZigFunction(
|
||||
"node_tls_binding.zig",
|
||||
"getDefaultMaxTLSVersionFromCLIFlag",
|
||||
0,
|
||||
) as () => number | null;
|
||||
|
||||
const TLS_VERSION_MAP = {
|
||||
"TLSv1": 0x0301,
|
||||
"TLSv1.1": 0x0302,
|
||||
"TLSv1.2": 0x0303,
|
||||
"TLSv1.3": 0x0304,
|
||||
} as const satisfies Record<import("node:tls").SecureVersion, number>;
|
||||
|
||||
const TLS_VERSION_REVERSE_MAP: {
|
||||
[Key in keyof typeof TLS_VERSION_MAP as (typeof TLS_VERSION_MAP)[Key]]: Key;
|
||||
} = {
|
||||
0x0301: "TLSv1",
|
||||
0x0302: "TLSv1.1",
|
||||
0x0303: "TLSv1.2",
|
||||
0x0304: "TLSv1.3",
|
||||
};
|
||||
|
||||
function isPemObject(obj: unknown): obj is { pem: unknown } {
|
||||
return $isObject(obj) && "pem" in obj;
|
||||
}
|
||||
@@ -50,4 +78,254 @@ function isValidTLSArray(obj: unknown) {
|
||||
|
||||
const VALID_TLS_ERROR_MESSAGE_TYPES = "string or an instance of Buffer, TypedArray, DataView, or BunFile";
|
||||
|
||||
export { VALID_TLS_ERROR_MESSAGE_TYPES, isValidTLSArray, isValidTLSItem, throwOnInvalidTLSArray };
|
||||
function getTlsVersionOrDefault(version: number | null, fallback: import("node:tls").SecureVersion) {
|
||||
if (!version) return fallback;
|
||||
const asString = TLS_VERSION_REVERSE_MAP[version];
|
||||
if (!asString) return fallback;
|
||||
return asString;
|
||||
}
|
||||
|
||||
const DEFAULT_MIN_VERSION: import("node:tls").SecureVersion = getTlsVersionOrDefault(
|
||||
getDefaultMinTLSVersionFromCLIFlag(),
|
||||
"TLSv1.2",
|
||||
);
|
||||
const DEFAULT_MAX_VERSION: import("node:tls").SecureVersion = getTlsVersionOrDefault(
|
||||
getDefaultMaxTLSVersionFromCLIFlag(),
|
||||
"TLSv1.3",
|
||||
);
|
||||
|
||||
// const forbiddenProtocols = ["SSLv23_method", "TLSv1_1_method", "TLSv1_method"];
|
||||
|
||||
function resolveTLSVersions(options: import("node:tls").TLSSocketOptions): [min: number, max: number] {
|
||||
// if (typeof options?.secureProtocol === "string" && forbiddenProtocols.includes(options.secureProtocol)) {
|
||||
// throw $ERR_SSL_UNSUPPORTED_PROTOCOL(`Protocol method ${options.secureProtocol} is not supported`);
|
||||
// }
|
||||
|
||||
const secureProtocol = options?.secureProtocol;
|
||||
const maybeConflictVersion = options.minVersion || options.maxVersion;
|
||||
if (secureProtocol && maybeConflictVersion) {
|
||||
throw $ERR_TLS_PROTOCOL_VERSION_CONFLICT(maybeConflictVersion, secureProtocol);
|
||||
}
|
||||
|
||||
let minVersionName: import("node:tls").SecureVersion = DEFAULT_MIN_VERSION;
|
||||
let maxVersionName: import("node:tls").SecureVersion = DEFAULT_MAX_VERSION;
|
||||
|
||||
// Node's C++ logic: https://github.com/nodejs/node/blob/main/src/crypto/crypto_context.cc
|
||||
if (typeof secureProtocol === "string") {
|
||||
if (
|
||||
secureProtocol === "SSLv2_method" ||
|
||||
secureProtocol === "SSLv2_server_method" ||
|
||||
secureProtocol === "SSLv2_client_method"
|
||||
) {
|
||||
throw $ERR_TLS_INVALID_PROTOCOL_METHOD("SSLv2 methods disabled");
|
||||
} else if (
|
||||
secureProtocol === "SSLv3_method" ||
|
||||
secureProtocol === "SSLv3_server_method" ||
|
||||
secureProtocol === "SSLv3_client_method"
|
||||
) {
|
||||
throw $ERR_TLS_INVALID_PROTOCOL_METHOD("SSLv3 methods disabled");
|
||||
} else if (secureProtocol === "SSLv23_method") {
|
||||
minVersionName = DEFAULT_MIN_VERSION;
|
||||
maxVersionName = DEFAULT_MAX_VERSION;
|
||||
} else if (secureProtocol === "SSLv23_server_method") {
|
||||
minVersionName = DEFAULT_MIN_VERSION;
|
||||
maxVersionName = DEFAULT_MAX_VERSION;
|
||||
} else if (secureProtocol === "SSLv23_client_method") {
|
||||
minVersionName = DEFAULT_MIN_VERSION;
|
||||
maxVersionName = DEFAULT_MAX_VERSION;
|
||||
// method = TLS_client_method();
|
||||
} else if (secureProtocol === "TLS_method") {
|
||||
minVersionName = "TLSv1";
|
||||
maxVersionName = "TLSv1.3";
|
||||
} else if (secureProtocol === "TLS_server_method") {
|
||||
minVersionName = "TLSv1";
|
||||
maxVersionName = "TLSv1.3";
|
||||
// method = TLS_server_method();
|
||||
} else if (secureProtocol === "TLS_client_method") {
|
||||
minVersionName = "TLSv1";
|
||||
maxVersionName = "TLSv1.3";
|
||||
// method = TLS_client_method();
|
||||
} else if (secureProtocol === "TLSv1_method") {
|
||||
minVersionName = maxVersionName = "TLSv1";
|
||||
} else if (secureProtocol === "TLSv1_server_method") {
|
||||
minVersionName = maxVersionName = "TLSv1";
|
||||
// method = TLS_server_method();
|
||||
} else if (secureProtocol === "TLSv1_client_method") {
|
||||
minVersionName = maxVersionName = "TLSv1";
|
||||
// method = TLS_client_method();
|
||||
} else if (secureProtocol === "TLSv1_1_method") {
|
||||
minVersionName = maxVersionName = "TLSv1.1";
|
||||
} else if (secureProtocol === "TLSv1_1_server_method") {
|
||||
minVersionName = maxVersionName = "TLSv1.1";
|
||||
// method = TLS_server_method();
|
||||
} else if (secureProtocol === "TLSv1_1_client_method") {
|
||||
minVersionName = maxVersionName = "TLSv1.1";
|
||||
// method = TLS_client_method();
|
||||
} else if (secureProtocol === "TLSv1_2_method") {
|
||||
minVersionName = maxVersionName = "TLSv1.2";
|
||||
} else if (secureProtocol === "TLSv1_2_server_method") {
|
||||
minVersionName = maxVersionName = "TLSv1.2";
|
||||
// method = TLS_server_method();
|
||||
} else if (secureProtocol === "TLSv1_2_client_method") {
|
||||
minVersionName = maxVersionName = "TLSv1.2";
|
||||
// method = TLS_client_method();
|
||||
} else if (secureProtocol === "TLSv1_3_method") {
|
||||
minVersionName = maxVersionName = "TLSv1.3";
|
||||
} else if (secureProtocol === "TLSv1_3_server_method") {
|
||||
minVersionName = maxVersionName = "TLSv1.3";
|
||||
// method = TLS_server_method();
|
||||
} else if (secureProtocol === "TLSv1_3_client_method") {
|
||||
minVersionName = maxVersionName = "TLSv1.3";
|
||||
// method = TLS_client_method();
|
||||
} else {
|
||||
throw $ERR_TLS_INVALID_PROTOCOL_METHOD(`Unknown method: ${secureProtocol}`);
|
||||
}
|
||||
} else {
|
||||
minVersionName = options && options.minVersion !== undefined ? options.minVersion : DEFAULT_MIN_VERSION;
|
||||
maxVersionName = options && options.maxVersion !== undefined ? options.maxVersion : DEFAULT_MAX_VERSION;
|
||||
}
|
||||
|
||||
let minVersion: number;
|
||||
let maxVersion: number;
|
||||
|
||||
if (typeof minVersionName === "string") {
|
||||
if (!(minVersionName in TLS_VERSION_MAP)) {
|
||||
throw $ERR_TLS_INVALID_PROTOCOL_VERSION(minVersionName, "minimum");
|
||||
}
|
||||
minVersion = TLS_VERSION_MAP[minVersionName];
|
||||
} else {
|
||||
throw $ERR_INVALID_ARG_TYPE("options.minVersion", "string", minVersionName);
|
||||
}
|
||||
|
||||
if (typeof maxVersionName === "string") {
|
||||
if (!(maxVersionName in TLS_VERSION_MAP)) {
|
||||
throw $ERR_TLS_INVALID_PROTOCOL_VERSION(maxVersionName, "maximum");
|
||||
}
|
||||
maxVersion = TLS_VERSION_MAP[maxVersionName];
|
||||
} else {
|
||||
throw $ERR_INVALID_ARG_TYPE("options.maxVersion", "string", maxVersionName);
|
||||
}
|
||||
|
||||
return [minVersion, maxVersion];
|
||||
}
|
||||
|
||||
function validateTLSOptions(options: any) {
|
||||
if (!options || typeof options !== "object") return;
|
||||
|
||||
let cert = options.cert;
|
||||
if (cert) throwOnInvalidTLSArray("options.cert", cert);
|
||||
|
||||
let key = options.key;
|
||||
if (key) throwOnInvalidTLSArray("options.key", key);
|
||||
|
||||
let ca = options.ca;
|
||||
if (ca) throwOnInvalidTLSArray("options.ca", ca);
|
||||
|
||||
if (!$isUndefinedOrNull(options.privateKeyIdentifier)) {
|
||||
if ($isUndefinedOrNull(options.privateKeyEngine)) {
|
||||
throw $ERR_INVALID_ARG_VALUE("options.privateKeyEngine", options.privateKeyEngine);
|
||||
} else if (typeof options.privateKeyEngine !== "string") {
|
||||
throw $ERR_INVALID_ARG_TYPE(
|
||||
"options.privateKeyEngine",
|
||||
["string", "null", "undefined"],
|
||||
options.privateKeyEngine,
|
||||
);
|
||||
}
|
||||
if (typeof options.privateKeyIdentifier !== "string") {
|
||||
throw $ERR_INVALID_ARG_TYPE(
|
||||
"options.privateKeyIdentifier",
|
||||
["string", "null", "undefined"],
|
||||
options.privateKeyIdentifier,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const ciphers = options.ciphers;
|
||||
if (ciphers !== undefined && typeof ciphers !== "string") {
|
||||
throw $ERR_INVALID_ARG_TYPE("options.ciphers", "string", ciphers);
|
||||
}
|
||||
|
||||
const passphrase = options.passphrase;
|
||||
if (passphrase !== undefined && typeof passphrase !== "string") {
|
||||
throw $ERR_INVALID_ARG_TYPE("options.passphrase", "string", passphrase);
|
||||
}
|
||||
|
||||
const servername = options.servername;
|
||||
if (servername !== undefined && typeof servername !== "string") {
|
||||
throw $ERR_INVALID_ARG_TYPE("options.servername", "string", servername);
|
||||
}
|
||||
|
||||
const ecdhCurve = options.ecdhCurve;
|
||||
if (ecdhCurve !== undefined && typeof ecdhCurve !== "string") {
|
||||
throw $ERR_INVALID_ARG_TYPE("options.ecdhCurve", "string", ecdhCurve);
|
||||
}
|
||||
|
||||
const handshakeTimeout = options.handshakeTimeout;
|
||||
if (handshakeTimeout !== undefined && typeof handshakeTimeout !== "number") {
|
||||
throw $ERR_INVALID_ARG_TYPE("options.handshakeTimeout", "number", handshakeTimeout);
|
||||
}
|
||||
|
||||
const sessionTimeout = options.sessionTimeout;
|
||||
if (sessionTimeout !== undefined && typeof sessionTimeout !== "number") {
|
||||
throw $ERR_INVALID_ARG_TYPE("options.sessionTimeout", "number", sessionTimeout);
|
||||
}
|
||||
|
||||
const ticketKeys = options.ticketKeys;
|
||||
if (ticketKeys !== undefined) {
|
||||
if (!Buffer.isBuffer(ticketKeys)) {
|
||||
throw $ERR_INVALID_ARG_TYPE("options.ticketKeys", "Buffer", ticketKeys);
|
||||
}
|
||||
if (ticketKeys.length !== 48) {
|
||||
throw $ERR_INVALID_ARG_VALUE(
|
||||
"options.ticketKeys",
|
||||
ticketKeys.length,
|
||||
"The property 'options.ticketKeys' must be exactly 48 bytes",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const secureOptions = options.secureOptions || 0;
|
||||
if (secureOptions && typeof secureOptions !== "number") {
|
||||
throw $ERR_INVALID_ARG_TYPE("options.secureOptions", "number", secureOptions);
|
||||
}
|
||||
|
||||
const requestCert = options.requestCert;
|
||||
if (requestCert !== undefined && typeof requestCert !== "boolean") {
|
||||
throw $ERR_INVALID_ARG_TYPE("options.requestCert", "boolean", requestCert);
|
||||
}
|
||||
|
||||
const rejectUnauthorized = options.rejectUnauthorized;
|
||||
if (rejectUnauthorized !== undefined && typeof rejectUnauthorized !== "boolean") {
|
||||
throw $ERR_INVALID_ARG_TYPE("options.rejectUnauthorized", "boolean", rejectUnauthorized);
|
||||
}
|
||||
}
|
||||
|
||||
let warnOnAllowUnauthorized = true;
|
||||
|
||||
function getAllowUnauthorized(): boolean {
|
||||
const allowUnauthorized = process.env.NODE_TLS_REJECT_UNAUTHORIZED === "0";
|
||||
|
||||
if (allowUnauthorized && warnOnAllowUnauthorized) {
|
||||
warnOnAllowUnauthorized = false;
|
||||
process.emitWarning(
|
||||
"Setting the NODE_TLS_REJECT_UNAUTHORIZED environment variable to '0' makes TLS " +
|
||||
"connections and HTTPS requests insecure by disabling certificate verification.",
|
||||
);
|
||||
}
|
||||
return allowUnauthorized;
|
||||
}
|
||||
|
||||
export default {
|
||||
getAllowUnauthorized,
|
||||
isValidTLSArray,
|
||||
isValidTLSItem,
|
||||
resolveTLSVersions,
|
||||
throwOnInvalidTLSArray,
|
||||
VALID_TLS_ERROR_MESSAGE_TYPES,
|
||||
DEFAULT_MIN_VERSION,
|
||||
DEFAULT_MAX_VERSION,
|
||||
validateTLSOptions,
|
||||
|
||||
TLS_VERSION_REVERSE_MAP,
|
||||
TLS_VERSION_MAP,
|
||||
};
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
const { Duplex } = require("node:stream");
|
||||
const EventEmitter = require("node:events");
|
||||
const { getAllowUnauthorized } = require("internal/tls");
|
||||
const [addServerName, upgradeDuplexToTLS, isNamedPipeSocket, getBufferedAmount] = $zig(
|
||||
"socket.zig",
|
||||
"createNodeTLSBinding",
|
||||
@@ -100,6 +101,7 @@ const kclosed = Symbol("closed");
|
||||
const kended = Symbol("ended");
|
||||
const kwriteCallback = Symbol("writeCallback");
|
||||
const kSocketClass = Symbol("kSocketClass");
|
||||
const kSocketOptions = Symbol("kSocketOptions");
|
||||
|
||||
function endNT(socket, callback, err) {
|
||||
socket.$end();
|
||||
@@ -272,15 +274,21 @@ const SocketHandlers: SocketHandler = {
|
||||
self.emit("secure", self);
|
||||
self.alpnProtocol = socket.alpnProtocol;
|
||||
const { checkServerIdentity } = self[bunTLSConnectOptions];
|
||||
|
||||
if (!verifyError && typeof checkServerIdentity === "function" && self.servername) {
|
||||
const cert = self.getPeerCertificate(true);
|
||||
verifyError = checkServerIdentity(self.servername, cert);
|
||||
}
|
||||
|
||||
if (self._requestCert || self._rejectUnauthorized) {
|
||||
if (verifyError) {
|
||||
self.authorized = false;
|
||||
self.authorizationError = verifyError.code || verifyError.message;
|
||||
if (self._rejectUnauthorized) {
|
||||
self.emit("error", verifyError);
|
||||
self.emit("secure", self);
|
||||
self.emit("_tlsError", verifyError);
|
||||
self.server.emit("tlsClientError", verifyError, self);
|
||||
self.destroy(verifyError);
|
||||
return;
|
||||
}
|
||||
@@ -290,7 +298,9 @@ const SocketHandlers: SocketHandler = {
|
||||
} else {
|
||||
self.authorized = true;
|
||||
}
|
||||
self.emit("secureConnect", verifyError);
|
||||
if (success) {
|
||||
self.emit("secureConnect", verifyError);
|
||||
}
|
||||
self.removeListener("end", onConnectEnd);
|
||||
},
|
||||
timeout(socket) {
|
||||
@@ -350,8 +360,18 @@ const ServerHandlers: SocketHandler = {
|
||||
const self = this.data;
|
||||
socket[kServerSocket] = self._handle;
|
||||
const options = self[bunSocketServerOptions];
|
||||
const { pauseOnConnect, connectionListener, [kSocketClass]: SClass, requestCert, rejectUnauthorized } = options;
|
||||
const _socket = new SClass({});
|
||||
|
||||
const {
|
||||
pauseOnConnect,
|
||||
connectionListener,
|
||||
[kSocketClass]: SClass,
|
||||
[kSocketOptions]: socketOptions = {},
|
||||
requestCert,
|
||||
rejectUnauthorized,
|
||||
} = options;
|
||||
|
||||
const _socket = new SClass(socketOptions);
|
||||
|
||||
_socket.isServer = true;
|
||||
_socket.server = self;
|
||||
_socket._requestCert = requestCert;
|
||||
@@ -394,48 +414,63 @@ const ServerHandlers: SocketHandler = {
|
||||
|
||||
handshake(socket, success, verifyError) {
|
||||
const { data: self } = socket;
|
||||
|
||||
if (!success && verifyError?.code === "ECONNRESET") {
|
||||
if (self._hadError) return;
|
||||
const err = new ConnResetException("socket hang up");
|
||||
self.emit("_tlsError", err);
|
||||
self.server.emit("tlsClientError", err, self);
|
||||
self._hadError = true;
|
||||
|
||||
// error before handshake on the server side will only be emitted using tlsClientError
|
||||
self.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
const err = verifyError || $ERR_SSL_UNSUPPORTED_PROTOCOL("TLS handshake failed");
|
||||
|
||||
self._hadError = true;
|
||||
self.emit("_tlsError", err);
|
||||
self.server.emit("tlsClientError", err, self);
|
||||
self.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
self._securePending = false;
|
||||
self.secureConnecting = false;
|
||||
self._secureEstablished = !!success;
|
||||
self.servername = socket.getServername();
|
||||
const server = self.server;
|
||||
self.alpnProtocol = socket.alpnProtocol;
|
||||
|
||||
if (self._requestCert || self._rejectUnauthorized) {
|
||||
if (verifyError) {
|
||||
self.authorized = false;
|
||||
self.authorizationError = verifyError.code || verifyError.message;
|
||||
server.emit("tlsClientError", verifyError, self);
|
||||
if (self._rejectUnauthorized) {
|
||||
// if we reject we still need to emit secure
|
||||
self.emit("secure", self);
|
||||
self.emit("_tlsError", verifyError);
|
||||
self.server.emit("tlsClientError", verifyError, self);
|
||||
self.destroy(verifyError);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
self.authorized = true;
|
||||
}
|
||||
} else {
|
||||
self.authorized = true;
|
||||
}
|
||||
|
||||
const connectionListener = server[bunSocketServerOptions]?.connectionListener;
|
||||
if (typeof connectionListener === "function") {
|
||||
connectionListener.$call(server, self);
|
||||
}
|
||||
server.emit("secureConnection", self);
|
||||
// after secureConnection event we emmit secure and secureConnect
|
||||
self.emit("secure", self);
|
||||
self.emit("secureConnect", verifyError);
|
||||
if (!server.pauseOnConnect) {
|
||||
self.resume();
|
||||
|
||||
if (success) {
|
||||
server.emit("secureConnection", self);
|
||||
self.emit("secure", self);
|
||||
self.emit("secureConnect", verifyError);
|
||||
|
||||
if (!server.pauseOnConnect) {
|
||||
self.resume();
|
||||
}
|
||||
}
|
||||
},
|
||||
error(socket, error) {
|
||||
@@ -735,7 +770,13 @@ Socket.prototype.connect = function connect(...args) {
|
||||
const bunTLS = this[bunTlsSymbol];
|
||||
var tls = undefined;
|
||||
if (typeof bunTLS === "function") {
|
||||
tls = bunTLS.$call(this, port, host, true);
|
||||
tls = bunTLS.$call(
|
||||
this,
|
||||
port,
|
||||
host,
|
||||
true, // isClient
|
||||
);
|
||||
|
||||
// Client always request Cert
|
||||
this._requestCert = true;
|
||||
if (tls) {
|
||||
@@ -743,7 +784,9 @@ Socket.prototype.connect = function connect(...args) {
|
||||
this._rejectUnauthorized = rejectUnauthorized;
|
||||
tls.rejectUnauthorized = rejectUnauthorized;
|
||||
} else {
|
||||
this._rejectUnauthorized = tls.rejectUnauthorized;
|
||||
const allowUnauth = getAllowUnauthorized();
|
||||
this._rejectUnauthorized = !allowUnauth;
|
||||
tls.rejectUnauthorized = !allowUnauth;
|
||||
}
|
||||
tls.requestCert = true;
|
||||
tls.session = session || tls.session;
|
||||
@@ -1451,7 +1494,15 @@ Server.prototype.listen = function listen(port, hostname, onListen) {
|
||||
if (typeof bunTLS === "function") {
|
||||
[tls, TLSSocketClass] = bunTLS.$call(this, port, hostname, false);
|
||||
options.servername = tls.serverName;
|
||||
options.minVersion = tls.minVersion;
|
||||
options.maxVersion = tls.maxVersion;
|
||||
|
||||
options[kSocketClass] = TLSSocketClass;
|
||||
options[kSocketOptions] = {
|
||||
minVersion: tls.minVersionName,
|
||||
maxVersion: tls.maxVersionName,
|
||||
};
|
||||
|
||||
contexts = tls.contexts;
|
||||
if (!tls.requestCert) {
|
||||
tls.rejectUnauthorized = false;
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
// Hardcoded module "node:tls"
|
||||
const { isArrayBufferView, isTypedArray } = require("node:util/types");
|
||||
const net = require("node:net");
|
||||
const { Duplex } = require("node:stream");
|
||||
const [addServerName] = $zig("socket.zig", "createNodeTLSBinding");
|
||||
const { throwNotImplemented } = require("internal/shared");
|
||||
const { throwOnInvalidTLSArray } = require("internal/tls");
|
||||
const {
|
||||
TLS_VERSION_REVERSE_MAP,
|
||||
resolveTLSVersions,
|
||||
DEFAULT_MIN_VERSION,
|
||||
DEFAULT_MAX_VERSION,
|
||||
validateTLSOptions,
|
||||
} = require("internal/tls");
|
||||
|
||||
const { Server: NetServer, Socket: NetSocket } = net;
|
||||
|
||||
@@ -202,62 +207,36 @@ var InternalSecureContext = class SecureContext {
|
||||
passphrase;
|
||||
servername;
|
||||
secureOptions;
|
||||
ciphers;
|
||||
|
||||
secureProtocol: string | undefined;
|
||||
minVersion: number | undefined;
|
||||
maxVersion: number | undefined;
|
||||
|
||||
constructor(options) {
|
||||
const context = {};
|
||||
|
||||
if (options) {
|
||||
validateTLSOptions(options);
|
||||
|
||||
let cert = options.cert;
|
||||
if (cert) {
|
||||
throwOnInvalidTLSArray("options.cert", cert);
|
||||
this.cert = cert;
|
||||
}
|
||||
if (cert) this.cert = cert;
|
||||
|
||||
let key = options.key;
|
||||
if (key) {
|
||||
throwOnInvalidTLSArray("options.key", key);
|
||||
this.key = key;
|
||||
}
|
||||
if (key) this.key = key;
|
||||
|
||||
let ca = options.ca;
|
||||
if (ca) {
|
||||
throwOnInvalidTLSArray("options.ca", ca);
|
||||
this.ca = ca;
|
||||
}
|
||||
const ca = options.ca;
|
||||
if (ca) this.ca = ca;
|
||||
|
||||
let passphrase = options.passphrase;
|
||||
if (passphrase && typeof passphrase !== "string") {
|
||||
throw new TypeError("passphrase argument must be an string");
|
||||
}
|
||||
this.passphrase = passphrase;
|
||||
this.ciphers = options.ciphers;
|
||||
this.passphrase = options.passphrase;
|
||||
this.servername = options.servername;
|
||||
this.secureOptions = options.secureOptions || 0;
|
||||
this.secureProtocol = options.secureProtocol;
|
||||
|
||||
let servername = options.servername;
|
||||
if (servername && typeof servername !== "string") {
|
||||
throw new TypeError("servername argument must be an string");
|
||||
}
|
||||
this.servername = servername;
|
||||
|
||||
let secureOptions = options.secureOptions || 0;
|
||||
if (secureOptions && typeof secureOptions !== "number") {
|
||||
throw new TypeError("secureOptions argument must be an number");
|
||||
}
|
||||
|
||||
this.secureOptions = secureOptions;
|
||||
|
||||
if (!$isUndefinedOrNull(options.privateKeyIdentifier)) {
|
||||
if ($isUndefinedOrNull(options.privateKeyEngine)) {
|
||||
// prettier-ignore
|
||||
throw $ERR_INVALID_ARG_VALUE("options.privateKeyEngine", options.privateKeyEngine);
|
||||
} else if (typeof options.privateKeyEngine !== "string") {
|
||||
// prettier-ignore
|
||||
throw $ERR_INVALID_ARG_TYPE("options.privateKeyEngine", ["string", "null", "undefined"], options.privateKeyEngine);
|
||||
}
|
||||
|
||||
if (typeof options.privateKeyIdentifier !== "string") {
|
||||
// prettier-ignore
|
||||
throw $ERR_INVALID_ARG_TYPE("options.privateKeyIdentifier", ["string", "null", "undefined"], options.privateKeyIdentifier);
|
||||
}
|
||||
}
|
||||
const [minVersion, maxVersion] = resolveTLSVersions(options);
|
||||
this.minVersion = minVersion;
|
||||
this.maxVersion = maxVersion;
|
||||
}
|
||||
|
||||
this.context = context;
|
||||
@@ -304,6 +283,8 @@ function TLSSocket(socket?, options?) {
|
||||
this.authorizationError;
|
||||
this[krenegotiationDisabled] = undefined;
|
||||
this.encrypted = true;
|
||||
this.ciphers = undefined;
|
||||
this.ecdhCurve = undefined;
|
||||
|
||||
const isNetSocketOrDuplex = socket instanceof Duplex;
|
||||
|
||||
@@ -313,6 +294,12 @@ function TLSSocket(socket?, options?) {
|
||||
|
||||
if (typeof options === "object") {
|
||||
const { ALPNProtocols } = options;
|
||||
|
||||
// use `in` check because passing undefined should throw according to node tests
|
||||
if ("checkServerIdentity" in options && typeof options.checkServerIdentity !== "function") {
|
||||
throw $ERR_INVALID_ARG_TYPE("options.checkServerIdentity", "function", options.checkServerIdentity);
|
||||
}
|
||||
|
||||
if (ALPNProtocols) {
|
||||
convertALPNProtocols(ALPNProtocols, this);
|
||||
}
|
||||
@@ -481,6 +468,9 @@ TLSSocket.prototype[buntls] = function (port, host) {
|
||||
session: this[ksession],
|
||||
rejectUnauthorized: this._rejectUnauthorized,
|
||||
requestCert: this._requestCert,
|
||||
minVersionName: TLS_VERSION_REVERSE_MAP[this[ksecureContext].minVersion],
|
||||
maxVersionName: TLS_VERSION_REVERSE_MAP[this[ksecureContext].maxVersion],
|
||||
secureProtocol: this[ksecureContext].secureProtocol,
|
||||
...this[ksecureContext],
|
||||
};
|
||||
};
|
||||
@@ -505,6 +495,10 @@ function Server(options, secureConnectionListener): void {
|
||||
this.servername = undefined;
|
||||
this.ALPNProtocols = undefined;
|
||||
|
||||
const [minVersion, maxVersion] = resolveTLSVersions(options || {});
|
||||
this.minVersion = minVersion;
|
||||
this.maxVersion = maxVersion;
|
||||
|
||||
let contexts: Map<string, typeof InternalSecureContext> | null = null;
|
||||
|
||||
this.addContext = function (hostname, context) {
|
||||
@@ -527,6 +521,7 @@ function Server(options, secureConnectionListener): void {
|
||||
options = options.context;
|
||||
}
|
||||
if (options) {
|
||||
validateTLSOptions(options);
|
||||
const { ALPNProtocols } = options;
|
||||
|
||||
if (ALPNProtocols) {
|
||||
@@ -534,48 +529,26 @@ function Server(options, secureConnectionListener): void {
|
||||
}
|
||||
|
||||
let cert = options.cert;
|
||||
if (cert) {
|
||||
throwOnInvalidTLSArray("options.cert", cert);
|
||||
this.cert = cert;
|
||||
}
|
||||
if (cert) this.cert = cert;
|
||||
|
||||
let key = options.key;
|
||||
if (key) {
|
||||
throwOnInvalidTLSArray("options.key", key);
|
||||
this.key = key;
|
||||
}
|
||||
if (key) this.key = key;
|
||||
|
||||
let ca = options.ca;
|
||||
if (ca) {
|
||||
throwOnInvalidTLSArray("options.ca", ca);
|
||||
this.ca = ca;
|
||||
}
|
||||
if (ca) this.ca = ca;
|
||||
|
||||
let passphrase = options.passphrase;
|
||||
if (passphrase && typeof passphrase !== "string") {
|
||||
throw $ERR_INVALID_ARG_TYPE("options.passphrase", "string", passphrase);
|
||||
}
|
||||
this.passphrase = passphrase;
|
||||
|
||||
let servername = options.servername;
|
||||
if (servername && typeof servername !== "string") {
|
||||
throw $ERR_INVALID_ARG_TYPE("options.servername", "string", servername);
|
||||
}
|
||||
this.servername = servername;
|
||||
|
||||
let secureOptions = options.secureOptions || 0;
|
||||
if (secureOptions && typeof secureOptions !== "number") {
|
||||
throw $ERR_INVALID_ARG_TYPE("options.secureOptions", "number", secureOptions);
|
||||
}
|
||||
this.secureOptions = secureOptions;
|
||||
this.ciphers = options.ciphers;
|
||||
this.ecdhCurve = options.ecdhCurve;
|
||||
this.passphrase = options.passphrase;
|
||||
this.servername = options.servername;
|
||||
this.secureOptions = options.secureOptions || 0;
|
||||
this.secureProtocol = options.secureProtocol;
|
||||
|
||||
const requestCert = options.requestCert || false;
|
||||
|
||||
if (requestCert) this._requestCert = requestCert;
|
||||
else this._requestCert = undefined;
|
||||
|
||||
const rejectUnauthorized = options.rejectUnauthorized;
|
||||
|
||||
if (typeof rejectUnauthorized !== "undefined") {
|
||||
this._rejectUnauthorized = rejectUnauthorized;
|
||||
} else this._rejectUnauthorized = rejectUnauthorizedDefault;
|
||||
@@ -586,7 +559,11 @@ function Server(options, secureConnectionListener): void {
|
||||
throw Error("Not implented in Bun yet");
|
||||
};
|
||||
|
||||
Server.prototype.setTicketKeys = function () {
|
||||
Server.prototype.setTicketKeys = function (ticketKeys) {
|
||||
if (!Buffer.isBuffer(ticketKeys) || ticketKeys.length !== 48) {
|
||||
throw $ERR_INVALID_ARG_TYPE("Session ticket keys must be a 48-byte buffer");
|
||||
}
|
||||
|
||||
throw Error("Not implented in Bun yet");
|
||||
};
|
||||
|
||||
@@ -598,6 +575,11 @@ function Server(options, secureConnectionListener): void {
|
||||
cert: this.cert,
|
||||
ca: this.ca,
|
||||
passphrase: this.passphrase,
|
||||
minVersion: this.minVersion,
|
||||
maxVersion: this.maxVersion,
|
||||
secureProtocol: this.secureProtocol,
|
||||
minVersionName: TLS_VERSION_REVERSE_MAP[this.minVersion],
|
||||
maxVersionName: TLS_VERSION_REVERSE_MAP[this.maxVersion],
|
||||
secureOptions: this.secureOptions,
|
||||
rejectUnauthorized: this._rejectUnauthorized,
|
||||
requestCert: isClient ? true : this._requestCert,
|
||||
@@ -620,12 +602,11 @@ function createServer(options, connectionListener) {
|
||||
const DEFAULT_ECDH_CURVE = "auto",
|
||||
// https://github.com/Jarred-Sumner/uSockets/blob/fafc241e8664243fc0c51d69684d5d02b9805134/src/crypto/openssl.c#L519-L523
|
||||
DEFAULT_CIPHERS =
|
||||
"DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256",
|
||||
DEFAULT_MIN_VERSION = "TLSv1.2",
|
||||
DEFAULT_MAX_VERSION = "TLSv1.3";
|
||||
"DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256";
|
||||
|
||||
function normalizeConnectArgs(listArgs) {
|
||||
const args = net._normalizeArgs(listArgs);
|
||||
// Cast to any to use internal _normalizeArgs helper as in Node.js implementation
|
||||
const args = (net as any)._normalizeArgs(listArgs);
|
||||
$assert($isObject(args[0]));
|
||||
|
||||
// If args[0] was options, then normalize dealt with it.
|
||||
@@ -648,10 +629,13 @@ function normalizeConnectArgs(listArgs) {
|
||||
function connect(...args) {
|
||||
let normal = normalizeConnectArgs(args);
|
||||
const options = normal[0];
|
||||
|
||||
const { ALPNProtocols } = options;
|
||||
|
||||
if (ALPNProtocols) {
|
||||
convertALPNProtocols(ALPNProtocols, options);
|
||||
}
|
||||
|
||||
return new TLSSocket(options).connect(normal);
|
||||
}
|
||||
|
||||
@@ -669,9 +653,11 @@ function convertProtocols(protocols) {
|
||||
(p, c, i) => {
|
||||
const len = Buffer.byteLength(c);
|
||||
if (len > 255) {
|
||||
throw new RangeError(
|
||||
const err = new RangeError(
|
||||
`The byte length of the protocol at index ${i} exceeds the maximum length. It must be <= 255. Received ${len}`,
|
||||
);
|
||||
err.code = "ERR_OUT_OF_RANGE";
|
||||
throw err;
|
||||
}
|
||||
lens[i] = len;
|
||||
return p + 1 + len;
|
||||
@@ -696,7 +682,9 @@ function convertALPNProtocols(protocols, out) {
|
||||
out.ALPNProtocols = convertProtocols(protocols);
|
||||
} else if (isTypedArray(protocols)) {
|
||||
// Copy new buffer not to be modified by user.
|
||||
out.ALPNProtocols = Buffer.from(protocols);
|
||||
out.ALPNProtocols = Buffer.from(
|
||||
protocols.buffer.slice(protocols.byteOffset, protocols.byteOffset + protocols.byteLength),
|
||||
);
|
||||
} else if (isArrayBufferView(protocols)) {
|
||||
out.ALPNProtocols = Buffer.from(
|
||||
protocols.buffer.slice(protocols.byteOffset, protocols.byteOffset + protocols.byteLength),
|
||||
|
||||
12
src/tls.zig
Normal file
12
src/tls.zig
Normal file
@@ -0,0 +1,12 @@
|
||||
const bun = @import("bun");
|
||||
|
||||
// These variables are set by the CLI parser.
|
||||
|
||||
// These get read by node_tls_binding.zig for `tls.ts` to consume and use
|
||||
// as the values for `tls.DEFAULT_MIN_VERSION` and `tls.DEFAULT_MAX_VERSION`
|
||||
|
||||
// A null value means no CLI flag was provided, and the *default* defaults
|
||||
// are defined in src/js/node/tls.ts
|
||||
|
||||
pub var min_tls_version_from_cli_flag: ?u16 = null;
|
||||
pub var max_tls_version_from_cli_flag: ?u16 = null;
|
||||
@@ -7,9 +7,10 @@
|
||||
|
||||
import { gc as bunGC, sleepSync, spawnSync, unsafe, which, write } from "bun";
|
||||
import { heapStats } from "bun:jsc";
|
||||
import { fork, ChildProcess } from "child_process";
|
||||
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
||||
import { readFile, readlink, writeFile, readdir, rm } from "fs/promises";
|
||||
import { ChildProcess, fork } from "child_process";
|
||||
import detectLibc from "detect-libc";
|
||||
import { readdir, readFile, readlink, rm, writeFile } from "fs/promises";
|
||||
import fs, { closeSync, openSync, rmSync } from "node:fs";
|
||||
import os from "node:os";
|
||||
import { dirname, isAbsolute, join } from "path";
|
||||
|
||||
@@ -33,9 +33,7 @@ declare global {
|
||||
}
|
||||
}
|
||||
expectType(Bun.env.BAZ).is<"BAZ">();
|
||||
expectType(process.env.BAZ).is<"BAZ">();
|
||||
expectType(import.meta.env.BAZ).is<"BAZ">();
|
||||
expectType(node_env.BAZ).is<"BAZ">();
|
||||
expectType(bun_env.BAZ).is<"BAZ">();
|
||||
|
||||
expectType(Bun.env.OTHER).is<string | undefined>();
|
||||
@@ -44,16 +42,14 @@ expectType(import.meta.env.OTHER).is<string | undefined>();
|
||||
expectType(node_env.OTHER).is<string | undefined>();
|
||||
expectType(bun_env.OTHER).is<string | undefined>();
|
||||
|
||||
function isAllSame<T>(a: T, b: T, c: T, d: T, e: T) {
|
||||
return a === b && b === c && c === d && d === e;
|
||||
}
|
||||
declare function isAllSame<T>(...args: T[]): void;
|
||||
|
||||
//prettier-ignore
|
||||
{
|
||||
|
||||
isAllSame <"FOO"> (process.env.FOO, Bun.env.FOO, import.meta.env.FOO, node_env.FOO, bun_env.FOO);
|
||||
isAllSame <"BAR"> (process.env.BAR, Bun.env.BAR, import.meta.env.BAR, node_env.BAR, bun_env.BAR);
|
||||
isAllSame <"BAZ"> (process.env.BAZ, Bun.env.BAZ, import.meta.env.BAZ, node_env.BAZ, bun_env.BAZ);
|
||||
isAllSame <"BAZ"> ( Bun.env.BAZ, import.meta.env.BAZ, bun_env.BAZ); // process.env doesn't extend import.meta.env
|
||||
isAllSame <string | undefined> (process.env.OTHER, Bun.env.OTHER, import.meta.env.OTHER, node_env.OTHER, bun_env.OTHER);
|
||||
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ const words: Record<string, { reason: string; limit?: number; regex?: boolean }>
|
||||
|
||||
[String.raw`: [a-zA-Z0-9_\.\*\?\[\]\(\)]+ = undefined,`]: { reason: "Do not default a struct field to undefined", limit: 241, regex: true },
|
||||
"usingnamespace": { reason: "Zig 0.15 will remove `usingnamespace`" },
|
||||
"catch unreachable": { reason: "For out-of-memory, prefer 'catch bun.outOfMemory()'", limit: 1849 },
|
||||
"catch unreachable": { reason: "For out-of-memory, prefer 'catch bun.outOfMemory()'", limit: 1856 },
|
||||
|
||||
"std.fs.Dir": { reason: "Prefer bun.sys + bun.FD instead of std.fs", limit: 180 },
|
||||
"std.fs.cwd": { reason: "Prefer bun.FD.cwd()", limit: 103 },
|
||||
|
||||
2
test/js/node/test/fixtures/tls-connect.js
vendored
2
test/js/node/test/fixtures/tls-connect.js
vendored
@@ -105,4 +105,4 @@ exports.connect = function connect(options, callback) {
|
||||
if (client.conn)
|
||||
client.conn.end();
|
||||
}
|
||||
};
|
||||
};
|
||||
137
test/js/node/test/parallel/test-tls-basic-validations.js
Normal file
137
test/js/node/test/parallel/test-tls-basic-validations.js
Normal file
@@ -0,0 +1,137 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto)
|
||||
common.skip('missing crypto');
|
||||
|
||||
const assert = require('assert');
|
||||
const tls = require('tls');
|
||||
|
||||
assert.throws(
|
||||
() => tls.createSecureContext({ ciphers: 1 }),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: 'The "options.ciphers" property must be of type string.' +
|
||||
' Received type number (1)'
|
||||
});
|
||||
|
||||
assert.throws(
|
||||
() => tls.createServer({ ciphers: 1 }),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: 'The "options.ciphers" property must be of type string.' +
|
||||
' Received type number (1)'
|
||||
});
|
||||
|
||||
assert.throws(
|
||||
() => tls.createSecureContext({ key: 'dummykey', passphrase: 1 }),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: /The "options\.passphrase" property must be of type string/
|
||||
});
|
||||
|
||||
assert.throws(
|
||||
() => tls.createServer({ key: 'dummykey', passphrase: 1 }),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: /The "options\.passphrase" property must be of type string/
|
||||
});
|
||||
|
||||
assert.throws(
|
||||
() => tls.createServer({ ecdhCurve: 1 }),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: /The "options\.ecdhCurve" property must be of type string/
|
||||
});
|
||||
|
||||
assert.throws(
|
||||
() => tls.createServer({ handshakeTimeout: 'abcd' }),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: 'The "options.handshakeTimeout" property must be of type number.' +
|
||||
" Received type string ('abcd')"
|
||||
}
|
||||
);
|
||||
|
||||
assert.throws(
|
||||
() => tls.createServer({ sessionTimeout: 'abcd' }),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: /The "options\.sessionTimeout" property must be of type number/
|
||||
});
|
||||
|
||||
assert.throws(
|
||||
() => tls.createServer({ ticketKeys: 'abcd' }),
|
||||
{
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: /The "options\.ticketKeys" property must be an instance of/
|
||||
});
|
||||
|
||||
assert.throws(() => tls.createServer({ ticketKeys: Buffer.alloc(0) }), {
|
||||
code: 'ERR_INVALID_ARG_VALUE',
|
||||
message: /The property 'options\.ticketKeys' must be exactly 48 bytes/
|
||||
});
|
||||
|
||||
{
|
||||
const buffer = Buffer.from('abcd');
|
||||
const out = {};
|
||||
tls.convertALPNProtocols(buffer, out);
|
||||
out.ALPNProtocols.write('efgh');
|
||||
assert(buffer.equals(Buffer.from('abcd')));
|
||||
assert(out.ALPNProtocols.equals(Buffer.from('efgh')));
|
||||
}
|
||||
|
||||
{
|
||||
const arrayBufferViewStr = 'abcd';
|
||||
const inputBuffer = Buffer.from(arrayBufferViewStr.repeat(8), 'utf8');
|
||||
for (const expectView of common.getArrayBufferViews(inputBuffer)) {
|
||||
const out = {};
|
||||
const expected = Buffer.from(expectView.buffer.slice(),
|
||||
expectView.byteOffset,
|
||||
expectView.byteLength);
|
||||
tls.convertALPNProtocols(expectView, out);
|
||||
assert(out.ALPNProtocols.equals(expected));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const protocols = [(new String('a')).repeat(500)];
|
||||
const out = {};
|
||||
assert.throws(
|
||||
() => tls.convertALPNProtocols(protocols, out),
|
||||
{
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
message: 'The byte length of the protocol at index 0 exceeds the ' +
|
||||
'maximum length. It must be <= 255. Received 500'
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
assert.throws(() => { tls.createSecureContext({ minVersion: 'fhqwhgads' }); },
|
||||
{
|
||||
code: 'ERR_TLS_INVALID_PROTOCOL_VERSION',
|
||||
name: 'TypeError'
|
||||
});
|
||||
|
||||
assert.throws(() => { tls.createSecureContext({ maxVersion: 'fhqwhgads' }); },
|
||||
{
|
||||
code: 'ERR_TLS_INVALID_PROTOCOL_VERSION',
|
||||
name: 'TypeError'
|
||||
});
|
||||
|
||||
for (const checkServerIdentity of [undefined, null, 1, true]) {
|
||||
assert.throws(() => {
|
||||
tls.connect({ checkServerIdentity });
|
||||
}, {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
});
|
||||
}
|
||||
15
test/js/node/test/parallel/test-tls-cli-max-version-1.2.js
Normal file
15
test/js/node/test/parallel/test-tls-cli-max-version-1.2.js
Normal file
@@ -0,0 +1,15 @@
|
||||
// Flags: --tls-max-v1.2
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto) common.skip('missing crypto');
|
||||
|
||||
// Check that node `--tls-max-v1.2` is supported.
|
||||
|
||||
const assert = require('assert');
|
||||
const tls = require('tls');
|
||||
|
||||
assert.strictEqual(tls.DEFAULT_MAX_VERSION, 'TLSv1.2');
|
||||
assert.strictEqual(tls.DEFAULT_MIN_VERSION, 'TLSv1.2');
|
||||
|
||||
// Check the min-max version protocol versions against these CLI settings.
|
||||
require('./test-tls-min-max-version.js');
|
||||
15
test/js/node/test/parallel/test-tls-cli-max-version-1.3.js
Normal file
15
test/js/node/test/parallel/test-tls-cli-max-version-1.3.js
Normal file
@@ -0,0 +1,15 @@
|
||||
// Flags: --tls-max-v1.3
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto) common.skip('missing crypto');
|
||||
|
||||
// Check that node `--tls-max-v1.3` is supported.
|
||||
|
||||
const assert = require('assert');
|
||||
const tls = require('tls');
|
||||
|
||||
assert.strictEqual(tls.DEFAULT_MAX_VERSION, 'TLSv1.3');
|
||||
assert.strictEqual(tls.DEFAULT_MIN_VERSION, 'TLSv1.2');
|
||||
|
||||
// Check the min-max version protocol versions against these CLI settings.
|
||||
require('./test-tls-min-max-version.js');
|
||||
14
test/js/node/test/parallel/test-tls-cli-min-max-conflict.js
Normal file
14
test/js/node/test/parallel/test-tls-cli-min-max-conflict.js
Normal file
@@ -0,0 +1,14 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto) common.skip('missing crypto');
|
||||
|
||||
// Check that conflicting TLS protocol versions are not allowed
|
||||
|
||||
const assert = require('assert');
|
||||
const child_process = require('child_process');
|
||||
|
||||
const args = ['--tls-min-v1.3', '--tls-max-v1.2', '-p', 'process.version'];
|
||||
child_process.execFile(process.argv[0], args, (err) => {
|
||||
assert(err);
|
||||
assert.match(err.message, /not both/);
|
||||
});
|
||||
15
test/js/node/test/parallel/test-tls-cli-min-version-1.0.js
Normal file
15
test/js/node/test/parallel/test-tls-cli-min-version-1.0.js
Normal file
@@ -0,0 +1,15 @@
|
||||
// Flags: --tls-min-v1.0 --tls-min-v1.1
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto) common.skip('missing crypto');
|
||||
|
||||
// Check that `node --tls-v1.0` is supported, and overrides --tls-v1.1.
|
||||
|
||||
const assert = require('assert');
|
||||
const tls = require('tls');
|
||||
|
||||
assert.strictEqual(tls.DEFAULT_MAX_VERSION, 'TLSv1.3');
|
||||
assert.strictEqual(tls.DEFAULT_MIN_VERSION, 'TLSv1');
|
||||
|
||||
// Check the min-max version protocol versions against these CLI settings.
|
||||
require('./test-tls-min-max-version.js');
|
||||
15
test/js/node/test/parallel/test-tls-cli-min-version-1.1.js
Normal file
15
test/js/node/test/parallel/test-tls-cli-min-version-1.1.js
Normal file
@@ -0,0 +1,15 @@
|
||||
// Flags: --tls-min-v1.1
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto) common.skip('missing crypto');
|
||||
|
||||
// Check that node `--tls-v1.1` is supported.
|
||||
|
||||
const assert = require('assert');
|
||||
const tls = require('tls');
|
||||
|
||||
assert.strictEqual(tls.DEFAULT_MAX_VERSION, 'TLSv1.3');
|
||||
assert.strictEqual(tls.DEFAULT_MIN_VERSION, 'TLSv1.1');
|
||||
|
||||
// Check the min-max version protocol versions against these CLI settings.
|
||||
require('./test-tls-min-max-version.js');
|
||||
15
test/js/node/test/parallel/test-tls-cli-min-version-1.2.js
Normal file
15
test/js/node/test/parallel/test-tls-cli-min-version-1.2.js
Normal file
@@ -0,0 +1,15 @@
|
||||
// Flags: --tls-min-v1.2
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto) common.skip('missing crypto');
|
||||
|
||||
// Check that node `--tls-min-v1.2` is supported.
|
||||
|
||||
const assert = require('assert');
|
||||
const tls = require('tls');
|
||||
|
||||
assert.strictEqual(tls.DEFAULT_MAX_VERSION, 'TLSv1.3');
|
||||
assert.strictEqual(tls.DEFAULT_MIN_VERSION, 'TLSv1.2');
|
||||
|
||||
// Check the min-max version protocol versions against these CLI settings.
|
||||
require('./test-tls-min-max-version.js');
|
||||
15
test/js/node/test/parallel/test-tls-cli-min-version-1.3.js
Normal file
15
test/js/node/test/parallel/test-tls-cli-min-version-1.3.js
Normal file
@@ -0,0 +1,15 @@
|
||||
// Flags: --tls-min-v1.3
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto) common.skip('missing crypto');
|
||||
|
||||
// Check that node `--tls-min-v1.3` is supported.
|
||||
|
||||
const assert = require('assert');
|
||||
const tls = require('tls');
|
||||
|
||||
assert.strictEqual(tls.DEFAULT_MAX_VERSION, 'TLSv1.3');
|
||||
assert.strictEqual(tls.DEFAULT_MIN_VERSION, 'TLSv1.3');
|
||||
|
||||
// Check the min-max version protocol versions against these CLI settings.
|
||||
require('./test-tls-min-max-version.js');
|
||||
13
test/js/node/test/parallel/test-tls-client-reject-12.js
Normal file
13
test/js/node/test/parallel/test-tls-client-reject-12.js
Normal file
@@ -0,0 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
// test-tls-client-reject specifically for TLS1.2.
|
||||
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto)
|
||||
common.skip('missing crypto');
|
||||
|
||||
const tls = require('tls');
|
||||
|
||||
tls.DEFAULT_MAX_VERSION = 'TLSv1.2';
|
||||
|
||||
require('./test-tls-client-reject.js');
|
||||
107
test/js/node/test/parallel/test-tls-client-reject.js
Normal file
107
test/js/node/test/parallel/test-tls-client-reject.js
Normal file
@@ -0,0 +1,107 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto)
|
||||
common.skip('missing crypto');
|
||||
|
||||
const assert = require('assert');
|
||||
const tls = require('tls');
|
||||
const fixtures = require('../common/fixtures');
|
||||
|
||||
const options = {
|
||||
key: fixtures.readKey('rsa_private.pem'),
|
||||
cert: fixtures.readKey('rsa_cert.crt')
|
||||
};
|
||||
|
||||
const server = tls.createServer(options, function(socket) {
|
||||
socket.pipe(socket);
|
||||
// Pipe already ends... but leaving this here tests .end() after .end().
|
||||
socket.on('end', () => socket.end());
|
||||
}).listen(0, common.mustCall(function() {
|
||||
unauthorized();
|
||||
}));
|
||||
|
||||
function unauthorized() {
|
||||
console.log('connect unauthorized');
|
||||
const socket = tls.connect({
|
||||
port: server.address().port,
|
||||
servername: 'localhost',
|
||||
rejectUnauthorized: false
|
||||
}, common.mustCall(function() {
|
||||
let _data;
|
||||
assert(!socket.authorized);
|
||||
socket.on('data', common.mustCall((data) => {
|
||||
assert.strictEqual(data.toString(), 'ok');
|
||||
_data = data;
|
||||
}));
|
||||
socket.on('end', common.mustCall(() => {
|
||||
assert(_data, 'data failed to echo!');
|
||||
}));
|
||||
socket.on('end', () => rejectUnauthorized());
|
||||
}));
|
||||
socket.once('session', common.mustCall());
|
||||
socket.on('error', common.mustNotCall());
|
||||
socket.end('ok');
|
||||
}
|
||||
|
||||
function rejectUnauthorized() {
|
||||
console.log('reject unauthorized');
|
||||
const socket = tls.connect(server.address().port, {
|
||||
servername: 'localhost'
|
||||
}, common.mustNotCall());
|
||||
socket.on('data', common.mustNotCall());
|
||||
socket.on('error', common.mustCall(function(err) {
|
||||
rejectUnauthorizedUndefined();
|
||||
}));
|
||||
socket.end('ng');
|
||||
}
|
||||
|
||||
function rejectUnauthorizedUndefined() {
|
||||
console.log('reject unauthorized undefined');
|
||||
const socket = tls.connect(server.address().port, {
|
||||
servername: 'localhost',
|
||||
rejectUnauthorized: undefined
|
||||
}, common.mustNotCall());
|
||||
socket.on('data', common.mustNotCall());
|
||||
socket.on('error', common.mustCall(function(err) {
|
||||
authorized();
|
||||
}));
|
||||
socket.end('ng');
|
||||
}
|
||||
|
||||
function authorized() {
|
||||
console.log('connect authorized');
|
||||
const socket = tls.connect(server.address().port, {
|
||||
ca: [fixtures.readKey('rsa_cert.crt')],
|
||||
servername: 'localhost'
|
||||
}, common.mustCall(function() {
|
||||
console.log('... authorized');
|
||||
assert(socket.authorized);
|
||||
socket.on('data', common.mustCall((data) => {
|
||||
assert.strictEqual(data.toString(), 'ok');
|
||||
}));
|
||||
socket.on('end', () => server.close());
|
||||
}));
|
||||
socket.on('error', common.mustNotCall());
|
||||
socket.end('ok');
|
||||
}
|
||||
283
test/js/node/test/parallel/test-tls-min-max-version.js
Normal file
283
test/js/node/test/parallel/test-tls-min-max-version.js
Normal file
@@ -0,0 +1,283 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
|
||||
if (!common.hasCrypto) {
|
||||
common.skip('missing crypto');
|
||||
}
|
||||
const {
|
||||
hasOpenSSL,
|
||||
hasOpenSSL3,
|
||||
} = require('../common/crypto');
|
||||
const fixtures = require('../common/fixtures');
|
||||
const { inspect } = require('util');
|
||||
|
||||
// Check min/max protocol versions.
|
||||
|
||||
const {
|
||||
assert, connect, keys, tls
|
||||
} = require(fixtures.path('tls-connect'));
|
||||
const DEFAULT_MIN_VERSION = tls.DEFAULT_MIN_VERSION;
|
||||
const DEFAULT_MAX_VERSION = tls.DEFAULT_MAX_VERSION;
|
||||
|
||||
|
||||
function test(cmin, cmax, cprot, smin, smax, sprot, proto, cerr, serr) {
|
||||
assert(proto || cerr || serr, 'test missing any expectations');
|
||||
|
||||
let ciphers;
|
||||
if (hasOpenSSL3 && (proto === 'TLSv1' || proto === 'TLSv1.1' ||
|
||||
proto === 'TLSv1_1_method' || proto === 'TLSv1_method' ||
|
||||
sprot === 'TLSv1_1_method' || sprot === 'TLSv1_method')) {
|
||||
if (serr !== 'ERR_SSL_UNSUPPORTED_PROTOCOL')
|
||||
ciphers = 'ALL@SECLEVEL=0';
|
||||
}
|
||||
if (hasOpenSSL(3, 1) && cerr === 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION') {
|
||||
ciphers = 'DEFAULT@SECLEVEL=0';
|
||||
}
|
||||
// Report where test was called from. Strip leading garbage from
|
||||
// at Object.<anonymous> (file:line)
|
||||
// from the stack location, we only want the file:line part.
|
||||
const where = inspect(new Error()).split('\n')[2].replace(/[^(]*/, '');
|
||||
connect({
|
||||
client: {
|
||||
checkServerIdentity: (servername, cert) => { },
|
||||
ca: `${keys.agent1.cert}\n${keys.agent6.ca}`,
|
||||
minVersion: cmin,
|
||||
maxVersion: cmax,
|
||||
secureProtocol: cprot,
|
||||
ciphers: ciphers,
|
||||
host: '127.0.0.1', // Required because Bun implements happy eyeballs
|
||||
},
|
||||
server: {
|
||||
cert: keys.agent6.cert,
|
||||
key: keys.agent6.key,
|
||||
minVersion: smin,
|
||||
maxVersion: smax,
|
||||
secureProtocol: sprot,
|
||||
ciphers: ciphers,
|
||||
host: '127.0.0.1', // Required because Bun implements happy eyeballs
|
||||
},
|
||||
}, common.mustCall((err, pair, cleanup) => {
|
||||
function u(_) { return _ === undefined ? 'U' : _; }
|
||||
console.log('test:', u(cmin), u(cmax), u(cprot), u(smin), u(smax), u(sprot),
|
||||
u(ciphers), 'expect', u(proto), u(cerr), u(serr));
|
||||
console.log(' ', where);
|
||||
if (!proto) {
|
||||
console.log('client', pair.client.err ? pair.client.err.code : undefined);
|
||||
console.log('server', pair.server.err ? pair.server.err.code : undefined);
|
||||
if (cerr) {
|
||||
assert(pair.client.err);
|
||||
// Accept these codes as aliases, the one reported depends on the
|
||||
// OpenSSL version.
|
||||
if (cerr === 'ERR_SSL_UNSUPPORTED_PROTOCOL' &&
|
||||
pair.client.err.code === 'ERR_SSL_VERSION_TOO_LOW')
|
||||
cerr = 'ERR_SSL_VERSION_TOO_LOW';
|
||||
assert.strictEqual(pair.client.err.code, cerr);
|
||||
}
|
||||
if (serr) {
|
||||
assert(pair.server.err);
|
||||
assert.strictEqual(pair.server.err.code, serr);
|
||||
}
|
||||
return cleanup();
|
||||
}
|
||||
|
||||
assert.ifError(err);
|
||||
assert.ifError(pair.server.err);
|
||||
assert.ifError(pair.client.err);
|
||||
assert(pair.server.conn);
|
||||
assert(pair.client.conn);
|
||||
assert.strictEqual(pair.client.conn.getProtocol(), proto);
|
||||
assert.strictEqual(pair.server.conn.getProtocol(), proto);
|
||||
return cleanup();
|
||||
}));
|
||||
}
|
||||
|
||||
const U = undefined;
|
||||
|
||||
// Default protocol is the max version.
|
||||
test(U, U, U, U, U, U, DEFAULT_MAX_VERSION);
|
||||
|
||||
// Insecure or invalid protocols cannot be enabled.
|
||||
test(U, U, U, U, U, 'SSLv2_method',
|
||||
U, U, 'ERR_TLS_INVALID_PROTOCOL_METHOD');
|
||||
test(U, U, U, U, U, 'SSLv3_method',
|
||||
U, U, 'ERR_TLS_INVALID_PROTOCOL_METHOD');
|
||||
test(U, U, 'SSLv2_method', U, U, U,
|
||||
U, 'ERR_TLS_INVALID_PROTOCOL_METHOD');
|
||||
test(U, U, 'SSLv3_method', U, U, U,
|
||||
U, 'ERR_TLS_INVALID_PROTOCOL_METHOD');
|
||||
test(U, U, 'hokey-pokey', U, U, U,
|
||||
U, 'ERR_TLS_INVALID_PROTOCOL_METHOD');
|
||||
test(U, U, U, U, U, 'hokey-pokey',
|
||||
U, U, 'ERR_TLS_INVALID_PROTOCOL_METHOD');
|
||||
|
||||
// Regression test: this should not crash because node should not pass the error
|
||||
// message (including unsanitized user input) to a printf-like function.
|
||||
test(U, U, U, U, U, '%s_method',
|
||||
U, U, 'ERR_TLS_INVALID_PROTOCOL_METHOD');
|
||||
|
||||
// Cannot use secureProtocol and min/max versions simultaneously.
|
||||
test(U, U, U, U, 'TLSv1.2', 'TLS1_2_method',
|
||||
U, U, 'ERR_TLS_PROTOCOL_VERSION_CONFLICT');
|
||||
test(U, U, U, 'TLSv1.2', U, 'TLS1_2_method',
|
||||
U, U, 'ERR_TLS_PROTOCOL_VERSION_CONFLICT');
|
||||
test(U, 'TLSv1.2', 'TLS1_2_method', U, U, U,
|
||||
U, 'ERR_TLS_PROTOCOL_VERSION_CONFLICT');
|
||||
test('TLSv1.2', U, 'TLS1_2_method', U, U, U,
|
||||
U, 'ERR_TLS_PROTOCOL_VERSION_CONFLICT');
|
||||
|
||||
// TLS_method means "any supported protocol".
|
||||
test(U, U, 'TLSv1_2_method', U, U, 'TLS_method', 'TLSv1.2');
|
||||
test(U, U, 'TLSv1_1_method', U, U, 'TLS_method', 'TLSv1.1');
|
||||
test(U, U, 'TLSv1_method', U, U, 'TLS_method', 'TLSv1');
|
||||
test(U, U, 'TLS_method', U, U, 'TLSv1_2_method', 'TLSv1.2');
|
||||
test(U, U, 'TLS_method', U, U, 'TLSv1_1_method', 'TLSv1.1');
|
||||
test(U, U, 'TLS_method', U, U, 'TLSv1_method', 'TLSv1');
|
||||
|
||||
// OpenSSL 1.1.1 and 3.0 use a different error code and alert (sent to the
|
||||
// client) when no protocols are enabled on the server.
|
||||
const NO_PROTOCOLS_AVAILABLE_SERVER = hasOpenSSL3 ?
|
||||
'ERR_SSL_NO_PROTOCOLS_AVAILABLE' : 'ERR_SSL_INTERNAL_ERROR';
|
||||
const NO_PROTOCOLS_AVAILABLE_SERVER_ALERT = hasOpenSSL3 ?
|
||||
'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION' : 'ERR_SSL_TLSV1_ALERT_INTERNAL_ERROR';
|
||||
|
||||
// SSLv23 also means "any supported protocol" greater than the default
|
||||
// minimum (which is configurable via command line).
|
||||
if (DEFAULT_MIN_VERSION === 'TLSv1.3') {
|
||||
test(U, U, 'TLSv1_2_method', U, U, 'SSLv23_method',
|
||||
U, NO_PROTOCOLS_AVAILABLE_SERVER_ALERT, NO_PROTOCOLS_AVAILABLE_SERVER);
|
||||
} else {
|
||||
test(U, U, 'TLSv1_2_method', U, U, 'SSLv23_method', 'TLSv1.2');
|
||||
}
|
||||
|
||||
if (DEFAULT_MIN_VERSION === 'TLSv1.3') {
|
||||
test(U, U, 'TLSv1_1_method', U, U, 'SSLv23_method',
|
||||
U, NO_PROTOCOLS_AVAILABLE_SERVER_ALERT, NO_PROTOCOLS_AVAILABLE_SERVER);
|
||||
test(U, U, 'TLSv1_method', U, U, 'SSLv23_method',
|
||||
U, NO_PROTOCOLS_AVAILABLE_SERVER_ALERT, NO_PROTOCOLS_AVAILABLE_SERVER);
|
||||
test(U, U, 'SSLv23_method', U, U, 'TLSv1_1_method',
|
||||
U, 'ERR_SSL_NO_PROTOCOLS_AVAILABLE', 'ERR_SSL_UNEXPECTED_MESSAGE');
|
||||
test(U, U, 'SSLv23_method', U, U, 'TLSv1_method',
|
||||
U, 'ERR_SSL_NO_PROTOCOLS_AVAILABLE', 'ERR_SSL_UNEXPECTED_MESSAGE');
|
||||
}
|
||||
|
||||
if (DEFAULT_MIN_VERSION === 'TLSv1.2') {
|
||||
test(U, U, 'TLSv1_1_method', U, U, 'SSLv23_method',
|
||||
U, 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION',
|
||||
'ERR_SSL_UNSUPPORTED_PROTOCOL');
|
||||
test(U, U, 'TLSv1_method', U, U, 'SSLv23_method',
|
||||
U, 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION',
|
||||
'ERR_SSL_UNSUPPORTED_PROTOCOL');
|
||||
// test(U, U, 'SSLv23_method', U, U, 'TLSv1_1_method',
|
||||
// U, 'ERR_SSL_UNSUPPORTED_PROTOCOL', 'ERR_SSL_WRONG_VERSION_NUMBER');
|
||||
// test(U, U, 'SSLv23_method', U, U, 'TLSv1_method',
|
||||
// U, 'ERR_SSL_UNSUPPORTED_PROTOCOL', 'ERR_SSL_WRONG_VERSION_NUMBER');
|
||||
}
|
||||
|
||||
if (DEFAULT_MIN_VERSION === 'TLSv1.1') {
|
||||
test(U, U, 'TLSv1_1_method', U, U, 'SSLv23_method', 'TLSv1.1');
|
||||
test(U, U, 'TLSv1_method', U, U, 'SSLv23_method',
|
||||
U, 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION',
|
||||
'ERR_SSL_UNSUPPORTED_PROTOCOL');
|
||||
test(U, U, 'SSLv23_method', U, U, 'TLSv1_1_method', 'TLSv1.1');
|
||||
test(U, U, 'SSLv23_method', U, U, 'TLSv1_method',
|
||||
U, 'ERR_SSL_UNSUPPORTED_PROTOCOL', 'ERR_SSL_WRONG_VERSION_NUMBER');
|
||||
}
|
||||
|
||||
if (DEFAULT_MIN_VERSION === 'TLSv1') {
|
||||
test(U, U, 'TLSv1_1_method', U, U, 'SSLv23_method', 'TLSv1.1');
|
||||
test(U, U, 'TLSv1_method', U, U, 'SSLv23_method', 'TLSv1');
|
||||
test(U, U, 'SSLv23_method', U, U, 'TLSv1_1_method', 'TLSv1.1');
|
||||
test(U, U, 'SSLv23_method', U, U, 'TLSv1_method', 'TLSv1');
|
||||
}
|
||||
|
||||
// TLSv1 thru TLSv1.2 are only supported with explicit configuration with API or
|
||||
// CLI (--tls-v1.0 and --tls-v1.1).
|
||||
test(U, U, 'TLSv1_2_method', U, U, 'TLSv1_2_method', 'TLSv1.2');
|
||||
test(U, U, 'TLSv1_1_method', U, U, 'TLSv1_1_method', 'TLSv1.1');
|
||||
test(U, U, 'TLSv1_method', U, U, 'TLSv1_method', 'TLSv1');
|
||||
|
||||
// The default default.
|
||||
if (DEFAULT_MIN_VERSION === 'TLSv1.2') {
|
||||
test(U, U, 'TLSv1_1_method', U, U, U,
|
||||
U, 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION',
|
||||
'ERR_SSL_UNSUPPORTED_PROTOCOL');
|
||||
test(U, U, 'TLSv1_method', U, U, U,
|
||||
U, 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION',
|
||||
'ERR_SSL_UNSUPPORTED_PROTOCOL');
|
||||
|
||||
if (DEFAULT_MAX_VERSION === 'TLSv1.2') {
|
||||
test(U, U, U, U, U, 'TLSv1_1_method',
|
||||
U, 'ERR_SSL_UNSUPPORTED_PROTOCOL', 'ERR_SSL_WRONG_VERSION_NUMBER');
|
||||
test(U, U, U, U, U, 'TLSv1_method',
|
||||
U, 'ERR_SSL_UNSUPPORTED_PROTOCOL', 'ERR_SSL_WRONG_VERSION_NUMBER');
|
||||
} else {
|
||||
// TLS1.3 client hellos are are not understood by TLS1.1 or below.
|
||||
test(U, U, U, U, U, 'TLSv1_1_method',
|
||||
U, 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION',
|
||||
'ERR_SSL_UNSUPPORTED_PROTOCOL');
|
||||
test(U, U, U, U, U, 'TLSv1_method',
|
||||
U, 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION',
|
||||
'ERR_SSL_UNSUPPORTED_PROTOCOL');
|
||||
}
|
||||
}
|
||||
|
||||
// The default with --tls-v1.1.
|
||||
if (DEFAULT_MIN_VERSION === 'TLSv1.1') {
|
||||
test(U, U, 'TLSv1_1_method', U, U, U, 'TLSv1.1');
|
||||
test(U, U, 'TLSv1_method', U, U, U,
|
||||
U, 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION',
|
||||
'ERR_SSL_UNSUPPORTED_PROTOCOL');
|
||||
test(U, U, U, U, U, 'TLSv1_1_method', 'TLSv1.1');
|
||||
|
||||
if (DEFAULT_MAX_VERSION === 'TLSv1.2') {
|
||||
test(U, U, U, U, U, 'TLSv1_method',
|
||||
U, 'ERR_SSL_UNSUPPORTED_PROTOCOL', 'ERR_SSL_WRONG_VERSION_NUMBER');
|
||||
} else {
|
||||
// TLS1.3 client hellos are are not understood by TLS1.1 or below.
|
||||
test(U, U, U, U, U, 'TLSv1_method',
|
||||
U, 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION',
|
||||
'ERR_SSL_UNSUPPORTED_PROTOCOL');
|
||||
}
|
||||
}
|
||||
|
||||
// The default with --tls-v1.0.
|
||||
if (DEFAULT_MIN_VERSION === 'TLSv1') {
|
||||
test(U, U, 'TLSv1_1_method', U, U, U, 'TLSv1.1');
|
||||
test(U, U, 'TLSv1_method', U, U, U, 'TLSv1');
|
||||
test(U, U, U, U, U, 'TLSv1_1_method', 'TLSv1.1');
|
||||
test(U, U, U, U, U, 'TLSv1_method', 'TLSv1');
|
||||
}
|
||||
|
||||
// TLS min/max are respected when set with no secureProtocol.
|
||||
test('TLSv1', 'TLSv1.2', U, U, U, 'TLSv1_method', 'TLSv1');
|
||||
test('TLSv1', 'TLSv1.2', U, U, U, 'TLSv1_1_method', 'TLSv1.1');
|
||||
test('TLSv1', 'TLSv1.2', U, U, U, 'TLSv1_2_method', 'TLSv1.2');
|
||||
test('TLSv1', 'TLSv1.2', U, U, U, 'TLS_method', 'TLSv1.2');
|
||||
|
||||
test(U, U, 'TLSv1_method', 'TLSv1', 'TLSv1.2', U, 'TLSv1');
|
||||
test(U, U, 'TLSv1_1_method', 'TLSv1', 'TLSv1.2', U, 'TLSv1.1');
|
||||
test(U, U, 'TLSv1_2_method', 'TLSv1', 'TLSv1.2', U, 'TLSv1.2');
|
||||
|
||||
test('TLSv1', 'TLSv1.1', U, 'TLSv1', 'TLSv1.3', U, 'TLSv1.1');
|
||||
test('TLSv1', 'TLSv1.1', U, 'TLSv1', 'TLSv1.2', U, 'TLSv1.1');
|
||||
test('TLSv1', 'TLSv1.2', U, 'TLSv1', 'TLSv1.1', U, 'TLSv1.1');
|
||||
test('TLSv1', 'TLSv1.3', U, 'TLSv1', 'TLSv1.1', U, 'TLSv1.1');
|
||||
test('TLSv1', 'TLSv1', U, 'TLSv1', 'TLSv1.1', U, 'TLSv1');
|
||||
test('TLSv1', 'TLSv1.2', U, 'TLSv1', 'TLSv1', U, 'TLSv1');
|
||||
test('TLSv1', 'TLSv1.3', U, 'TLSv1', 'TLSv1', U, 'TLSv1');
|
||||
test('TLSv1.1', 'TLSv1.1', U, 'TLSv1', 'TLSv1.2', U, 'TLSv1.1');
|
||||
test('TLSv1', 'TLSv1.2', U, 'TLSv1.1', 'TLSv1.1', U, 'TLSv1.1');
|
||||
test('TLSv1', 'TLSv1.2', U, 'TLSv1', 'TLSv1.3', U, 'TLSv1.2');
|
||||
|
||||
// v-any client can connect to v-specific server
|
||||
test('TLSv1', 'TLSv1.3', U, 'TLSv1.3', 'TLSv1.3', U, 'TLSv1.3');
|
||||
test('TLSv1', 'TLSv1.3', U, 'TLSv1.2', 'TLSv1.3', U, 'TLSv1.3');
|
||||
test('TLSv1', 'TLSv1.3', U, 'TLSv1.2', 'TLSv1.2', U, 'TLSv1.2');
|
||||
test('TLSv1', 'TLSv1.3', U, 'TLSv1.1', 'TLSv1.1', U, 'TLSv1.1');
|
||||
test('TLSv1', 'TLSv1.3', U, 'TLSv1', 'TLSv1', U, 'TLSv1');
|
||||
|
||||
// v-specific client can connect to v-any server
|
||||
test('TLSv1.3', 'TLSv1.3', U, 'TLSv1', 'TLSv1.3', U, 'TLSv1.3');
|
||||
test('TLSv1.2', 'TLSv1.2', U, 'TLSv1', 'TLSv1.3', U, 'TLSv1.2');
|
||||
test('TLSv1.1', 'TLSv1.1', U, 'TLSv1', 'TLSv1.3', U, 'TLSv1.1');
|
||||
test('TLSv1', 'TLSv1', U, 'TLSv1', 'TLSv1.3', U, 'TLSv1');
|
||||
58
test/js/node/test/parallel/test-tls-no-sslv23.js
Normal file
58
test/js/node/test/parallel/test-tls-no-sslv23.js
Normal file
@@ -0,0 +1,58 @@
|
||||
'use strict';
|
||||
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' });
|
||||
24
test/js/node/test/parallel/test-tls-ticket-invalid-arg.js
Normal file
24
test/js/node/test/parallel/test-tls-ticket-invalid-arg.js
Normal file
@@ -0,0 +1,24 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
if (!common.hasCrypto) {
|
||||
common.skip('missing crypto');
|
||||
}
|
||||
|
||||
const assert = require('assert');
|
||||
const tls = require('tls');
|
||||
|
||||
const server = new tls.Server();
|
||||
|
||||
[null, undefined, 0, 1, 1n, Symbol(), {}, [], true, false, '', () => {}]
|
||||
.forEach((arg) =>
|
||||
assert.throws(
|
||||
() => server.setTicketKeys(arg),
|
||||
{ code: 'ERR_INVALID_ARG_TYPE' }
|
||||
));
|
||||
|
||||
[new Uint8Array(1), Buffer.from([1]), new DataView(new ArrayBuffer(2))].forEach(
|
||||
(arg) =>
|
||||
assert.throws(() => {
|
||||
server.setTicketKeys(arg);
|
||||
}, /Session ticket keys must be a 48-byte buffer/)
|
||||
);
|
||||
5
test/js/node/tls/node-tls-reject-unauthorized-env.ts
Normal file
5
test/js/node/tls/node-tls-reject-unauthorized-env.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { describe, it } from "bun:test";
|
||||
|
||||
describe("Bun respects the NODE_TLS_REJECT_UNAUTHORIZED environment variables", () => {
|
||||
it.todo("should reject unauthorized certificates by default");
|
||||
});
|
||||
103
test/js/node/tls/tls-min-max-cli-args.test.ts
Normal file
103
test/js/node/tls/tls-min-max-cli-args.test.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { bunEnv, bunExe } from "harness";
|
||||
|
||||
const PRINT_MIN = ["-p", "tls.DEFAULT_MIN_VERSION"];
|
||||
const PRINT_MAX = ["-p", "tls.DEFAULT_MAX_VERSION"];
|
||||
|
||||
const TLS_VERSION_TO_SECUREVERSION: Record<`${number}.${number}`, import("tls").SecureVersion> = {
|
||||
"1.0": "TLSv1",
|
||||
"1.1": "TLSv1.1",
|
||||
"1.2": "TLSv1.2",
|
||||
"1.3": "TLSv1.3",
|
||||
};
|
||||
|
||||
describe("TLS min/max CLI args", () => {
|
||||
test.each(["1.0", "1.1", "1.2", "1.3"])("TLSv%s", async version => {
|
||||
const child = Bun.spawn({
|
||||
cmd: [bunExe(), `--tls-min-v${version}`, ...PRINT_MIN],
|
||||
stdio: ["pipe", "pipe", "pipe"],
|
||||
env: bunEnv,
|
||||
});
|
||||
|
||||
const stdout = await Bun.readableStreamToText(child.stdout);
|
||||
|
||||
expect(stdout.trim()).toBe(TLS_VERSION_TO_SECUREVERSION[version]);
|
||||
});
|
||||
|
||||
test.each(["1.2", "1.3"])("TLSv%s", async version => {
|
||||
const child = Bun.spawn({
|
||||
cmd: [bunExe(), `--tls-max-v${version}`, ...PRINT_MAX],
|
||||
stdio: ["pipe", "pipe", "pipe"],
|
||||
env: bunEnv,
|
||||
});
|
||||
|
||||
const stdout = await Bun.readableStreamToText(child.stdout);
|
||||
|
||||
expect(stdout.trim()).toBe(`TLSv${version}`);
|
||||
});
|
||||
|
||||
test("Specifying both min and max should exit with error code 1", async () => {
|
||||
const child = Bun.spawn({
|
||||
cmd: [bunExe(), "--tls-min-v1.3", "--tls-max-v1.3"],
|
||||
stdio: ["pipe", "pipe", "pipe"],
|
||||
env: bunEnv,
|
||||
});
|
||||
|
||||
const stderr = await Bun.readableStreamToText(child.stderr);
|
||||
expect(stderr.trim()).toMatch(/not both/);
|
||||
|
||||
expect(await child.exited).toBe(1);
|
||||
});
|
||||
|
||||
test("Specifying multiple max flags should use the highest version", async () => {
|
||||
// Node.js docs:
|
||||
// If multiple of the options are provided, the highest maximum is used.
|
||||
|
||||
const child = Bun.spawn({
|
||||
cmd: [bunExe(), "--tls-max-v1.3", "--tls-max-v1.2", ...PRINT_MAX],
|
||||
stdio: ["pipe", "pipe", "pipe"],
|
||||
env: bunEnv,
|
||||
});
|
||||
|
||||
const stdout = await Bun.readableStreamToText(child.stdout);
|
||||
expect(stdout.trim()).toBe("TLSv1.3");
|
||||
});
|
||||
|
||||
test("Specifying multiple min flags should use the lowest version", async () => {
|
||||
// Node.js docs:
|
||||
// If multiple of the options are provided, the lowest minimum is used.
|
||||
|
||||
const child = Bun.spawn({
|
||||
cmd: [bunExe(), "--tls-min-v1.3", "--tls-min-v1.2", ...PRINT_MIN],
|
||||
stdio: ["pipe", "pipe", "pipe"],
|
||||
env: bunEnv,
|
||||
});
|
||||
|
||||
const stdout = await Bun.readableStreamToText(child.stdout);
|
||||
expect(stdout.trim()).toBe("TLSv1.2");
|
||||
});
|
||||
|
||||
test("invalid min/max vals should do nothing since the flags don't exist in the CLI parser", async () => {
|
||||
const { DEFAULT_MAX_VERSION, DEFAULT_MIN_VERSION } = await import("tls");
|
||||
|
||||
{
|
||||
const child = Bun.spawn({
|
||||
cmd: [bunExe(), "--tls-max-v1.9999", ...PRINT_MAX],
|
||||
env: bunEnv,
|
||||
});
|
||||
|
||||
const stdout = await Bun.readableStreamToText(child.stdout);
|
||||
expect(stdout.trim()).toBe(DEFAULT_MAX_VERSION);
|
||||
}
|
||||
|
||||
{
|
||||
const child = Bun.spawn({
|
||||
cmd: [bunExe(), "--tls-min-v1.9999", ...PRINT_MIN],
|
||||
env: bunEnv,
|
||||
});
|
||||
|
||||
const stdout = await Bun.readableStreamToText(child.stdout);
|
||||
expect(stdout.trim()).toBe(DEFAULT_MIN_VERSION);
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user