From 42f949c3455dfeaf9c52e982fe5cdbe5934cac0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gera=20G=2E=20G=C3=BCiles?= Date: Fri, 23 Oct 2026 21:05:15 +0200 Subject: [PATCH 1/2] Hardened hostname handling against Node.js October 2026 security release Node.js published security releases on 2026-10-21 (22.23.4, 24.22.1, 26.12.1). Several of the fixed issues depend on how a hostname is written before it reaches the DNS resolver or the TLS identity check. Total.js cannot require a specific Node.js version, so the framework now normalizes and validates hostnames itself, whatever Node.js version the user runs. Related advisories: - CVE-2026-105482 (high): dns.resolve*() truncates names at a NUL byte, so "attacker.example\0.trusted.example" can pass a hostname validation and then resolve a different name. - CVE-2026-105483 (high), CVE-2026-106069 (high), CVE-2026-106072 (medium): tls.checkServerIdentity() mishandles IPv4 literals written with non-ASCII digits, leading zeros (octal vs decimal) or non-canonical forms (0x7f.0.0.1, 127.1, 2130706433), so a certificate can match a different address than the one the OS connects to. Changes: - utils.js: added U.validhost(host). It accepts plain hostnames and canonical IP literals only and rejects NUL bytes, non-ASCII characters and non-canonical IPv4 forms. - utils.js: added U.tohost(host). It converts the host to its canonical ASCII form with url.domainToASCII (punycode for international names, dotted IPv4 for numeric forms) and returns an empty string when the host is invalid. Valid input keeps working: only the written form is normalized. - utils.js: U.resolve() (used by REQUEST) rejects invalid hostnames before resolving. parseURI() already normalizes URLs, so this is defense in depth. - utils.js: U.connect() normalizes opt.host and returns an "Invalid hostname" error instead of connecting when the host is not valid. - mail.js: the SMTP server is normalized with U.tohost() on a copy of the options (the user's object is not modified). Invalid values return "Invalid SMTP server hostname." instead of reaching the socket. Not covered by the framework (fixed only by updating Node.js): - CVE-2026-105481 (medium): unsolicited data on a reused keep-alive socket. - The Brotli decoder issue (CVE-2026-111497) does not apply, the framework only decompresses gzip and deflate. The WebSocket client and the HTTPS proxy take their hosts from parseURI(), so they already receive normalized values. --- mail.js | 9 +++++++-- utils.js | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/mail.js b/mail.js index a17f838..4186328 100755 --- a/mail.js +++ b/mail.js @@ -477,8 +477,13 @@ Mailer.send = function(opt, messages, callback) { if (opt.secure && !opt.port) opt.port = 465; - if (!opt.server) { - const err = new Error('No SMTP server configuration.'); + const server = opt.server ? F.TUtils.tohost(opt.server) : ''; + + if (server && server !== opt.server) + opt = Object.assign({}, opt, { server: server }); + + if (!server) { + const err = new Error(opt.server ? 'Invalid SMTP server hostname.' : 'No SMTP server configuration.'); let iscallback = false; if (callback) { diff --git a/utils.js b/utils.js index 49a94b8..6f675f9 100755 --- a/utils.js +++ b/utils.js @@ -489,12 +489,41 @@ exports.toURLEncode = function(value) { return builder.length ? builder.join('&') : ''; }; +const REG_HOST_NUMERIC = /^(0x[0-9a-f]+|[0-9]+)(\.(0x[0-9a-f]+|[0-9]+))*$/i; +const REG_HOST_IPV4 = /^((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])$/; + +// Accepts plain hostnames and canonical IP literals only. It rejects NUL bytes, non-ASCII characters +// and non-canonical IPv4 forms (0127.0.0.1, 0x7f.1, 2130706433, 127.1) which Node.js, certificate +// checks and the OS can interpret differently. +exports.validhost = function(host) { + + if (!host || typeof(host) !== 'string' || /[^\x21-\x7e]/.test(host)) + return false; + + return REG_HOST_NUMERIC.test(host) ? REG_HOST_IPV4.test(host) : true; +}; + +// Converts a host to its canonical ASCII form (punycode for international names, canonical dotted IPv4) +// and returns an empty string when the host is not valid. +exports.tohost = function(host) { + + if (!host || typeof(host) !== 'string') + return ''; + + if (host.indexOf(':') === -1) + host = F.Url.domainToASCII(host); + + return exports.validhost(host) ? host : ''; +}; + exports.resolve = function(url, callback, param) { let uri; try { uri = exports.parseURI(url); + if (!exports.validhost(uri.hostname)) + throw new Error('Invalid hostname'); } catch (e) { callback(e); return; @@ -5921,6 +5950,15 @@ exports.connect = function(opt, callback) { delete opt.tls; + if (opt.host) { + opt.host = exports.tohost(opt.host); + if (!opt.host) { + meta.destroy = meta.close = NOOP; + callback && setImmediate(callback, new Error('Invalid hostname')); + return meta; + } + } + var close = function() { if (meta.socket1) { From d05b59fdcf8b2948b185b90a8f41e1deffbb33a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gera=20G=2E=20G=C3=BCiles?= Date: Fri, 23 Oct 2026 21:11:13 +0200 Subject: [PATCH 2/2] Updated changelog with the Node.js October 2026 security hardening Documents the hostname normalization and validation (U.validhost, U.tohost, U.resolve, U.connect, SMTP server) and the Node.js CVEs it covers (CVE-2026-105482, CVE-2026-105483, CVE-2026-106069, CVE-2026-106072). Notes that CVE-2026-105481 is fixed only by updating Node.js and that CVE-2026-111497 does not affect the framework. --- changelog.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/changelog.txt b/changelog.txt index ac0bf00..d409588 100644 --- a/changelog.txt +++ b/changelog.txt @@ -29,6 +29,15 @@ - fixed `attachmentfs()` in the mail: the file of the storage is read as a Buffer and the message is completed (it threw a `TypeError` and the SMTP connection stayed open) - fixed `attachment()` with a `Buffer` in the mail: the Base64 of the whole Buffer is split into lines (the bytes were split into pieces of 68 bytes and each one got its own padding) - fixed `@{hostname()}` view engine command +- security: hardened the hostnames against the Node.js security release of 2026-10-21 (patched in Node.js 22.23.4, 24.22.1 and 26.12.1) ; the framework does not require a specific version of Node.js, the hostnames are normalized and validated in the framework itself + - CVE-2026-105482: `dns.resolve*()` truncates a name at a NUL byte (`attacker.example\0.trusted.example`) + - CVE-2026-105483, CVE-2026-106069, CVE-2026-106072: `tls.checkServerIdentity()` with IPv4 literals written with non-ASCII digits, leading zeros or in a non-canonical form (`0x7f.0.0.1`, `127.1`, `2130706433`) +- added `U.validhost(host)` that accepts only plain hostnames and canonical IP literals +- added `U.tohost(host)` that converts a hostname to the canonical ASCII form (punycode, dotted IPv4) and returns an empty string for an invalid hostname +- fixed `U.resolve()` (used by `REQUEST()`): an invalid hostname gives an error before the DNS query +- fixed `U.connect()`: the `host` is normalized and an invalid hostname gives the `Invalid hostname` error +- fixed the SMTP `server` in the mail: the hostname is normalized (international names to punycode, IPv4 to the canonical form) and an invalid hostname gives the `Invalid SMTP server hostname.` error +- note: the issue CVE-2026-105481 (unsolicited data on a reused keep-alive socket, `http.Agent`) is fixed only by updating Node.js ; the Brotli issue CVE-2026-111497 does not affect the framework (only `gzip` and `deflate` are decompressed) ======================== 0.0.19