node_modules: update (#307)

Co-authored-by: dawidd6 <9713907+dawidd6@users.noreply.github.com>
This commit is contained in:
Dawid Dziurla
2026-08-07 08:08:22 +02:00
committed by GitHub
parent 5cdad7c44d
commit 8de3c31270
35 changed files with 1023 additions and 339 deletions
+80 -22
View File
@@ -298,6 +298,20 @@ class SMTPConnection extends EventEmitter {
try {
this._socket.connect(this.port, this.host, () => {
this._socket.setKeepAlive(true);
// a `secure` connection over a caller-provided socket must still
// perform the TLS handshake, otherwise AUTH and the message body
// would be sent in cleartext despite the caller requesting TLS
if (this.secureConnection && !this.alreadySecured) {
return this._upgradeConnection(err => {
if (err) {
this._onError(new Error('Error initiating TLS - ' + (err.message || err)), 'ETLS', false, 'CONN');
return;
}
this._onConnect();
});
}
this._onConnect();
});
this._setupConnectionHandlers();
@@ -365,6 +379,14 @@ class SMTPConnection extends EventEmitter {
* @param {Boolean} secure Whether to use TLS
*/
_connectToHost(opts, secure) {
// If the client was closed while DNS resolution was in flight, do not open
// a socket here: close() ran with this._socket still unset and so had
// nothing to tear down, and _onConnect's remedial close() is a no-op once
// _closing is set — the freshly connected socket would leak.
if (this._destroyed || this._closing) {
return;
}
this._connectionAttemptId++;
const currentAttemptId = this._connectionAttemptId;
@@ -431,6 +453,9 @@ class SMTPConnection extends EventEmitter {
if (this._socket) {
try {
this._socket.removeListener('error', this._onConnectionSocketError);
// Absorb any late teardown error (e.g. a TLS fallback socket emitting
// after destroy), mirroring the guard used in close()
this._socket.on('error', TEARDOWN_NOOP);
this._socket.destroy();
} catch (_E) {
// ignore
@@ -757,6 +782,11 @@ class SMTPConnection extends EventEmitter {
* @param {Function} callback Callback to return once connection is reset
*/
reset(callback) {
const isDestroyedMessage = this._isDestroyedMessage('reset');
if (isDestroyedMessage) {
return callback(this._formatError(isDestroyedMessage, 'ECONNECTION', false, 'API'));
}
this._sendCommand('RSET');
this._responseActions.push(str => {
if (str.charAt(0) !== '2') {
@@ -805,6 +835,9 @@ class SMTPConnection extends EventEmitter {
this._socket.removeListener('end', this._onSocketEnd);
// Switch from connection-phase error handler to normal error handler
this._socket.removeListener('error', this._onConnectionSocketError);
// _upgradeConnection (options.connection + secure) may already have attached
// the normal handler; remove it first so we never end up with a duplicate
this._socket.removeListener('error', this._onSocketError);
this._socket.on('error', this._onSocketError);
this._socket.on('data', this._onSocketData);
@@ -994,6 +1027,8 @@ class SMTPConnection extends EventEmitter {
return;
}
this._destroyed = true;
// keep the documented public flag in sync with the private state
this.destroyed = true;
this.emit('end');
}
@@ -1004,6 +1039,15 @@ class SMTPConnection extends EventEmitter {
* has been secured
*/
_upgradeConnection(callback) {
// RFC 3207 section 6: the client MUST discard any knowledge obtained from
// the server that was not received over the TLS-protected session. Drop any
// buffered input received before the handshake so a man-in-the-middle cannot
// inject plaintext bytes after the "220" reply (e.g. a CRLF-free fragment that
// would otherwise be prepended to the first post-TLS response and parsed as
// part of the secured EHLO capabilities). STARTTLS response injection.
this._remainder = '';
this._responseQueue = [];
// do not remove all listeners or it breaks node v0.10 as there's
// apparently a 'finish' event set that would be cleared as well
@@ -1032,6 +1076,9 @@ class SMTPConnection extends EventEmitter {
socketPlain.removeListener('close', this._onSocketClose);
socketPlain.removeListener('end', this._onSocketEnd);
socketPlain.removeListener('error', this._onSocketError);
// the connection-phase handler is attached when upgrading a pre-opened
// options.connection socket; strip it so nothing lingers on the plain socket
socketPlain.removeListener('error', this._onConnectionSocketError);
};
this.upgrading = true;
@@ -1064,18 +1111,27 @@ class SMTPConnection extends EventEmitter {
/**
* Processes queued responses from the server
*
* @param {Boolean} force If true, ignores _processing flag
*/
_processResponse() {
if (!this._responseQueue.length) {
return false;
}
let str = (this.lastServerResponse = decodeServerResponse((this._responseQueue.shift() || '').toString()));
const raw = (this._responseQueue.shift() || '').toString();
// Skip unexpected empty lines without consuming a response action or
// overwriting lastServerResponse; reprocess whatever else is queued.
if (!raw.trim()) {
setImmediate(() => this._processResponse());
return;
}
let str = (this.lastServerResponse = decodeServerResponse(raw));
if (/^\d+-/.test(str.split('\n').pop())) {
// keep waiting for the final part of multiline response
// last line is still a continuation: put the partial response back on the
// queue and wait for the rest rather than dropping it
this._responseQueue.unshift(raw);
return;
}
@@ -1088,11 +1144,6 @@ class SMTPConnection extends EventEmitter {
);
}
if (!str.trim()) {
// skip unexpected empty lines
setImmediate(() => this._processResponse());
}
const action = this._responseActions.shift();
if (typeof action === 'function') {
@@ -1189,6 +1240,23 @@ class SMTPConnection extends EventEmitter {
}
}
// RFC 8689: validate REQUIRETLS eligibility before queuing the MAIL FROM
// response action, so a rejection here cannot leave an orphaned action in
// _responseActions (which would consume the next reply and desync a reused
// connection).
if (this._envelope.requireTLSExtensionEnabled) {
if (!this.secure) {
return callback(
this._formatError('REQUIRETLS can only be used over TLS connections (RFC 8689)', 'EREQUIRETLS', false, 'MAIL FROM')
);
}
if (!this._supportedExtensions.includes('REQUIRETLS')) {
return callback(
this._formatError('Server does not support REQUIRETLS extension (RFC 8689)', 'EREQUIRETLS', false, 'MAIL FROM')
);
}
}
this._responseActions.push(str => {
this._actionMAIL(str, callback);
});
@@ -1225,20 +1293,10 @@ class SMTPConnection extends EventEmitter {
}
}
// RFC 8689: If the envelope requests REQUIRETLS extension
// then append REQUIRETLS keyword to the MAIL FROM command
// Note: REQUIRETLS can only be used over TLS connections and requires server support
// RFC 8689: append the REQUIRETLS keyword to MAIL FROM. Eligibility
// (TLS connection + server support) was already validated above, before
// the response action was queued.
if (this._envelope.requireTLSExtensionEnabled) {
if (!this.secure) {
return callback(
this._formatError('REQUIRETLS can only be used over TLS connections (RFC 8689)', 'EREQUIRETLS', false, 'MAIL FROM')
);
}
if (!this._supportedExtensions.includes('REQUIRETLS')) {
return callback(
this._formatError('Server does not support REQUIRETLS extension (RFC 8689)', 'EREQUIRETLS', false, 'MAIL FROM')
);
}
args.push('REQUIRETLS');
}