mirror of
https://github.com/ovosimpatico/smtp-loadbalancer.git
synced 2026-01-15 08:23:37 -03:00
Enhance email handling by adding buffer serialization for attachments in QueueManager, sanitizing attachments in SMTPClient, and implementing email loop detection in IncomingSMTPServer.
This commit is contained in:
@@ -40,13 +40,39 @@ export class QueueManager {
|
||||
{
|
||||
store: new SQLiteStore({
|
||||
path: this.dbPath,
|
||||
// Store email
|
||||
serialize: (data) => JSON.stringify(data),
|
||||
deserialize: (text) => JSON.parse(text),
|
||||
// Buffer handling
|
||||
serialize: (data) => {
|
||||
// Convert attachment buffers to base64
|
||||
const serializable = { ...data };
|
||||
if (serializable.attachments) {
|
||||
serializable.attachments = serializable.attachments.map((att) => ({
|
||||
...att,
|
||||
content: att.content && Buffer.isBuffer(att.content)
|
||||
? att.content.toString('base64')
|
||||
: att.content,
|
||||
_isBuffer: Buffer.isBuffer(att.content),
|
||||
}));
|
||||
}
|
||||
return JSON.stringify(serializable);
|
||||
},
|
||||
deserialize: (text) => {
|
||||
const data = JSON.parse(text);
|
||||
// Convert base64 back to Buffers
|
||||
if (data.attachments) {
|
||||
data.attachments = data.attachments.map((att) => ({
|
||||
...att,
|
||||
content: att._isBuffer && typeof att.content === 'string'
|
||||
? Buffer.from(att.content, 'base64')
|
||||
: att.content,
|
||||
}));
|
||||
// Clean up
|
||||
data.attachments.forEach((att) => delete att._isBuffer);
|
||||
}
|
||||
return data;
|
||||
},
|
||||
}),
|
||||
// Retry
|
||||
// Retry configuration
|
||||
maxRetries: queueConfig.maxRetries,
|
||||
retryDelay: queueConfig.retryDelay,
|
||||
concurrent: 1,
|
||||
// Retry exponential backoff
|
||||
afterProcessDelay: 100,
|
||||
|
||||
@@ -50,6 +50,24 @@ export class SMTPClient {
|
||||
const transport = this.getTransport(provider);
|
||||
|
||||
try {
|
||||
// Validate and sanitize attachments
|
||||
const sanitizedAttachments = (emailData.attachments || []).map((att) => {
|
||||
// Ensure content is a Buffer or string
|
||||
if (att.content && typeof att.content === 'object' && !Buffer.isBuffer(att.content)) {
|
||||
this.logger.warn('Invalid attachment content type, skipping', {
|
||||
filename: att.filename,
|
||||
type: typeof att.content,
|
||||
});
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
filename: att.filename || 'attachment',
|
||||
content: att.content,
|
||||
contentType: att.contentType || 'application/octet-stream',
|
||||
encoding: att.encoding || 'base64',
|
||||
};
|
||||
}).filter(Boolean); // Remove null entries
|
||||
|
||||
// Prepare email
|
||||
const mailOptions = {
|
||||
from: provider.from,
|
||||
@@ -58,7 +76,7 @@ export class SMTPClient {
|
||||
text: emailData.text,
|
||||
html: emailData.html,
|
||||
headers: emailData.headers || {},
|
||||
attachments: emailData.attachments || [],
|
||||
attachments: sanitizedAttachments,
|
||||
|
||||
// Reply-to original sender
|
||||
replyTo: emailData.envelope.from,
|
||||
|
||||
@@ -119,7 +119,8 @@ export class IncomingSMTPServer {
|
||||
content: att.content,
|
||||
contentType: att.contentType,
|
||||
contentDisposition: att.contentDisposition,
|
||||
size: att.size,
|
||||
contentId: att.contentId,
|
||||
encoding: 'base64',
|
||||
})) || [],
|
||||
messageId: parsed.messageId,
|
||||
date: parsed.date,
|
||||
@@ -135,6 +136,20 @@ export class IncomingSMTPServer {
|
||||
throw new Error('Missing recipient address');
|
||||
}
|
||||
|
||||
// Check for email loops (sender sending to themselves)
|
||||
const loopRecipients = emailData.envelope.to.filter(
|
||||
(recipient) => recipient.toLowerCase() === emailData.envelope.from.toLowerCase()
|
||||
);
|
||||
|
||||
if (loopRecipients.length > 0) {
|
||||
this.logger.warn('Email loop detected - dropping email', {
|
||||
from: emailData.envelope.from,
|
||||
to: emailData.envelope.to,
|
||||
loopRecipients,
|
||||
});
|
||||
throw new Error('Email loop detected: sender cannot send to themselves');
|
||||
}
|
||||
|
||||
this.logger.debug('Email parsed successfully', {
|
||||
from: emailData.envelope.from,
|
||||
to: emailData.envelope.to,
|
||||
|
||||
Reference in New Issue
Block a user