import { tap, expect } from '@push.rocks/tapbundle'; import { IPReputationChecker, ReputationThreshold, IPType } from '../ts/security/classes.ipreputationchecker.js'; import * as plugins from '../ts/plugins.js'; // Mock for dns lookup const originalDnsResolve = plugins.dns.promises.resolve; let mockDnsResolveImpl: (hostname: string) => Promise = async () => ['127.0.0.1']; // Setup mock DNS resolver plugins.dns.promises.resolve = async (hostname: string) => { return mockDnsResolveImpl(hostname); }; // Test instantiation tap.test('IPReputationChecker - should be instantiable', async () => { const checker = IPReputationChecker.getInstance({ enableDNSBL: false, enableIPInfo: false, enableLocalCache: false }); expect(checker).toBeTruthy(); }); // Test singleton pattern tap.test('IPReputationChecker - should use singleton pattern', async () => { const checker1 = IPReputationChecker.getInstance(); const checker2 = IPReputationChecker.getInstance(); // Both instances should be the same object expect(checker1 === checker2).toEqual(true); }); // Test IP validation tap.test('IPReputationChecker - should validate IP address format', async () => { const checker = IPReputationChecker.getInstance({ enableDNSBL: false, enableIPInfo: false, enableLocalCache: false }); // Valid IP should work const result = await checker.checkReputation('192.168.1.1'); expect(result.score).toBeGreaterThan(0); expect(result.error).toBeUndefined(); // Invalid IP should fail with error const invalidResult = await checker.checkReputation('invalid.ip'); expect(invalidResult.error).toBeTruthy(); }); // Test DNSBL lookups tap.test('IPReputationChecker - should check IP against DNSBL', async () => { try { // Setup mock implementation for DNSBL mockDnsResolveImpl = async (hostname: string) => { // Listed in DNSBL if IP contains 2 if (hostname.includes('2.1.168.192') && hostname.includes('zen.spamhaus.org')) { return ['127.0.0.2']; } throw { code: 'ENOTFOUND' }; }; // Create a new instance with specific settings for this test const testInstance = new IPReputationChecker({ dnsblServers: ['zen.spamhaus.org'], enableIPInfo: false, enableLocalCache: false, maxCacheSize: 1 // Small cache for testing }); // Clean IP should have good score const cleanResult = await testInstance.checkReputation('192.168.1.1'); expect(cleanResult.isSpam).toEqual(false); expect(cleanResult.score).toEqual(100); // Blacklisted IP should have reduced score const blacklistedResult = await testInstance.checkReputation('192.168.1.2'); expect(blacklistedResult.isSpam).toEqual(true); expect(blacklistedResult.score < 100).toEqual(true); // Less than 100 expect(blacklistedResult.blacklists).toBeTruthy(); expect((blacklistedResult.blacklists || []).length > 0).toEqual(true); } catch (err) { console.error('Test error:', err); throw err; } }); // Test caching behavior tap.test('IPReputationChecker - should cache reputation results', async () => { // Create a fresh instance for this test const testInstance = new IPReputationChecker({ enableIPInfo: false, enableLocalCache: false, maxCacheSize: 10 // Small cache for testing }); // Check that first look performs a lookup and second uses cache const ip = '192.168.1.10'; // First check should add to cache const result1 = await testInstance.checkReputation(ip); expect(result1).toBeTruthy(); // Manually verify it's in cache - access private member for testing const hasInCache = (testInstance as any).reputationCache.has(ip); expect(hasInCache).toEqual(true); // Call again, should use cache const result2 = await testInstance.checkReputation(ip); expect(result2).toBeTruthy(); // Results should be identical expect(result1.score).toEqual(result2.score); }); // Test risk level classification tap.test('IPReputationChecker - should classify risk levels correctly', async () => { expect(IPReputationChecker.getRiskLevel(10)).toEqual('high'); expect(IPReputationChecker.getRiskLevel(30)).toEqual('medium'); expect(IPReputationChecker.getRiskLevel(60)).toEqual('low'); expect(IPReputationChecker.getRiskLevel(90)).toEqual('trusted'); }); // Test IP type detection tap.test('IPReputationChecker - should detect special IP types', async () => { const testInstance = new IPReputationChecker({ enableDNSBL: false, enableIPInfo: true, enableLocalCache: false, maxCacheSize: 5 // Small cache for testing }); // Test Tor exit node detection const torResult = await testInstance.checkReputation('171.25.1.1'); expect(torResult.isTor).toEqual(true); expect(torResult.score < 90).toEqual(true); // Test VPN detection const vpnResult = await testInstance.checkReputation('185.156.1.1'); expect(vpnResult.isVPN).toEqual(true); expect(vpnResult.score < 90).toEqual(true); // Test proxy detection const proxyResult = await testInstance.checkReputation('34.92.1.1'); expect(proxyResult.isProxy).toEqual(true); expect(proxyResult.score < 90).toEqual(true); }); // Test error handling tap.test('IPReputationChecker - should handle DNS lookup errors gracefully', async () => { // Setup mock implementation to simulate error mockDnsResolveImpl = async () => { throw new Error('DNS server error'); }; const checker = IPReputationChecker.getInstance({ dnsblServers: ['zen.spamhaus.org'], enableIPInfo: false, enableLocalCache: false, maxCacheSize: 300 // Force new instance }); // Should return a result despite errors const result = await checker.checkReputation('192.168.1.1'); expect(result.score).toEqual(100); // No blacklist hits found due to error expect(result.isSpam).toEqual(false); }); // Restore original implementation at the end tap.test('Cleanup - restore mocks', async () => { plugins.dns.promises.resolve = originalDnsResolve; }); export default tap.start();