import { tap, expect } from '@git.zone/tstest/tapbundle'; import * as plugins from '../../../ts/plugins.js'; import * as net from 'net'; import { startTestServer, stopTestServer } from '../../helpers/server.loader.js' import type { ITestServer } from '../../helpers/server.loader.js'; const TEST_PORT = 2525; let testServer: ITestServer; // Helper to wait for SMTP response const waitForResponse = (socket: net.Socket, expectedCode?: string, timeout = 5000): Promise => { return new Promise((resolve, reject) => { let buffer = ''; const timer = setTimeout(() => { socket.removeListener('data', handler); reject(new Error(`Timeout waiting for ${expectedCode || 'any'} response`)); }, timeout); const handler = (data: Buffer) => { buffer += data.toString(); const lines = buffer.split('\r\n'); for (const line of lines) { if (expectedCode) { if (line.startsWith(expectedCode + ' ')) { clearTimeout(timer); socket.removeListener('data', handler); resolve(buffer); return; } } else { // Look for any complete response if (line.match(/^\d{3} /)) { clearTimeout(timer); socket.removeListener('data', handler); resolve(buffer); return; } } } }; socket.on('data', handler); }); }; tap.test('setup - start test server', async (toolsArg) => { testServer = await startTestServer({ port: TEST_PORT }); await toolsArg.delayFor(1000); }); tap.test('IP Reputation - Suspicious hostname in EHLO', async (tools) => { const socket = net.createConnection({ host: 'localhost', port: TEST_PORT, timeout: 30000 }); try { // Wait for greeting const greeting = await waitForResponse(socket, '220'); console.log('Server response:', greeting); // Use suspicious hostname socket.write('EHLO suspicious-host.badreputation.com\r\n'); const ehloResponse = await waitForResponse(socket); console.log('Server response:', ehloResponse); const accepted = ehloResponse.includes('250'); const rejected = ehloResponse.includes('550') || ehloResponse.includes('521'); console.log(`Suspicious hostname: accepted=${accepted}, rejected=${rejected}`); expect(accepted || rejected).toEqual(true); if (rejected) { console.log('IP reputation check working - suspicious host rejected at EHLO'); } socket.write('QUIT\r\n'); await waitForResponse(socket, '221'); } finally { socket.destroy(); } }); tap.test('IP Reputation - Blacklisted sender domain', async (tools) => { const socket = net.createConnection({ host: 'localhost', port: TEST_PORT, timeout: 30000 }); try { // Wait for greeting const greeting = await waitForResponse(socket, '220'); console.log('Server response:', greeting); // Send EHLO socket.write('EHLO testclient\r\n'); const ehloResponse = await waitForResponse(socket, '250'); console.log('Server response:', ehloResponse); // Use known spam/blacklisted domain socket.write('MAIL FROM:\r\n'); const mailResponse = await waitForResponse(socket); console.log('Server response:', mailResponse); if (mailResponse.includes('250')) { console.log('Blacklisted sender accepted at MAIL FROM'); // Try RCPT TO socket.write('RCPT TO:\r\n'); const rcptResponse = await waitForResponse(socket); console.log('Server response:', rcptResponse); const accepted = rcptResponse.includes('250'); const rejected = rcptResponse.includes('550') || rcptResponse.includes('553'); console.log(`Blacklisted domain at RCPT: accepted=${accepted}, rejected=${rejected}`); expect(accepted || rejected).toEqual(true); } else if (mailResponse.includes('550') || mailResponse.includes('553')) { console.log('Blacklisted sender rejected - IP reputation check working'); expect(true).toEqual(true); } socket.write('QUIT\r\n'); await waitForResponse(socket, '221'); } finally { socket.destroy(); } }); tap.test('IP Reputation - Known good sender', async (tools) => { const socket = net.createConnection({ host: 'localhost', port: TEST_PORT, timeout: 30000 }); try { // Wait for greeting const greeting = await waitForResponse(socket, '220'); console.log('Server response:', greeting); // Send EHLO socket.write('EHLO localhost\r\n'); const ehloResponse = await waitForResponse(socket, '250'); console.log('Server response:', ehloResponse); // Use legitimate sender socket.write('MAIL FROM:\r\n'); const mailResponse = await waitForResponse(socket, '250'); console.log('Server response:', mailResponse); // Send RCPT TO socket.write('RCPT TO:\r\n'); const rcptResponse = await waitForResponse(socket, '250'); console.log('Server response:', rcptResponse); console.log('Good sender accepted - IP reputation allows legitimate senders'); expect(true).toEqual(true); socket.write('QUIT\r\n'); await waitForResponse(socket, '221'); } finally { socket.destroy(); } }); tap.test('IP Reputation - Multiple connections from same IP', async (tools) => { const connections: net.Socket[] = []; const totalConnections = 3; const connectionResults: Promise[] = []; // Create multiple connections rapidly for (let i = 0; i < totalConnections; i++) { const connectionPromise = (async () => { const socket = net.createConnection({ host: 'localhost', port: TEST_PORT, timeout: 30000 }); connections.push(socket); try { // Wait for greeting const greeting = await waitForResponse(socket, '220'); console.log(`Connection ${i + 1} response:`, greeting); // Send EHLO socket.write('EHLO testclient\r\n'); const ehloResponse = await waitForResponse(socket); console.log(`Connection ${i + 1} response:`, ehloResponse); if (ehloResponse.includes('250')) { socket.write('QUIT\r\n'); await waitForResponse(socket, '221'); } else if (ehloResponse.includes('421') || ehloResponse.includes('550')) { // Connection rejected due to rate limiting or reputation console.log(`Connection ${i + 1} rejected - IP reputation/rate limiting active`); } } catch (err: any) { console.error(`Connection ${i + 1} error:`, err.message); } finally { socket.destroy(); } })(); connectionResults.push(connectionPromise); // Small delay between connections if (i < totalConnections - 1) { await tools.delayFor(100); } } // Wait for all connections to complete await Promise.all(connectionResults); console.log('All connections completed'); expect(true).toEqual(true); }); tap.test('IP Reputation - Suspicious patterns in email', async (tools) => { const socket = net.createConnection({ host: 'localhost', port: TEST_PORT, timeout: 30000 }); try { // Wait for greeting const greeting = await waitForResponse(socket, '220'); console.log('Server response:', greeting); // Send EHLO socket.write('EHLO testclient\r\n'); const ehloResponse = await waitForResponse(socket, '250'); console.log('Server response:', ehloResponse); // Send MAIL FROM socket.write('MAIL FROM:\r\n'); const mailResponse = await waitForResponse(socket, '250'); console.log('Server response:', mailResponse); // Multiple recipients (spam pattern) socket.write('RCPT TO:\r\n'); const rcpt1Response = await waitForResponse(socket, '250'); console.log('Server response:', rcpt1Response); socket.write('RCPT TO:\r\n'); const rcpt2Response = await waitForResponse(socket, '250'); console.log('Server response:', rcpt2Response); socket.write('RCPT TO:\r\n'); const rcpt3Response = await waitForResponse(socket); console.log('Server response:', rcpt3Response); if (rcpt3Response.includes('250')) { // Send DATA socket.write('DATA\r\n'); const dataResponse = await waitForResponse(socket, '354'); console.log('Server response:', dataResponse); // Email with spam-like content const email = [ `From: sender@example.com`, `To: recipient1@example.com, recipient2@example.com, recipient3@example.com`, `Subject: URGENT!!! You've won $1,000,000!!!`, `Date: ${new Date().toUTCString()}`, `Message-ID: `, '', 'CLICK HERE NOW!!! Limited time offer!!!', 'Visit http://suspicious-link.com/win-money', 'Act NOW before it\'s too late!!!', '.', '' ].join('\r\n'); socket.write(email); const emailResponse = await waitForResponse(socket); console.log('Server response:', emailResponse); const result = emailResponse.includes('250') ? 'accepted' : 'rejected'; console.log(`Suspicious content email ${result}`); expect(true).toEqual(true); } else if (rcpt3Response.includes('452') || rcpt3Response.includes('550')) { console.log('Multiple recipients limited - reputation control active'); expect(true).toEqual(true); } socket.write('QUIT\r\n'); await waitForResponse(socket, '221'); } finally { socket.destroy(); } }); tap.test('cleanup - stop test server', async () => { await stopTestServer(testServer); }); tap.start();