/** * Unit tests for fuzzing strategies and FuzzingEngine. */ import { detectInputType } from '../../src/plugins/fuzzers/InputTypeDetector'; import { EmptyValueStrategy } from '../../src/plugins/fuzzers/strategies/EmptyValueStrategy'; import { OversizedStringStrategy } from '../../src/plugins/fuzzers/strategies/OversizedStringStrategy'; import { SpecialCharsStrategy } from '../../src/plugins/fuzzers/strategies/SpecialCharsStrategy'; import { TypeMismatchStrategy } from '../../src/plugins/fuzzers/strategies/TypeMismatchStrategy'; import { BoundaryValueStrategy } from '../../src/plugins/fuzzers/strategies/BoundaryValueStrategy'; import { FuzzingEngine } from '../../src/plugins/fuzzers/FuzzingEngine'; import { IState } from '../../src/core/interfaces'; function makeState(domSnapshot = ''): IState { return { id: 'state1', url: 'http://test.com', title: 'Test', timestamp: Date.now(), domSnapshot, visitCount: 1, }; } // ─── InputTypeDetector ──────────────────────────────────────────────────────── describe('detectInputType', () => { it('detects email from inputType', () => { expect(detectInputType({ inputType: 'email' })).toBe('email'); }); it('detects password from inputType', () => { expect(detectInputType({ inputType: 'password' })).toBe('password'); }); it('detects number from inputType', () => { expect(detectInputType({ inputType: 'number' })).toBe('number'); }); it('detects email from name attribute', () => { expect(detectInputType({ name: 'email_address' })).toBe('email'); }); it('detects phone from placeholder', () => { expect(detectInputType({ placeholder: 'Enter phone number' })).toBe('phone'); }); it('detects textarea from tagName', () => { expect(detectInputType({ tagName: 'textarea' })).toBe('textarea'); }); it('falls back to text for unknown', () => { expect(detectInputType({})).toBe('text'); }); }); // ─── EmptyValueStrategy ─────────────────────────────────────────────────────── describe('EmptyValueStrategy', () => { const strategy = new EmptyValueStrategy(); it('applies to all types', () => { expect(strategy.appliesTo('email')).toBe(true); expect(strategy.appliesTo('number')).toBe(true); expect(strategy.appliesTo('text')).toBe(true); }); it('returns empty/whitespace values', () => { expect(strategy.values()).toContain(''); expect(strategy.values()).toContain(' '); expect(strategy.values()).toContain('\t'); }); }); // ─── OversizedStringStrategy ────────────────────────────────────────────────── describe('OversizedStringStrategy', () => { it('applies to text types', () => { const s = new OversizedStringStrategy('medium'); expect(s.appliesTo('text')).toBe(true); expect(s.appliesTo('email')).toBe(true); expect(s.appliesTo('number')).toBe(false); }); it('returns low-intensity 256 chars', () => { const s = new OversizedStringStrategy('low'); expect(s.values()[0]?.length).toBe(256); }); it('returns medium-intensity 1024 chars', () => { const s = new OversizedStringStrategy('medium'); expect(s.values()[0]?.length).toBe(1024); }); it('returns high-intensity 10000+ chars', () => { const s = new OversizedStringStrategy('high'); expect(s.values()[0]!.length).toBeGreaterThan(10000); }); }); // ─── SpecialCharsStrategy ───────────────────────────────────────────────────── describe('SpecialCharsStrategy', () => { const s = new SpecialCharsStrategy(); it('applies to text, email, search, textarea', () => { expect(s.appliesTo('text')).toBe(true); expect(s.appliesTo('email')).toBe(true); expect(s.appliesTo('number')).toBe(false); }); it('includes SQL injection payload', () => { expect(s.values()).toContain("' OR 1=1 --"); }); it('includes XSS payload', () => { expect(s.values()).toContain(''); }); }); // ─── TypeMismatchStrategy ───────────────────────────────────────────────────── describe('TypeMismatchStrategy', () => { const s = new TypeMismatchStrategy(); it('applies to typed fields', () => { expect(s.appliesTo('email')).toBe(true); expect(s.appliesTo('number')).toBe(true); expect(s.appliesTo('text')).toBe(false); }); it('returns mismatched values for email', () => { expect(s.values('email')).toContain('not-an-email'); }); it('returns mismatched values for number', () => { expect(s.values('number')).toContain('abc'); }); it('returns empty for unhandled type', () => { expect(s.values('text')).toEqual([]); }); }); // ─── BoundaryValueStrategy ──────────────────────────────────────────────────── describe('BoundaryValueStrategy', () => { const s = new BoundaryValueStrategy(); it('applies to number and date', () => { expect(s.appliesTo('number')).toBe(true); expect(s.appliesTo('date')).toBe(true); expect(s.appliesTo('text')).toBe(false); }); it('returns boundary numbers', () => { expect(s.values('number')).toContain('0'); expect(s.values('number')).toContain('2147483647'); }); it('returns boundary dates', () => { expect(s.values('date')).toContain('1900-01-01'); }); }); // ─── FuzzingEngine ──────────────────────────────────────────────────────────── describe('FuzzingEngine', () => { it('generates actions from DOM snapshot with input fields', () => { const engine = new FuzzingEngine({ intensity: 'low', seed: 42 }); const dom = `
`; const state = makeState(dom); const actions = engine.generateFuzzActions(dom, state); expect(actions.length).toBeGreaterThan(0); expect(actions.every((a) => a.type === 'fill')).toBe(true); expect(actions.every((a) => a.stateId === 'state1')).toBe(true); }); it('generates more actions at high intensity', () => { const low = new FuzzingEngine({ intensity: 'low', seed: 1 }); const high = new FuzzingEngine({ intensity: 'high', seed: 1 }); const dom = ``; const state = makeState(dom); expect(high.generateFuzzActions(dom, state).length).toBeGreaterThan( low.generateFuzzActions(dom, state).length ); }); it('returns empty array for DOM with no inputs', () => { const engine = new FuzzingEngine({ intensity: 'medium', seed: 1 }); const dom = `No forms here