Phase 3 of the Rust migration: the Rust security bridge is now mandatory and all TypeScript security fallback implementations have been removed. - UnifiedEmailServer.start() throws if Rust bridge fails to start - SpfVerifier gutted to thin wrapper (parseSpfRecord stays in TS) - DKIMVerifier gutted to thin wrapper delegating to bridge.verifyDkim() - IPReputationChecker delegates to bridge.checkIpReputation(), keeps LRU cache - DmarcVerifier keeps alignment logic (works with pre-computed results) - DKIM signing via bridge.signDkim() in all 4 locations - Removed mailauth and ip packages from plugins.ts (~1,200 lines deleted)
117 lines
3.8 KiB
TypeScript
117 lines
3.8 KiB
TypeScript
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
|
import { IPReputationChecker, ReputationThreshold } from '../ts/security/classes.ipreputationchecker.js';
|
|
import { RustSecurityBridge } from '../ts/security/classes.rustsecuritybridge.js';
|
|
|
|
let bridge: RustSecurityBridge;
|
|
|
|
// Start the Rust bridge before tests
|
|
tap.test('setup - start Rust security bridge', async () => {
|
|
bridge = RustSecurityBridge.getInstance();
|
|
const ok = await bridge.start();
|
|
expect(ok).toEqual(true);
|
|
});
|
|
|
|
// Test instantiation
|
|
tap.test('IPReputationChecker - should be instantiable', async () => {
|
|
const checker = IPReputationChecker.getInstance({
|
|
enableLocalCache: false
|
|
});
|
|
|
|
expect(checker).toBeTruthy();
|
|
});
|
|
|
|
// Test singleton pattern
|
|
tap.test('IPReputationChecker - should use singleton pattern', async () => {
|
|
const checker1 = IPReputationChecker.getInstance();
|
|
const checker2 = IPReputationChecker.getInstance();
|
|
|
|
expect(checker1 === checker2).toEqual(true);
|
|
});
|
|
|
|
// Test IP validation
|
|
tap.test('IPReputationChecker - should validate IP address format', async () => {
|
|
const checker = IPReputationChecker.getInstance();
|
|
|
|
// Invalid IP should fail with error
|
|
const invalidResult = await checker.checkReputation('invalid.ip');
|
|
expect(invalidResult.error).toBeTruthy();
|
|
});
|
|
|
|
// Test reputation check via Rust bridge
|
|
tap.test('IPReputationChecker - should check IP reputation via Rust', async () => {
|
|
const testInstance = new IPReputationChecker({
|
|
enableLocalCache: false,
|
|
maxCacheSize: 10
|
|
});
|
|
|
|
// Check a public IP (Google DNS) — should get a result with a score
|
|
const result = await testInstance.checkReputation('8.8.8.8');
|
|
expect(result).toBeTruthy();
|
|
expect(result.score).toBeGreaterThan(0);
|
|
expect(result.score).toBeLessThanOrEqual(100);
|
|
expect(typeof result.isSpam).toEqual('boolean');
|
|
expect(typeof result.isProxy).toEqual('boolean');
|
|
expect(typeof result.isTor).toEqual('boolean');
|
|
expect(typeof result.isVPN).toEqual('boolean');
|
|
expect(result.timestamp).toBeGreaterThan(0);
|
|
});
|
|
|
|
// Test caching behavior
|
|
tap.test('IPReputationChecker - should cache reputation results', async () => {
|
|
const testInstance = new IPReputationChecker({
|
|
enableLocalCache: false,
|
|
maxCacheSize: 10
|
|
});
|
|
|
|
const ip = '1.1.1.1';
|
|
|
|
// First check should add to cache
|
|
const result1 = await testInstance.checkReputation(ip);
|
|
expect(result1).toBeTruthy();
|
|
|
|
// Verify it's in cache
|
|
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 (from cache)
|
|
expect(result1.score).toEqual(result2.score);
|
|
expect(result1.isSpam).toEqual(result2.isSpam);
|
|
});
|
|
|
|
// 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 error handling for error result
|
|
tap.test('IPReputationChecker - should handle errors gracefully', async () => {
|
|
const testInstance = new IPReputationChecker({
|
|
enableLocalCache: false,
|
|
maxCacheSize: 5
|
|
});
|
|
|
|
// Invalid format should return error result with neutral score
|
|
const result = await testInstance.checkReputation('not-an-ip');
|
|
expect(result.score).toEqual(50);
|
|
expect(result.error).toBeTruthy();
|
|
expect(result.isSpam).toEqual(false);
|
|
});
|
|
|
|
// Stop bridge
|
|
tap.test('cleanup - stop Rust security bridge', async () => {
|
|
await bridge.stop();
|
|
});
|
|
|
|
tap.test('stop', async () => {
|
|
await tap.stopForcefully();
|
|
});
|
|
|
|
export default tap.start();
|