2025-05-07 20:20:17 +00:00
|
|
|
import { tap, expect } from '@push.rocks/tapbundle';
|
|
|
|
import { SzPlatformService } from '../ts/platformservice.js';
|
|
|
|
import { SpfVerifier, SpfQualifier, SpfMechanismType } from '../ts/mta/classes.spfverifier.js';
|
|
|
|
import { DmarcVerifier, DmarcPolicy, DmarcAlignment } from '../ts/mta/classes.dmarcverifier.js';
|
|
|
|
import { Email } from '../ts/mta/classes.email.js';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test email authentication systems: SPF and DMARC
|
|
|
|
*/
|
|
|
|
|
|
|
|
// Setup platform service for testing
|
|
|
|
let platformService: SzPlatformService;
|
|
|
|
|
|
|
|
tap.test('Setup test environment', async () => {
|
|
|
|
platformService = new SzPlatformService();
|
2025-05-07 22:06:55 +00:00
|
|
|
// Use start() instead of init() which doesn't exist
|
|
|
|
await platformService.start();
|
2025-05-07 20:20:17 +00:00
|
|
|
expect(platformService.mtaService).toBeTruthy();
|
|
|
|
});
|
|
|
|
|
|
|
|
// SPF Verifier Tests
|
|
|
|
tap.test('SPF Verifier - should parse SPF record', async () => {
|
|
|
|
const spfVerifier = new SpfVerifier(platformService.mtaService);
|
|
|
|
|
|
|
|
// Test valid SPF record parsing
|
|
|
|
const record = 'v=spf1 a mx ip4:192.168.0.1/24 include:example.org ~all';
|
|
|
|
const parsedRecord = spfVerifier.parseSpfRecord(record);
|
|
|
|
|
|
|
|
expect(parsedRecord).toBeTruthy();
|
|
|
|
expect(parsedRecord.version).toEqual('spf1');
|
|
|
|
expect(parsedRecord.mechanisms.length).toEqual(5);
|
|
|
|
|
|
|
|
// Check specific mechanisms
|
|
|
|
expect(parsedRecord.mechanisms[0].type).toEqual(SpfMechanismType.A);
|
|
|
|
expect(parsedRecord.mechanisms[0].qualifier).toEqual(SpfQualifier.PASS);
|
|
|
|
|
|
|
|
expect(parsedRecord.mechanisms[1].type).toEqual(SpfMechanismType.MX);
|
|
|
|
expect(parsedRecord.mechanisms[1].qualifier).toEqual(SpfQualifier.PASS);
|
|
|
|
|
|
|
|
expect(parsedRecord.mechanisms[2].type).toEqual(SpfMechanismType.IP4);
|
|
|
|
expect(parsedRecord.mechanisms[2].value).toEqual('192.168.0.1/24');
|
|
|
|
|
|
|
|
expect(parsedRecord.mechanisms[3].type).toEqual(SpfMechanismType.INCLUDE);
|
|
|
|
expect(parsedRecord.mechanisms[3].value).toEqual('example.org');
|
|
|
|
|
|
|
|
expect(parsedRecord.mechanisms[4].type).toEqual(SpfMechanismType.ALL);
|
|
|
|
expect(parsedRecord.mechanisms[4].qualifier).toEqual(SpfQualifier.SOFTFAIL);
|
|
|
|
|
|
|
|
// Test invalid record
|
|
|
|
const invalidRecord = 'not-a-spf-record';
|
|
|
|
const invalidParsed = spfVerifier.parseSpfRecord(invalidRecord);
|
|
|
|
expect(invalidParsed).toBeNull();
|
|
|
|
});
|
|
|
|
|
|
|
|
// DMARC Verifier Tests
|
|
|
|
tap.test('DMARC Verifier - should parse DMARC record', async () => {
|
|
|
|
const dmarcVerifier = new DmarcVerifier(platformService.mtaService);
|
|
|
|
|
|
|
|
// Test valid DMARC record parsing
|
|
|
|
const record = 'v=DMARC1; p=reject; sp=quarantine; pct=50; adkim=s; aspf=r; rua=mailto:dmarc@example.com';
|
|
|
|
const parsedRecord = dmarcVerifier.parseDmarcRecord(record);
|
|
|
|
|
|
|
|
expect(parsedRecord).toBeTruthy();
|
|
|
|
expect(parsedRecord.version).toEqual('DMARC1');
|
|
|
|
expect(parsedRecord.policy).toEqual(DmarcPolicy.REJECT);
|
|
|
|
expect(parsedRecord.subdomainPolicy).toEqual(DmarcPolicy.QUARANTINE);
|
|
|
|
expect(parsedRecord.pct).toEqual(50);
|
|
|
|
expect(parsedRecord.adkim).toEqual(DmarcAlignment.STRICT);
|
|
|
|
expect(parsedRecord.aspf).toEqual(DmarcAlignment.RELAXED);
|
|
|
|
expect(parsedRecord.reportUriAggregate).toContain('dmarc@example.com');
|
|
|
|
|
|
|
|
// Test invalid record
|
|
|
|
const invalidRecord = 'not-a-dmarc-record';
|
|
|
|
const invalidParsed = dmarcVerifier.parseDmarcRecord(invalidRecord);
|
|
|
|
expect(invalidParsed).toBeNull();
|
|
|
|
});
|
|
|
|
|
|
|
|
tap.test('DMARC Verifier - should verify DMARC alignment', async () => {
|
|
|
|
const dmarcVerifier = new DmarcVerifier(platformService.mtaService);
|
|
|
|
|
|
|
|
// Test email domains with DMARC alignment
|
|
|
|
const email = new Email({
|
|
|
|
from: 'sender@example.com',
|
|
|
|
to: 'recipient@example.net',
|
|
|
|
subject: 'Test DMARC alignment',
|
|
|
|
text: 'This is a test email'
|
|
|
|
});
|
|
|
|
|
|
|
|
// Test when both SPF and DKIM pass with alignment
|
|
|
|
const dmarcResult = await dmarcVerifier.verify(
|
|
|
|
email,
|
|
|
|
{ domain: 'example.com', result: true }, // SPF - aligned and passed
|
|
|
|
{ domain: 'example.com', result: true } // DKIM - aligned and passed
|
|
|
|
);
|
|
|
|
|
|
|
|
expect(dmarcResult).toBeTruthy();
|
|
|
|
expect(dmarcResult.spfPassed).toEqual(true);
|
|
|
|
expect(dmarcResult.dkimPassed).toEqual(true);
|
|
|
|
expect(dmarcResult.spfDomainAligned).toEqual(true);
|
|
|
|
expect(dmarcResult.dkimDomainAligned).toEqual(true);
|
|
|
|
expect(dmarcResult.action).toEqual('pass');
|
|
|
|
|
|
|
|
// Test when neither SPF nor DKIM is aligned
|
|
|
|
const dmarcResult2 = await dmarcVerifier.verify(
|
|
|
|
email,
|
|
|
|
{ domain: 'differentdomain.com', result: true }, // SPF - passed but not aligned
|
|
|
|
{ domain: 'anotherdomain.com', result: true } // DKIM - passed but not aligned
|
|
|
|
);
|
|
|
|
|
|
|
|
expect(dmarcResult2).toBeTruthy();
|
|
|
|
expect(dmarcResult2.spfPassed).toEqual(true);
|
|
|
|
expect(dmarcResult2.dkimPassed).toEqual(true);
|
|
|
|
expect(dmarcResult2.spfDomainAligned).toEqual(false);
|
|
|
|
expect(dmarcResult2.dkimDomainAligned).toEqual(false);
|
|
|
|
// Since there's no DMARC record in test environment, we expect "none" policy
|
|
|
|
expect(dmarcResult2.policyEvaluated).toEqual(DmarcPolicy.NONE);
|
|
|
|
});
|
|
|
|
|
|
|
|
tap.test('DMARC Verifier - should apply policy correctly', async () => {
|
|
|
|
const dmarcVerifier = new DmarcVerifier(platformService.mtaService);
|
|
|
|
|
|
|
|
// Create test email
|
|
|
|
const email = new Email({
|
|
|
|
from: 'sender@example.com',
|
|
|
|
to: 'recipient@example.net',
|
|
|
|
subject: 'Test DMARC policy application',
|
|
|
|
text: 'This is a test email'
|
|
|
|
});
|
|
|
|
|
|
|
|
// Test pass action
|
2025-05-07 22:06:55 +00:00
|
|
|
const passResult: any = {
|
2025-05-07 20:20:17 +00:00
|
|
|
hasDmarc: true,
|
|
|
|
spfDomainAligned: true,
|
|
|
|
dkimDomainAligned: true,
|
|
|
|
spfPassed: true,
|
|
|
|
dkimPassed: true,
|
|
|
|
policyEvaluated: DmarcPolicy.NONE,
|
|
|
|
actualPolicy: DmarcPolicy.NONE,
|
|
|
|
appliedPercentage: 100,
|
|
|
|
action: 'pass',
|
|
|
|
details: 'DMARC passed'
|
|
|
|
};
|
|
|
|
|
|
|
|
const passApplied = dmarcVerifier.applyPolicy(email, passResult);
|
|
|
|
expect(passApplied).toEqual(true);
|
|
|
|
expect(email.mightBeSpam).toEqual(false);
|
|
|
|
expect(email.headers['X-DMARC-Result']).toEqual('DMARC passed');
|
|
|
|
|
|
|
|
// Test quarantine action
|
2025-05-07 22:06:55 +00:00
|
|
|
const quarantineResult: any = {
|
2025-05-07 20:20:17 +00:00
|
|
|
hasDmarc: true,
|
|
|
|
spfDomainAligned: false,
|
|
|
|
dkimDomainAligned: false,
|
|
|
|
spfPassed: false,
|
|
|
|
dkimPassed: false,
|
|
|
|
policyEvaluated: DmarcPolicy.QUARANTINE,
|
|
|
|
actualPolicy: DmarcPolicy.QUARANTINE,
|
|
|
|
appliedPercentage: 100,
|
|
|
|
action: 'quarantine',
|
|
|
|
details: 'DMARC failed, policy=quarantine'
|
|
|
|
};
|
|
|
|
|
|
|
|
// Reset email spam flag
|
|
|
|
email.mightBeSpam = false;
|
|
|
|
email.headers = {};
|
|
|
|
|
|
|
|
const quarantineApplied = dmarcVerifier.applyPolicy(email, quarantineResult);
|
|
|
|
expect(quarantineApplied).toEqual(true);
|
|
|
|
expect(email.mightBeSpam).toEqual(true);
|
|
|
|
expect(email.headers['X-Spam-Flag']).toEqual('YES');
|
|
|
|
expect(email.headers['X-DMARC-Result']).toEqual('DMARC failed, policy=quarantine');
|
|
|
|
|
|
|
|
// Test reject action
|
2025-05-07 22:06:55 +00:00
|
|
|
const rejectResult: any = {
|
2025-05-07 20:20:17 +00:00
|
|
|
hasDmarc: true,
|
|
|
|
spfDomainAligned: false,
|
|
|
|
dkimDomainAligned: false,
|
|
|
|
spfPassed: false,
|
|
|
|
dkimPassed: false,
|
|
|
|
policyEvaluated: DmarcPolicy.REJECT,
|
|
|
|
actualPolicy: DmarcPolicy.REJECT,
|
|
|
|
appliedPercentage: 100,
|
|
|
|
action: 'reject',
|
|
|
|
details: 'DMARC failed, policy=reject'
|
|
|
|
};
|
|
|
|
|
|
|
|
// Reset email spam flag
|
|
|
|
email.mightBeSpam = false;
|
|
|
|
email.headers = {};
|
|
|
|
|
|
|
|
const rejectApplied = dmarcVerifier.applyPolicy(email, rejectResult);
|
|
|
|
expect(rejectApplied).toEqual(false);
|
|
|
|
expect(email.mightBeSpam).toEqual(true);
|
|
|
|
});
|
|
|
|
|
|
|
|
tap.test('Cleanup test environment', async () => {
|
|
|
|
await platformService.stop();
|
|
|
|
});
|
|
|
|
|
2025-05-07 22:06:55 +00:00
|
|
|
tap.test('stop', async () => {
|
|
|
|
await tap.stopForcefully();
|
|
|
|
});
|
|
|
|
|
2025-05-07 20:20:17 +00:00
|
|
|
export default tap.start();
|