"""Scripted attacker for defender Gen-1 GRPO training. Provides randomised attack templates so the training dataset covers all 8 attack types, not just single_ip_flood. """ from __future__ import annotations import logging import random logger = logging.getLogger(__name__) try: from ..models import AttackerAction except ImportError: from models import AttackerAction TEMPLATES: list[str] = [ "single_ip_flood", "ip_spray", "credential_stuffing", "payload_injection", "header_spoof", "slow_drip", "path_traversal", "mixed_legit_cover", ] _DEFAULTS: dict[str, dict] = { "single_ip_flood": {"count": 20, "target_path": "/login"}, "ip_spray": {"count": 15, "target_path": "/api/data"}, "credential_stuffing": {"count": 10, "target_path": "/login"}, "payload_injection": {"count": 8, "target_path": "/api/process"}, "header_spoof": {"count": 10, "target_path": "/login"}, "slow_drip": {"count": 5, "target_path": "/"}, "path_traversal": {"count": 8, "target_path": "/api/data"}, "mixed_legit_cover": {"count": 12, "target_path": "/"}, } class ScriptedAttacker: """Randomly selects attack templates for defender training episodes. Uses a seeded RNG so the same seed always produces the same sequence. """ def __init__(self, seed: int = 42) -> None: self._rng = random.Random(seed) def act(self) -> AttackerAction: """Return a random AttackerAction drawn uniformly from all 8 templates.""" template = self._rng.choice(TEMPLATES) defaults = _DEFAULTS[template] return AttackerAction( template=template, count=defaults["count"], target_path=defaults["target_path"], )