261 lines
7.1 KiB
TypeScript
261 lines
7.1 KiB
TypeScript
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
|
import { startTestServer, stopTestServer, type ITestServer } from '../../helpers/server.loader.js';
|
|
import { createSmtpClient } from '../../../ts/mail/delivery/smtpclient/index.js';
|
|
import { Email } from '../../../ts/mail/core/classes.email.js';
|
|
import * as net from 'net';
|
|
|
|
let testServer: ITestServer;
|
|
|
|
tap.test('setup - start SMTP server for rate limiting tests', async () => {
|
|
testServer = await startTestServer({
|
|
port: 2578,
|
|
tlsEnabled: false,
|
|
authRequired: false
|
|
});
|
|
|
|
expect(testServer.port).toEqual(2578);
|
|
});
|
|
|
|
tap.test('CERR-08: Server rate limiting - 421 too many connections', async () => {
|
|
// Create server that immediately rejects with rate limit
|
|
const rateLimitServer = net.createServer((socket) => {
|
|
socket.write('421 4.7.0 Too many connections, please try again later\r\n');
|
|
socket.end();
|
|
});
|
|
|
|
await new Promise<void>((resolve) => {
|
|
rateLimitServer.listen(2579, () => resolve());
|
|
});
|
|
|
|
const smtpClient = await createSmtpClient({
|
|
host: '127.0.0.1',
|
|
port: 2579,
|
|
secure: false,
|
|
connectionTimeout: 5000
|
|
});
|
|
|
|
const result = await smtpClient.verify();
|
|
|
|
expect(result).toBeFalse();
|
|
console.log('✅ 421 rate limit response handled');
|
|
|
|
await smtpClient.close();
|
|
await new Promise<void>((resolve) => {
|
|
rateLimitServer.close(() => resolve());
|
|
});
|
|
});
|
|
|
|
tap.test('CERR-08: Message rate limiting - 452', async () => {
|
|
// Create server that rate limits at MAIL FROM
|
|
const messageRateServer = net.createServer((socket) => {
|
|
socket.write('220 Message Rate Server\r\n');
|
|
|
|
let buffer = '';
|
|
|
|
socket.on('data', (data) => {
|
|
buffer += data.toString();
|
|
|
|
let lines = buffer.split('\r\n');
|
|
buffer = lines.pop() || '';
|
|
|
|
for (const line of lines) {
|
|
const command = line.trim();
|
|
if (!command) continue;
|
|
|
|
if (command.startsWith('EHLO')) {
|
|
socket.write('250 OK\r\n');
|
|
} else if (command.startsWith('MAIL FROM')) {
|
|
socket.write('452 4.3.2 Too many messages sent, please try later\r\n');
|
|
} else if (command === 'QUIT') {
|
|
socket.write('221 Bye\r\n');
|
|
socket.end();
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
await new Promise<void>((resolve) => {
|
|
messageRateServer.listen(2580, () => resolve());
|
|
});
|
|
|
|
const smtpClient = await createSmtpClient({
|
|
host: '127.0.0.1',
|
|
port: 2580,
|
|
secure: false,
|
|
connectionTimeout: 5000
|
|
});
|
|
|
|
const email = new Email({
|
|
from: 'sender@example.com',
|
|
to: 'recipient@example.com',
|
|
subject: 'Rate Limit Test',
|
|
text: 'Testing rate limiting'
|
|
});
|
|
|
|
const result = await smtpClient.sendMail(email);
|
|
|
|
expect(result.success).toBeFalse();
|
|
console.log('Actual error:', result.error?.message);
|
|
expect(result.error?.message).toMatch(/452|many|messages|rate/i);
|
|
console.log('✅ 452 message rate limit handled');
|
|
|
|
await smtpClient.close();
|
|
await new Promise<void>((resolve) => {
|
|
messageRateServer.close(() => resolve());
|
|
});
|
|
});
|
|
|
|
tap.test('CERR-08: User rate limiting - 550', async () => {
|
|
// Create server that permanently blocks user
|
|
const userRateServer = net.createServer((socket) => {
|
|
socket.write('220 User Rate Server\r\n');
|
|
|
|
let buffer = '';
|
|
|
|
socket.on('data', (data) => {
|
|
buffer += data.toString();
|
|
|
|
let lines = buffer.split('\r\n');
|
|
buffer = lines.pop() || '';
|
|
|
|
for (const line of lines) {
|
|
const command = line.trim();
|
|
if (!command) continue;
|
|
|
|
if (command.startsWith('EHLO')) {
|
|
socket.write('250 OK\r\n');
|
|
} else if (command.startsWith('MAIL FROM')) {
|
|
if (command.includes('blocked@')) {
|
|
socket.write('550 5.7.1 User sending rate exceeded\r\n');
|
|
} else {
|
|
socket.write('250 OK\r\n');
|
|
}
|
|
} else if (command.startsWith('RCPT TO')) {
|
|
socket.write('250 OK\r\n');
|
|
} else if (command === 'QUIT') {
|
|
socket.write('221 Bye\r\n');
|
|
socket.end();
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
await new Promise<void>((resolve) => {
|
|
userRateServer.listen(2581, () => resolve());
|
|
});
|
|
|
|
const smtpClient = await createSmtpClient({
|
|
host: '127.0.0.1',
|
|
port: 2581,
|
|
secure: false,
|
|
connectionTimeout: 5000
|
|
});
|
|
|
|
const email = new Email({
|
|
from: 'blocked@example.com',
|
|
to: 'recipient@example.com',
|
|
subject: 'User Rate Test',
|
|
text: 'Testing user rate limiting'
|
|
});
|
|
|
|
const result = await smtpClient.sendMail(email);
|
|
|
|
expect(result.success).toBeFalse();
|
|
console.log('Actual error:', result.error?.message);
|
|
expect(result.error?.message).toMatch(/550|rate|exceeded/i);
|
|
console.log('✅ 550 user rate limit handled');
|
|
|
|
await smtpClient.close();
|
|
await new Promise<void>((resolve) => {
|
|
userRateServer.close(() => resolve());
|
|
});
|
|
});
|
|
|
|
tap.test('CERR-08: Connection throttling - delayed response', async () => {
|
|
// Create server that delays responses to simulate throttling
|
|
const throttleServer = net.createServer((socket) => {
|
|
// Delay initial greeting
|
|
setTimeout(() => {
|
|
socket.write('220 Throttle Server\r\n');
|
|
}, 100);
|
|
|
|
let buffer = '';
|
|
|
|
socket.on('data', (data) => {
|
|
buffer += data.toString();
|
|
|
|
let lines = buffer.split('\r\n');
|
|
buffer = lines.pop() || '';
|
|
|
|
for (const line of lines) {
|
|
const command = line.trim();
|
|
if (!command) continue;
|
|
|
|
// Add delay to all responses
|
|
setTimeout(() => {
|
|
if (command.startsWith('EHLO')) {
|
|
socket.write('250 OK\r\n');
|
|
} else if (command === 'QUIT') {
|
|
socket.write('221 Bye\r\n');
|
|
socket.end();
|
|
} else {
|
|
socket.write('250 OK\r\n');
|
|
}
|
|
}, 50);
|
|
}
|
|
});
|
|
});
|
|
|
|
await new Promise<void>((resolve) => {
|
|
throttleServer.listen(2582, () => resolve());
|
|
});
|
|
|
|
const smtpClient = await createSmtpClient({
|
|
host: '127.0.0.1',
|
|
port: 2582,
|
|
secure: false,
|
|
connectionTimeout: 5000
|
|
});
|
|
|
|
const startTime = Date.now();
|
|
const result = await smtpClient.verify();
|
|
const duration = Date.now() - startTime;
|
|
|
|
expect(result).toBeTrue();
|
|
console.log(`✅ Throttled connection succeeded in ${duration}ms`);
|
|
|
|
await smtpClient.close();
|
|
await new Promise<void>((resolve) => {
|
|
throttleServer.close(() => resolve());
|
|
});
|
|
});
|
|
|
|
tap.test('CERR-08: Normal email without rate limiting', async () => {
|
|
// Test successful email send with working server
|
|
const smtpClient = await createSmtpClient({
|
|
host: testServer.hostname,
|
|
port: testServer.port,
|
|
secure: false,
|
|
connectionTimeout: 5000
|
|
});
|
|
|
|
const email = new Email({
|
|
from: 'sender@example.com',
|
|
to: 'recipient@example.com',
|
|
subject: 'Normal Test',
|
|
text: 'Testing normal operation without rate limits'
|
|
});
|
|
|
|
const result = await smtpClient.sendMail(email);
|
|
|
|
expect(result.success).toBeTrue();
|
|
console.log('✅ Normal email sent successfully');
|
|
|
|
await smtpClient.close();
|
|
});
|
|
|
|
tap.test('cleanup - stop SMTP server', async () => {
|
|
await stopTestServer(testServer);
|
|
});
|
|
|
|
export default tap.start(); |