test(15-02): add failing tests for phishing pattern matcher + content hash

- Covers all 8 locked DETECT-01 patterns individually, negative case,
  case-insensitivity, and content-hash stability/change/null-normalization
This commit is contained in:
lorentz 2026-07-15 07:41:14 -04:00
parent 6dffb7b358
commit 0e7daf9a6a

View file

@ -0,0 +1,106 @@
import { describe, it, expect } from 'vitest';
import {
KNOWN_PHISHING_PATTERNS,
matchesPhishingPatterns,
computePhishingContentHash,
} from './phishing-detector';
describe('KNOWN_PHISHING_PATTERNS', () => {
it('has exactly 8 locked patterns', () => {
expect(KNOWN_PHISHING_PATTERNS).toHaveLength(8);
});
});
describe('matchesPhishingPatterns', () => {
it('flags a title containing "Phishing Report"', () => {
const result = matchesPhishingPatterns('Fwd: Phishing Report', null);
expect(result.flagged).toBe(true);
expect(result.matched).toContain('Phishing Report');
});
it('flags a title containing "Spam Alert"', () => {
const result = matchesPhishingPatterns('Spam Alert - user reported', null);
expect(result.flagged).toBe(true);
expect(result.matched).toContain('Spam Alert');
});
it('flags a title containing "Phishing Alert - Email Security Report"', () => {
const result = matchesPhishingPatterns('Phishing Alert - Email Security Report', null);
expect(result.flagged).toBe(true);
expect(result.matched).toContain('Phishing Alert - Email Security Report');
});
it('flags a description containing "KnowBe4 Phish Alert Report"', () => {
const result = matchesPhishingPatterns(null, 'This is a KnowBe4 Phish Alert Report for review');
expect(result.flagged).toBe(true);
expect(result.matched).toContain('KnowBe4 Phish Alert Report');
});
it('flags a description containing "Source: KnowBe4 Phish Alert Button"', () => {
const result = matchesPhishingPatterns(null, 'Source: KnowBe4 Phish Alert Button');
expect(result.flagged).toBe(true);
expect(result.matched).toContain('Source: KnowBe4 Phish Alert Button');
});
it('flags a description containing "userSubmissionsReportMessage"', () => {
const result = matchesPhishingPatterns(null, 'Generated by userSubmissionsReportMessage flow');
expect(result.flagged).toBe(true);
expect(result.matched).toContain('userSubmissionsReportMessage');
});
it('flags a description containing "reported message destinations"', () => {
const result = matchesPhishingPatterns(null, 'See reported message destinations below');
expect(result.flagged).toBe(true);
expect(result.matched).toContain('reported message destinations');
});
it('flags a description containing "Microsoft directly"', () => {
const result = matchesPhishingPatterns(null, 'This message was reported to Microsoft directly');
expect(result.flagged).toBe(true);
expect(result.matched).toContain('Microsoft directly');
});
it('does not flag a ticket with none of the patterns', () => {
const result = matchesPhishingPatterns('Re: order confirmation', 'please review invoice');
expect(result.flagged).toBe(false);
expect(result.matched).toEqual([]);
});
it('matches case-insensitively', () => {
const result = matchesPhishingPatterns('SPAM ALERT from user', null);
expect(result.flagged).toBe(true);
expect(result.matched).toContain('Spam Alert');
});
it('returns only the patterns actually present in matched[]', () => {
const result = matchesPhishingPatterns('Phishing Report', 'unrelated body text');
expect(result.matched).toEqual(['Phishing Report']);
});
it('checks both title and description for a match', () => {
const result = matchesPhishingPatterns('Unrelated subject', 'Spam Alert triggered');
expect(result.flagged).toBe(true);
expect(result.matched).toContain('Spam Alert');
});
});
describe('computePhishingContentHash', () => {
it('is stable for identical title+description', () => {
expect(computePhishingContentHash('t', 'd')).toBe(computePhishingContentHash('t', 'd'));
});
it('changes when the title changes', () => {
expect(computePhishingContentHash('t', 'd')).not.toBe(computePhishingContentHash('t2', 'd'));
});
it('changes when the description changes', () => {
expect(computePhishingContentHash('t', 'd')).not.toBe(computePhishingContentHash('t', 'd2'));
});
it('is defined and stable when description is null', () => {
const hash1 = computePhishingContentHash('t', null);
const hash2 = computePhishingContentHash('t', null);
expect(hash1).toBeTruthy();
expect(hash1).toBe(hash2);
});
});