import { expect, tap } from '@git.zone/tstest/tapbundle'; import * as plugins from './plugins.js'; import { createTestServer } from '../../helpers/server.loader.js'; import { createSmtpClient } from '../../helpers/smtp.client.js'; tap.test('CSEC-10: should handle anti-spam measures correctly', async (tools) => { const testId = 'CSEC-10-anti-spam-measures'; console.log(`\n${testId}: Testing anti-spam measure handling...`); let scenarioCount = 0; // Scenario 1: Reputation-based filtering await (async () => { scenarioCount++; console.log(`\nScenario ${scenarioCount}: Testing reputation-based filtering`); const ipReputation = new Map([ ['127.0.0.1', { score: 100, status: 'trusted' }], ['10.0.0.1', { score: 50, status: 'neutral' }], ['192.168.1.100', { score: 10, status: 'suspicious' }], ['10.10.10.10', { score: 0, status: 'blocked' }] ]); const testServer = await createTestServer({ onConnection: async (socket) => { const clientIP = socket.remoteAddress || '127.0.0.1'; const reputation = ipReputation.get(clientIP) || { score: 50, status: 'unknown' }; console.log(` [Server] Client ${clientIP} connected (reputation: ${reputation.status})`); if (reputation.score === 0) { socket.write('554 5.7.1 Your IP has been blocked due to poor reputation\r\n'); socket.end(); return; } socket.write('220 reputation.example.com ESMTP\r\n'); socket.on('data', (data) => { const command = data.toString().trim(); console.log(` [Server] Received: ${command}`); if (command.startsWith('EHLO')) { socket.write('250-reputation.example.com\r\n'); if (reputation.score < 30) { // Suspicious IPs get limited features socket.write('250 OK\r\n'); } else { socket.write('250-SIZE 10485760\r\n'); socket.write('250-AUTH PLAIN LOGIN\r\n'); socket.write('250 OK\r\n'); } } else if (command.startsWith('MAIL FROM:')) { if (reputation.score < 30) { // Add delay for suspicious IPs (tarpitting) setTimeout(() => { socket.write('250 OK\r\n'); }, 2000); } else { socket.write('250 OK\r\n'); } } else if (command.startsWith('RCPT TO:')) { socket.write('250 OK\r\n'); } else if (command === 'DATA') { socket.write('354 Start mail input\r\n'); } else if (command === '.') { socket.write(`250 OK: Message accepted (reputation score: ${reputation.score})\r\n`); } else if (command === 'QUIT') { socket.write('221 Bye\r\n'); socket.end(); } }); } }); // Test with good reputation (localhost) const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false }); const email = new plugins.smartmail.Email({ from: 'sender@example.com', to: ['recipient@example.com'], subject: 'Reputation test', text: 'Testing reputation-based filtering' }); const result = await smtpClient.sendMail(email); console.log(' Good reputation: Message accepted'); expect(result).toBeDefined(); expect(result.response).toContain('reputation score: 100'); await testServer.server.close(); })(); // Scenario 2: Content filtering and spam scoring await (async () => { scenarioCount++; console.log(`\nScenario ${scenarioCount}: Testing content filtering and spam scoring`); const spamKeywords = [ { word: 'viagra', score: 5 }, { word: 'lottery', score: 4 }, { word: 'winner', score: 3 }, { word: 'click here', score: 3 }, { word: 'free money', score: 5 }, { word: 'guarantee', score: 2 }, { word: 'act now', score: 3 }, { word: '100% free', score: 4 } ]; const testServer = await createTestServer({ onConnection: async (socket) => { console.log(' [Server] Client connected'); socket.write('220 content-filter.example.com ESMTP\r\n'); let inData = false; let messageContent = ''; socket.on('data', (data) => { const text = data.toString(); if (inData) { messageContent += text; if (text.includes('\r\n.\r\n')) { inData = false; // Calculate spam score let spamScore = 0; const lowerContent = messageContent.toLowerCase(); spamKeywords.forEach(({ word, score }) => { if (lowerContent.includes(word)) { spamScore += score; console.log(` [Server] Found spam keyword: "${word}" (+${score})`); } }); // Check for suspicious patterns if ((messageContent.match(/!/g) || []).length > 5) { spamScore += 2; console.log(' [Server] Excessive exclamation marks (+2)'); } if ((messageContent.match(/\$|€|£/g) || []).length > 3) { spamScore += 2; console.log(' [Server] Multiple currency symbols (+2)'); } if (messageContent.includes('ALL CAPS') || /[A-Z]{10,}/.test(messageContent)) { spamScore += 1; console.log(' [Server] Excessive capitals (+1)'); } console.log(` [Server] Total spam score: ${spamScore}`); if (spamScore >= 10) { socket.write('550 5.7.1 Message rejected due to spam content\r\n'); } else if (spamScore >= 5) { socket.write('250 OK: Message quarantined for review\r\n'); } else { socket.write('250 OK: Message accepted\r\n'); } messageContent = ''; } return; } const command = text.trim(); console.log(` [Server] Received: ${command}`); if (command.startsWith('EHLO')) { socket.write('250-content-filter.example.com\r\n'); socket.write('250 OK\r\n'); } else if (command.startsWith('MAIL FROM:')) { socket.write('250 OK\r\n'); } else if (command.startsWith('RCPT TO:')) { socket.write('250 OK\r\n'); } else if (command === 'DATA') { socket.write('354 Start mail input\r\n'); inData = true; } else if (command === 'QUIT') { socket.write('221 Bye\r\n'); socket.end(); } }); } }); const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false }); // Test 1: Clean email const cleanEmail = new plugins.smartmail.Email({ from: 'sender@example.com', to: ['recipient@example.com'], subject: 'Business proposal', text: 'I would like to discuss our upcoming project. Please let me know your availability.' }); const cleanResult = await smtpClient.sendMail(cleanEmail); console.log(' Clean email: Accepted'); expect(cleanResult.response).toContain('Message accepted'); // Test 2: Suspicious email const suspiciousEmail = new plugins.smartmail.Email({ from: 'sender@example.com', to: ['recipient@example.com'], subject: 'You are a WINNER!', text: 'Click here to claim your lottery prize! Act now! 100% guarantee!' }); const suspiciousResult = await smtpClient.sendMail(suspiciousEmail); console.log(' Suspicious email: Quarantined'); expect(suspiciousResult.response).toContain('quarantined'); // Test 3: Spam email const spamEmail = new plugins.smartmail.Email({ from: 'sender@example.com', to: ['recipient@example.com'], subject: 'FREE MONEY - VIAGRA - LOTTERY WINNER!!!', text: 'CLICK HERE NOW!!! 100% FREE VIAGRA!!! You are a LOTTERY WINNER!!! Act now to claim your FREE MONEY!!! $$$€€€£££' }); try { await smtpClient.sendMail(spamEmail); console.log(' Unexpected: Spam email accepted'); } catch (error) { console.log(' Spam email: Rejected'); expect(error.message).toContain('spam content'); } await testServer.server.close(); })(); // Scenario 3: Greylisting await (async () => { scenarioCount++; console.log(`\nScenario ${scenarioCount}: Testing greylisting`); const greylist = new Map(); const greylistDuration = 2000; // 2 seconds for testing const testServer = await createTestServer({ onConnection: async (socket) => { console.log(' [Server] Client connected'); socket.write('220 greylist.example.com ESMTP\r\n'); let triplet = ''; socket.on('data', (data) => { const command = data.toString().trim(); console.log(` [Server] Received: ${command}`); if (command.startsWith('EHLO')) { socket.write('250-greylist.example.com\r\n'); socket.write('250 OK\r\n'); } else if (command.startsWith('MAIL FROM:')) { const from = command.match(/<(.+)>/)?.[1] || ''; triplet = `${socket.remoteAddress}-${from}`; socket.write('250 OK\r\n'); } else if (command.startsWith('RCPT TO:')) { const to = command.match(/<(.+)>/)?.[1] || ''; triplet += `-${to}`; const now = Date.now(); const greylistEntry = greylist.get(triplet); if (!greylistEntry) { // First time seeing this triplet greylist.set(triplet, { firstSeen: now, attempts: 1 }); console.log(' [Server] New sender - greylisting'); socket.write('451 4.7.1 Greylisting in effect, please retry later\r\n'); } else { greylistEntry.attempts++; const elapsed = now - greylistEntry.firstSeen; if (elapsed < greylistDuration) { console.log(` [Server] Too soon (${elapsed}ms) - still greylisted`); socket.write('451 4.7.1 Greylisting in effect, please retry later\r\n'); } else { console.log(` [Server] Greylist passed (${greylistEntry.attempts} attempts)`); socket.write('250 OK\r\n'); } } } else if (command === 'DATA') { socket.write('354 Start mail input\r\n'); } else if (command === '.') { socket.write('250 OK: Message accepted after greylisting\r\n'); } else if (command === 'QUIT') { socket.write('221 Bye\r\n'); socket.end(); } }); } }); const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false }); const email = new plugins.smartmail.Email({ from: 'sender@example.com', to: ['recipient@example.com'], subject: 'Greylist test', text: 'Testing greylisting mechanism' }); // First attempt - should be greylisted try { await smtpClient.sendMail(email); console.log(' Unexpected: First attempt succeeded'); } catch (error) { console.log(' First attempt: Greylisted as expected'); expect(error.message).toContain('Greylisting'); } // Wait and retry console.log(` Waiting ${greylistDuration}ms before retry...`); await new Promise(resolve => setTimeout(resolve, greylistDuration + 100)); // Second attempt - should succeed const retryResult = await smtpClient.sendMail(email); console.log(' Retry attempt: Accepted after greylist period'); expect(retryResult).toBeDefined(); expect(retryResult.response).toContain('after greylisting'); await testServer.server.close(); })(); // Scenario 4: DNS blacklist (DNSBL) checking await (async () => { scenarioCount++; console.log(`\nScenario ${scenarioCount}: Testing DNSBL checking`); const blacklistedIPs = ['192.168.1.100', '10.0.0.50']; const blacklistedDomains = ['spam-domain.com', 'phishing-site.net']; const testServer = await createTestServer({ onConnection: async (socket) => { const clientIP = socket.remoteAddress || ''; console.log(` [Server] Client connected from ${clientIP}`); // Simulate DNSBL check const isBlacklisted = blacklistedIPs.some(ip => clientIP.includes(ip)); if (isBlacklisted) { console.log(' [Server] IP found in DNSBL'); socket.write('554 5.7.1 Your IP is listed in DNSBL\r\n'); socket.end(); return; } socket.write('220 dnsbl.example.com ESMTP\r\n'); socket.on('data', (data) => { const command = data.toString().trim(); console.log(` [Server] Received: ${command}`); if (command.startsWith('EHLO')) { const domain = command.split(' ')[1]; if (blacklistedDomains.includes(domain)) { console.log(' [Server] HELO domain in DNSBL'); socket.write('554 5.7.1 Your domain is blacklisted\r\n'); socket.end(); return; } socket.write('250-dnsbl.example.com\r\n'); socket.write('250 OK\r\n'); } else if (command.startsWith('MAIL FROM:')) { const fromAddress = command.match(/<(.+)>/)?.[1] || ''; const fromDomain = fromAddress.split('@')[1]; if (blacklistedDomains.includes(fromDomain)) { console.log(' [Server] Sender domain in DNSBL'); socket.write('554 5.7.1 Sender domain is blacklisted\r\n'); } else { socket.write('250 OK\r\n'); } } else if (command.startsWith('RCPT TO:')) { socket.write('250 OK\r\n'); } else if (command === 'DATA') { socket.write('354 Start mail input\r\n'); } else if (command === '.') { socket.write('250 OK\r\n'); } else if (command === 'QUIT') { socket.write('221 Bye\r\n'); socket.end(); } }); } }); const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false }); // Test with clean sender const cleanEmail = new plugins.smartmail.Email({ from: 'sender@clean-domain.com', to: ['recipient@example.com'], subject: 'DNSBL test', text: 'Testing DNSBL checking' }); const result = await smtpClient.sendMail(cleanEmail); console.log(' Clean sender: Accepted'); expect(result).toBeDefined(); // Test with blacklisted domain const blacklistedEmail = new plugins.smartmail.Email({ from: 'sender@spam-domain.com', to: ['recipient@example.com'], subject: 'Blacklisted domain test', text: 'Testing from blacklisted domain' }); try { await smtpClient.sendMail(blacklistedEmail); console.log(' Unexpected: Blacklisted domain accepted'); } catch (error) { console.log(' Blacklisted domain: Rejected'); expect(error.message).toContain('blacklisted'); } await testServer.server.close(); })(); // Scenario 5: Connection behavior analysis await (async () => { scenarioCount++; console.log(`\nScenario ${scenarioCount}: Testing connection behavior analysis`); const testServer = await createTestServer({ onConnection: async (socket) => { console.log(' [Server] Client connected'); const connectionStart = Date.now(); let commandCount = 0; let errorCount = 0; let rapidCommands = 0; let lastCommandTime = Date.now(); // Set initial timeout socket.setTimeout(30000); // 30 seconds socket.write('220 behavior.example.com ESMTP\r\n'); socket.on('data', (data) => { const now = Date.now(); const timeSinceLastCommand = now - lastCommandTime; lastCommandTime = now; commandCount++; // Check for rapid-fire commands (bot behavior) if (timeSinceLastCommand < 50) { rapidCommands++; if (rapidCommands > 5) { console.log(' [Server] Detected rapid-fire commands (bot behavior)'); socket.write('421 4.7.0 Suspicious behavior detected\r\n'); socket.end(); return; } } else { rapidCommands = 0; // Reset counter } const command = data.toString().trim(); console.log(` [Server] Received: ${command} (${timeSinceLastCommand}ms since last)`); // Check for invalid commands (spam bot behavior) if (!command.match(/^(EHLO|HELO|MAIL FROM:|RCPT TO:|DATA|QUIT|RSET|NOOP|AUTH|\.)/i)) { errorCount++; if (errorCount > 3) { console.log(' [Server] Too many invalid commands'); socket.write('421 4.7.0 Too many errors\r\n'); socket.end(); return; } socket.write('500 5.5.1 Command not recognized\r\n'); return; } if (command.startsWith('EHLO') || command.startsWith('HELO')) { socket.write('250-behavior.example.com\r\n'); socket.write('250 OK\r\n'); } else if (command.startsWith('MAIL FROM:')) { socket.write('250 OK\r\n'); } else if (command.startsWith('RCPT TO:')) { socket.write('250 OK\r\n'); } else if (command === 'DATA') { socket.write('354 Start mail input\r\n'); } else if (command === '.') { const connectionDuration = Date.now() - connectionStart; console.log(` [Server] Session duration: ${connectionDuration}ms, commands: ${commandCount}`); socket.write('250 OK: Message accepted\r\n'); } else if (command === 'QUIT') { socket.write('221 Bye\r\n'); socket.end(); } else if (command === 'NOOP') { socket.write('250 OK\r\n'); } }); socket.on('timeout', () => { console.log(' [Server] Connection timeout - possible spam bot'); socket.write('421 4.4.2 Connection timeout\r\n'); socket.end(); }); } }); // Test normal behavior const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false }); const email = new plugins.smartmail.Email({ from: 'sender@example.com', to: ['recipient@example.com'], subject: 'Behavior test', text: 'Testing normal email sending behavior' }); const result = await smtpClient.sendMail(email); console.log(' Normal behavior: Accepted'); expect(result).toBeDefined(); await testServer.server.close(); })(); // Scenario 6: Attachment and link scanning await (async () => { scenarioCount++; console.log(`\nScenario ${scenarioCount}: Testing attachment and link scanning`); const dangerousExtensions = ['.exe', '.scr', '.vbs', '.com', '.bat', '.cmd', '.pif']; const suspiciousLinks = ['bit.ly', 'tinyurl.com', 'short.link']; const testServer = await createTestServer({ onConnection: async (socket) => { console.log(' [Server] Client connected'); socket.write('220 scanner.example.com ESMTP\r\n'); let inData = false; let messageContent = ''; socket.on('data', (data) => { const text = data.toString(); if (inData) { messageContent += text; if (text.includes('\r\n.\r\n')) { inData = false; let threatLevel = 0; const threats: string[] = []; // Check for dangerous attachments const attachmentMatch = messageContent.match(/filename="([^"]+)"/gi); if (attachmentMatch) { attachmentMatch.forEach(match => { const filename = match.match(/filename="([^"]+)"/i)?.[1] || ''; const extension = filename.substring(filename.lastIndexOf('.')).toLowerCase(); if (dangerousExtensions.includes(extension)) { threatLevel += 10; threats.push(`Dangerous attachment: ${filename}`); console.log(` [Server] Found dangerous attachment: ${filename}`); } }); } // Check for suspicious links const urlMatch = messageContent.match(/https?:\/\/[^\s]+/gi); if (urlMatch) { urlMatch.forEach(url => { if (suspiciousLinks.some(domain => url.includes(domain))) { threatLevel += 5; threats.push(`Suspicious link: ${url}`); console.log(` [Server] Found suspicious link: ${url}`); } }); } // Check for phishing patterns if (messageContent.includes('verify your account') && urlMatch) { threatLevel += 5; threats.push('Possible phishing attempt'); } console.log(` [Server] Threat level: ${threatLevel}`); if (threatLevel >= 10) { socket.write(`550 5.7.1 Message rejected: ${threats.join(', ')}\r\n`); } else if (threatLevel >= 5) { socket.write('250 OK: Message flagged for review\r\n'); } else { socket.write('250 OK: Message scanned and accepted\r\n'); } messageContent = ''; } return; } const command = text.trim(); console.log(` [Server] Received: ${command}`); if (command.startsWith('EHLO')) { socket.write('250-scanner.example.com\r\n'); socket.write('250-SIZE 10485760\r\n'); socket.write('250 OK\r\n'); } else if (command.startsWith('MAIL FROM:')) { socket.write('250 OK\r\n'); } else if (command.startsWith('RCPT TO:')) { socket.write('250 OK\r\n'); } else if (command === 'DATA') { socket.write('354 Start mail input\r\n'); inData = true; } else if (command === 'QUIT') { socket.write('221 Bye\r\n'); socket.end(); } }); } }); const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false }); // Test 1: Clean email with safe attachment const safeEmail = new plugins.smartmail.Email({ from: 'sender@example.com', to: ['recipient@example.com'], subject: 'Document for review', text: 'Please find the attached document.', attachments: [{ filename: 'report.pdf', content: 'PDF content here' }] }); const safeResult = await smtpClient.sendMail(safeEmail); console.log(' Safe email: Scanned and accepted'); expect(safeResult.response).toContain('scanned and accepted'); // Test 2: Email with suspicious link const suspiciousEmail = new plugins.smartmail.Email({ from: 'sender@example.com', to: ['recipient@example.com'], subject: 'Check this out', text: 'Click here: https://bit.ly/abc123 to verify your account', html: '

Click here to verify your account

' }); const suspiciousResult = await smtpClient.sendMail(suspiciousEmail); console.log(' Suspicious email: Flagged for review'); expect(suspiciousResult.response).toContain('flagged for review'); // Test 3: Email with dangerous attachment const dangerousEmail = new plugins.smartmail.Email({ from: 'sender@example.com', to: ['recipient@example.com'], subject: 'Important update', text: 'Please run the attached file', attachments: [{ filename: 'update.exe', content: Buffer.from('MZ\x90\x00\x03') // Fake executable header }] }); try { await smtpClient.sendMail(dangerousEmail); console.log(' Unexpected: Dangerous attachment accepted'); } catch (error) { console.log(' Dangerous attachment: Rejected'); expect(error.message).toContain('Dangerous attachment'); } await testServer.server.close(); })(); console.log(`\n${testId}: All ${scenarioCount} anti-spam scenarios tested ✓`); });