docs: enterprise refactor plan with ralph specs

This commit is contained in:
debian
2026-03-04 16:17:03 -05:00
parent 4c92712d20
commit f8191133c8
204 changed files with 32722 additions and 422 deletions

View File

@@ -0,0 +1,246 @@
/**
* 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('<script>alert(1)</script>');
});
});
// ─── 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 = `<form><input type="email" name="email" /><input type="password" name="pass" /></form>`;
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 = `<input type="text" name="q" />`;
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 = `<div><p>No forms here</p></div>`;
const state = makeState(dom);
expect(engine.generateFuzzActions(dom, state)).toHaveLength(0);
});
});
// ─── AnomalyDetector fuzzing rules ────────────────────────────────────────────
describe('AnomalyDetector fuzzing anomaly types', () => {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { AnomalyDetector } = require('../../src/core/AnomalyDetector');
const detector = new AnomalyDetector();
const baseObs = {
id: 'obs1',
actionId: 'act1',
newStateId: 's1',
httpResponses: [],
consoleErrors: [],
jsExceptions: [],
timestamp: Date.now(),
};
it('detects validation_bypass on 200 response to empty input', () => {
const obs = { ...baseObs, httpResponses: [{ url: '/', status: 200, method: 'POST', durationMs: 10 }] };
const result = detector.checkValidationBypass(obs, [], '');
expect(result).not.toBeNull();
expect(result!.type).toBe('validation_bypass');
});
it('does not detect validation_bypass without 2xx', () => {
const obs = { ...baseObs, httpResponses: [{ url: '/', status: 400, method: 'POST', durationMs: 10 }] };
const result = detector.checkValidationBypass(obs, [], '');
expect(result).toBeNull();
});
it('detects server_error_on_fuzz on 500', () => {
const obs = { ...baseObs, httpResponses: [{ url: '/', status: 500, method: 'POST', durationMs: 10 }] };
const result = detector.checkServerErrorOnFuzz(obs, []);
expect(result).not.toBeNull();
expect(result!.type).toBe('server_error_on_fuzz');
expect(result!.severity).toBe('high');
});
it('detects xss_reflection when script tag in DOM', () => {
const result = detector.checkXssReflection(baseObs, [], '<script>alert(1)</script>');
expect(result).not.toBeNull();
expect(result!.type).toBe('xss_reflection');
expect(result!.severity).toBe('critical');
});
it('does not detect xss_reflection without payload in DOM', () => {
const result = detector.checkXssReflection(baseObs, [], '<div>clean</div>');
expect(result).toBeNull();
});
});