Files
Autonomous-Bug-Explorer/src/modules/fuzzing/infrastructure/adapters/FuzzingEngineAdapter.ts
2026-03-05 09:22:55 -05:00

122 lines
3.8 KiB
TypeScript

/**
* FuzzingEngineAdapter — implements IFuzzerEngine port using the 5 fuzzing strategies.
* Adapts the legacy FuzzingEngine logic to the hexagonal architecture.
*/
import * as crypto from 'crypto';
import { IAction, IState } from '../../../../core/interfaces';
import { IFuzzerEngine } from '../../domain/ports/IFuzzerEngine';
import { detectInputType, DetectedInputType } from './InputTypeDetector';
import { EmptyValueStrategy } from '../strategies/EmptyValueStrategy';
import { OversizedStringStrategy } from '../strategies/OversizedStringStrategy';
import { SpecialCharsStrategy } from '../strategies/SpecialCharsStrategy';
import { TypeMismatchStrategy } from '../strategies/TypeMismatchStrategy';
import { BoundaryValueStrategy } from '../strategies/BoundaryValueStrategy';
interface FormField {
selector: string;
tagName: string;
inputType?: string;
name?: string;
placeholder?: string;
ariaLabel?: string;
}
const INPUT_RE = /<(input|textarea|select)[^>]*>/gi;
const ATTR_RE = (name: string): RegExp => new RegExp(`${name}="([^"]*)"`, 'i');
function extractFields(domSnapshot: string): FormField[] {
const fields: FormField[] = [];
let match: RegExpExecArray | null;
while ((match = INPUT_RE.exec(domSnapshot)) !== null) {
const tag = match[0] ?? '';
const tagName = match[1] ?? 'input';
const idMatch = ATTR_RE('id').exec(tag);
const nameMatch = ATTR_RE('name').exec(tag);
const typeMatch = ATTR_RE('type').exec(tag);
const placeholderMatch = ATTR_RE('placeholder').exec(tag);
const ariaMatch = ATTR_RE('aria-label').exec(tag);
const selector = idMatch?.[1]
? `#${idMatch[1]}`
: nameMatch?.[1]
? `[name="${nameMatch[1]}"]`
: tagName;
fields.push({
selector,
tagName,
inputType: typeMatch?.[1],
name: nameMatch?.[1],
placeholder: placeholderMatch?.[1],
ariaLabel: ariaMatch?.[1],
});
}
return fields;
}
type FuzzingStrategy = {
name: string;
appliesTo(type: DetectedInputType): boolean;
values(type?: DetectedInputType): string[];
};
export class FuzzingEngineAdapter implements IFuzzerEngine {
private readonly intensity: 'low' | 'medium' | 'high';
private readonly seed: number;
constructor(config: { intensity: 'low' | 'medium' | 'high'; seed: number }) {
this.intensity = config.intensity;
this.seed = config.seed;
}
generateFuzzActions(domSnapshot: string, state: IState): IAction[] {
const fields = extractFields(domSnapshot);
const actions: IAction[] = [];
const now = Date.now();
const strategies = this.selectStrategies();
for (const field of fields) {
const detectedType = detectInputType({
tagName: field.tagName,
inputType: field.inputType,
name: field.name,
placeholder: field.placeholder,
ariaLabel: field.ariaLabel,
});
for (const strategy of strategies) {
if (!strategy.appliesTo(detectedType)) continue;
const values = strategy.values(detectedType);
for (const value of values) {
actions.push({
id: crypto.randomUUID(),
type: 'fill',
selector: field.selector,
value,
timestamp: now,
seed: this.seed,
stateId: state.id,
});
}
}
}
return actions;
}
private selectStrategies(): FuzzingStrategy[] {
const empty = new EmptyValueStrategy();
const typeMismatch = new TypeMismatchStrategy();
const oversized = new OversizedStringStrategy(this.intensity);
const boundary = new BoundaryValueStrategy();
const special = new SpecialCharsStrategy();
switch (this.intensity) {
case 'low': return [empty, typeMismatch];
case 'medium': return [empty, typeMismatch, oversized, boundary];
case 'high': return [empty, typeMismatch, oversized, boundary, special];
}
}
}