node_modules: update (#338)

Co-authored-by: dawidd6 <9713907+dawidd6@users.noreply.github.com>
This commit is contained in:
Dawid Dziurlaanddawidd6 authored and GitHub committed 2026-10-03 10:23:35 +02:00
1 parent e1936242ed
commit a9c5eb6c35
39 files changed
+799 -388

No files matched your search

+77 -23
View File
@@ -66,8 +66,8 @@ function isPartialLine(line) {
* * **port** - is the port to connect to (defaults to 587 or 465)
* * **host** - is the hostname or IP address to connect to (defaults to 'localhost')
* * **secure** - use SSL
* * **ignoreTLS** - ignore server support for STARTTLS
* * **requireTLS** - forces the client to use STARTTLS
* * **ignoreTLS** - ignore server support for STARTTLS (has no effect when requireTLS is set)
* * **requireTLS** - forces the client to use STARTTLS, takes precedence over ignoreTLS and opportunisticTLS
* * **name** - the name of the client server
* * **localAddress** - outbound address to bind to (see: http://nodejs.org/api/net.html#net_net_connect_options_connectionlistener)
* * **greetingTimeout** - Time to wait in ms until greeting message is received from the server (defaults to 30 seconds)
@@ -92,6 +92,11 @@ class SMTPConnection extends EventEmitter {
this.id = crypto.randomBytes(8).toString('base64').replace(/\W/g, '');
this.stage = 'init';
this.options = options || {};
if (this.options.requireTLS && (this.options.ignoreTLS || this.options.opportunisticTLS)) {
// requireTLS wins, a contradictory configuration must not quietly fall back to plaintext.
// Copied so the caller's (possibly shared) options object is left as it was
this.options = Object.assign({}, this.options, { ignoreTLS: false, opportunisticTLS: false });
}
this.secureConnection = !!this.options.secure;
this.alreadySecured = !!this.options.secured;
this.port = Number(this.options.port) || (this.secureConnection ? 465 : 587);
@@ -135,6 +140,8 @@ class SMTPConnection extends EventEmitter {
this._destroyed = false;
this._closing = false;
this._currentDataStream = false;
this._pendingSend = false;
this._connectCallback = false;
this._onSocketData = chunk => this._onData(chunk);
this._onSocketError = error => this._onError(error, 'ESOCKET', false, 'CONN');
this._onSocketClose = () => this._onClose();
@@ -149,7 +156,9 @@ class SMTPConnection extends EventEmitter {
*/
connect(connectCallback) {
if (typeof connectCallback === 'function') {
this._connectCallback = connectCallback;
this.once('connect', () => {
this._connectCallback = false;
this.logger.debug({
tnx: 'smtp'
}, 'SMTP handshake finished');
@@ -381,6 +390,16 @@ class SMTPConnection extends EventEmitter {
}
this._currentDataStream = false;
}
// Detach from the message stream as well. The listener is swapped for a no-op rather than
// removed, a stream destroyed with an error later on would otherwise throw it as unhandled
if (this._pendingSend) {
const { stream, onStreamError } = this._pendingSend;
if (stream) {
stream.removeListener('error', onStreamError);
stream.on('error', TEARDOWN_NOOP);
}
this._pendingSend = false;
}
if (socket && !socket.destroyed) {
try {
// Clear socket timeout to prevent timer leaks
@@ -568,6 +587,9 @@ class SMTPConnection extends EventEmitter {
return;
}
returned = true;
if (this._pendingSend && this._pendingSend.callback === callback) {
this._pendingSend = false;
}
done(err, info);
};
if (!message) {
@@ -584,9 +606,16 @@ class SMTPConnection extends EventEmitter {
});
return;
}
const pendingSend = {
callback,
stream: false,
onStreamError: err => callback(this._formatError(err, 'ESTREAM', false, 'API'))
};
if (typeof message.on === 'function') {
message.on('error', err => callback(this._formatError(err, 'ESTREAM', false, 'API')));
pendingSend.stream = message;
pendingSend.stream.on('error', pendingSend.onStreamError);
}
this._pendingSend = pendingSend;
const startTime = Date.now();
this._setEnvelope(envelope, (err, info) => {
if (err) {
@@ -779,8 +808,14 @@ class SMTPConnection extends EventEmitter {
else {
this.logger.error(data, err.message);
}
// close() forgets the send in flight, it is completed with this same error afterwards so
// a late message stream error has nothing left to report
const pendingSend = this._pendingSend;
this.emit('error', err);
this.close();
if (pendingSend) {
pendingSend.callback(err);
}
}
/** @internal */
_formatError(message, type, response, command) {
@@ -826,14 +861,25 @@ class SMTPConnection extends EventEmitter {
this.logger.info({
tnx: 'network'
}, 'Connection closed');
// the unterminated remainder is only reported as a reply (and so gives the error a responseCode)
// when it starts like a complete failure reply, not for a fragment such as "55" or a 250
const failureResponse = typeof serverResponse === 'string' && /^[45]\d{2}[ -]/.test(serverResponse) ? serverResponse : false;
if (this.upgrading && !this._destroyed) {
return this._onError(new Error('Connection closed unexpectedly'), 'ETLS', serverResponse, 'CONN');
return this._onError(new Error('Connection closed unexpectedly'), 'ETLS', failureResponse, 'CONN');
}
else if (![this._actionGreeting, this.close].includes(this._responseActions[0]) && !this._destroyed) {
return this._onError(new Error('Connection closed unexpectedly'), 'ECONNECTION', serverResponse, 'CONN');
if (!failureResponse && this._responseActions[0] === this._actionGreeting && this._connectCallback && !this._destroyed) {
// A silent close before the greeting is handed to the connect() callback rather than
// emitted as 'error', callers that never saw an error for it must not start throwing one
const connectCallback = this._connectCallback;
this._connectCallback = false;
const err = this._formatError(new Error('Connection closed unexpectedly'), 'ECONNECTION', false, 'CONN');
this.logger.warn({ tnx: 'network' }, err.message);
connectCallback(err);
this.close();
return;
}
else if (/^[45]\d{2}\b/.test(serverResponse)) {
return this._onError(new Error('Connection closed unexpectedly'), 'ECONNECTION', serverResponse, 'CONN');
if (failureResponse || (this._responseActions[0] !== this.close && !this._destroyed)) {
return this._onError(new Error('Connection closed unexpectedly'), 'ECONNECTION', failureResponse, 'CONN');
}
this._destroy();
}
@@ -1310,21 +1356,25 @@ class SMTPConnection extends EventEmitter {
if (/[ -]AUTH\b/i.test(str)) {
this.allowsAuth = true;
}
// Detect if the server supports PLAIN auth
if (/[ -]AUTH(?:(\s+|=)[^\n]*\s+|\s+|=)PLAIN/i.test(str)) {
this._supportedAuth.push('PLAIN');
// Detect the advertised SASL mechanisms. The list is split into whole tokens rather
// than searched for each name with a pattern: the patterns this replaced let two
// whitespace runs overlap and backtracked quadratically over an AUTH line padded with
// spaces, so a hostile server could stall the event loop from its EHLO reply
// (GHSA-4ffr-jq9g-5ffx).
const authMechanisms = new Set();
for (const line of this._ehloLines) {
const authMatch = /^AUTH[\s=](.*)/i.exec(line);
if (authMatch) {
for (const mechanism of authMatch[1].split(/[\s=]+/)) {
authMechanisms.add(mechanism.toUpperCase());
}
}
}
// Detect if the server supports LOGIN auth
if (/[ -]AUTH(?:(\s+|=)[^\n]*\s+|\s+|=)LOGIN/i.test(str)) {
this._supportedAuth.push('LOGIN');
}
// Detect if the server supports CRAM-MD5 auth
if (/[ -]AUTH(?:(\s+|=)[^\n]*\s+|\s+|=)CRAM-MD5/i.test(str)) {
this._supportedAuth.push('CRAM-MD5');
}
// Detect if the server supports XOAUTH2 auth
if (/[ -]AUTH(?:(\s+|=)[^\n]*\s+|\s+|=)XOAUTH2/i.test(str)) {
this._supportedAuth.push('XOAUTH2');
// listed in order of preference, the first one the credentials allow is used
for (const mechanism of ['PLAIN', 'LOGIN', 'CRAM-MD5', 'XOAUTH2']) {
if (authMechanisms.has(mechanism)) {
this._supportedAuth.push(mechanism);
}
}
// Detect if the server supports SIZE extensions (and the max allowed size)
if ((match = str.match(/[ -]SIZE(?:[ \t]+(\d+))?/im))) {
@@ -1584,7 +1634,11 @@ class SMTPConnection extends EventEmitter {
this._sendCommand('DATA');
}
else {
err = this._formatError("Can't send mail - all recipients were rejected", 'EENVELOPE', str, 'RCPT TO');
// report a temporary rejection when there is one, taking the last reply would mark the
// whole message as permanently failed although some recipients were only deferred
const deferred = envelope.rejectedErrors.find(rejectedErr => rejectedErr.responseCode && rejectedErr.responseCode < 500);
const reply = deferred?.response ?? str;
err = this._formatError("Can't send mail - all recipients were rejected", 'EENVELOPE', reply, 'RCPT TO');
err.rejected = envelope.rejected;
err.rejectedErrors = envelope.rejectedErrors;
return callback(err);