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
+17 -1
View File
@@ -181,6 +181,7 @@ class Tokenizer {
this.operatorExpecting = '';
this.node = null;
this.escaped = false;
this.inDomainLiteral = false;
this.list = [];
/**
@@ -232,6 +233,21 @@ class Tokenizer {
* @param {String} chr Character from the address field
*/
checkChar(chr, nextChr) {
// Track RFC 5322 domain-literals ("[" *dtext "]"). Operator characters such
// as the ":" of an IPv6 address-literal (user@[IPv6:2001:db8::1]) are dtext
// and must not be treated as the group delimiter while inside the brackets.
// Quoted strings and comments are handled separately via operatorExpecting,
// so only enter this state when no operator is open. The list separators ","
// and ";" are the exception: they always end the literal (and split the
// address list) so that an unclosed "[" cannot swallow later recipients.
if (!this.escaped && !this.operatorExpecting) {
if (!this.inDomainLiteral && chr === '[') {
this.inDomainLiteral = true;
} else if (this.inDomainLiteral && (chr === ']' || chr === ',' || chr === ';')) {
this.inDomainLiteral = false;
}
}
if (this.escaped) {
// ignore next condition blocks
} else if (chr === this.operatorExpecting) {
@@ -250,7 +266,7 @@ class Tokenizer {
this.escaped = false;
return;
} else if (!this.operatorExpecting && chr in this.operators) {
} else if (!this.operatorExpecting && !this.inDomainLiteral && chr in this.operators) {
this.node = {
type: 'operator',
value: chr
+1 -1
View File
@@ -11,7 +11,7 @@ class RelaxedBody extends Transform {
options = options || {};
this.chunkBuffer = [];
this.chunkBufferLen = 0;
this.bodyHash = crypto.createHash(options.hashAlgo || 'sha1');
this.bodyHash = crypto.createHash(options.hashAlgo || 'sha256');
this.remainder = '';
this.byteLength = 0;
+5 -1
View File
@@ -32,7 +32,11 @@ class MailComposer {
// Compose MIME tree
if (this.mail.raw) {
this.message = new MimeNode('message/rfc822', { newline: this.mail.newline }).setRaw(this.mail.raw);
this.message = new MimeNode('message/rfc822', {
newline: this.mail.newline,
disableUrlAccess: this.mail.disableUrlAccess,
disableFileAccess: this.mail.disableFileAccess
}).setRaw(this.mail.raw);
} else if (this._useMixed) {
this.message = this._createMixed();
} else if (this._useAlternative) {
+43 -13
View File
@@ -43,11 +43,12 @@ class SESTransport extends EventEmitter {
getRegion(cb) {
if (this.ses.sesClient.config && typeof this.ses.sesClient.config.region === 'function') {
// promise
return this.ses.sesClient.config
.region()
.then(region => cb(null, region))
.catch(err => cb(err));
// Resolve the region provider. Use the two-argument form of then() so that a
// synchronous throw from cb is not recaught here and used to invoke cb a second time.
return this.ses.sesClient.config.region().then(
region => cb(null, region),
err => cb(err)
);
}
return cb(null, false);
}
@@ -150,8 +151,27 @@ class SESTransport extends EventEmitter {
region = 'us-east-1';
}
const command = new this.ses.SendEmailCommand(sesMessage);
const sendPromise = this.ses.sesClient.send(command);
let sendPromise;
try {
// command construction or dispatch can throw synchronously on a
// misconfigured SDK; surface it as a single error callback instead
// of letting it escape into getRegion's promise chain
const command = new this.ses.SendEmailCommand(sesMessage);
sendPromise = this.ses.sesClient.send(command);
} catch (err) {
tagSesError(err);
this.logger.error(
{
err,
tnx: 'send'
},
'Send error for %s: %s',
messageId,
err.message
);
setImmediate(() => callback(err));
return;
}
sendPromise
.then(data => {
@@ -159,7 +179,7 @@ class SESTransport extends EventEmitter {
region = 'email';
}
callback(null, {
const info = {
envelope: {
from: envelope.from,
to: envelope.to
@@ -167,7 +187,11 @@ class SESTransport extends EventEmitter {
messageId: '<' + data.MessageId + (!/@/.test(data.MessageId) ? '@' + region + '.amazonses.com' : '') + '>',
response: data.MessageId,
raw
});
};
// invoke the callback outside the promise chain so a throw from it
// is not recaught by .catch() and used to call it a second time
setImmediate(() => callback(null, info));
})
.catch(err => {
tagSesError(err);
@@ -180,7 +204,7 @@ class SESTransport extends EventEmitter {
messageId,
err.message
);
callback(err);
setImmediate(() => callback(err));
});
});
})
@@ -222,10 +246,16 @@ class SESTransport extends EventEmitter {
// the region value is not used for anything when verifying, but the lookup
// exercises the client configuration the same way as send() does
this.getRegion(() => {
const command = new this.ses.SendEmailCommand(sesMessage);
const sendPromise = this.ses.sesClient.send(command);
let sendPromise;
try {
const command = new this.ses.SendEmailCommand(sesMessage);
sendPromise = this.ses.sesClient.send(command);
} catch (err) {
setImmediate(() => cb(err));
return;
}
sendPromise.then(() => cb(null)).catch(err => cb(err));
sendPromise.then(() => setImmediate(() => cb(null))).catch(err => setImmediate(() => cb(err)));
});
return promise;
+21
View File
@@ -9,6 +9,10 @@ const tls = require('tls');
const urllib = require('../shared/url');
const errors = require('../errors');
// Cap the CONNECT response we buffer before the header terminator, so a proxy that
// never sends \r\n\r\n cannot grow memory unboundedly before the socket times out.
const MAX_RESPONSE_HEADER_BYTES = 64 * 1024;
/**
* Establishes proxied connection to destinationPort
*
@@ -29,6 +33,16 @@ function httpProxyClient(proxyUrl, destinationPort, destinationHost, tlsOptions,
}
tlsOptions = tlsOptions || {};
// Reject CRLF in the destination before it reaches the CONNECT request line
// and Host header. A tainted host/port could otherwise inject additional
// request headers into the proxy connection (HTTP request splitting).
destinationPort = Number(destinationPort) || 0;
if (!destinationPort || /[\r\n]/.test(destinationHost)) {
const err = new Error('Invalid proxy destination');
err.code = errors.EPROXY;
return setImmediate(() => callback(err));
}
const proxy = urllib.parse(proxyUrl);
const connectOptions = {
@@ -140,6 +154,13 @@ function httpProxyClient(proxyUrl, destinationPort, destinationHost, tlsOptions,
return callback(null, socket);
}
if (headers.length > MAX_RESPONSE_HEADER_BYTES) {
socket.removeListener('data', onSocketData);
const err = new Error('Proxy response headers too large');
err.code = errors.EPROXY;
return tempSocketErr(err);
}
};
socket.on('data', onSocketData);
});
+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');
}