Skip to content

feat(trace): add regression tests for 13 pre-existing detectors - #568

Open
stealthwhizz wants to merge 24 commits into
GenAI-Security-Project:mainfrom
stealthwhizz:feature/trace-qa-regression-suite
Open

feat(trace): add regression tests for 13 pre-existing detectors#568
stealthwhizz wants to merge 24 commits into
GenAI-Security-Project:mainfrom
stealthwhizz:feature/trace-qa-regression-suite

Conversation

@stealthwhizz

Copy link
Copy Markdown
Contributor

Summary

Fills the remaining gap in detector test coverage so every detector in the registry has a unit regression test, matching the coverage pattern already used for the earlier-tested detectors.

Test plan

  • Unit tests added for all 13 previously-untested detectors
  • pytest tests/unit/ctf/ passes

Adds StepSpec dataclass and SequenceDetector base structure to
finbot/ctf/detectors/primitives/. Includes config validation,
get_relevant_event_types(), and stubbed private helpers for
history querying, step matching, and time-window checks.
check_event() and all helpers are NotImplementedError stubs
pending implementation.
…gration

- Add SequenceDetector to finbot/ctf/detectors/primitives/
  Detects multi-step attack patterns across a session or workflow window.
  Supports ordered step matching, glob event_type patterns, within_n_events
  and within_seconds windows, and all ToolCallDetector field operators.
  Challenge authors configure it from YAML with no Python required.

- Add composite index idx_ctf_event_session_ts_type on (session_id, timestamp,
  event_type) to keep session-window history queries below 10ms p95.

- Export SequenceDetector from finbot/ctf/detectors/primitives/__init__.py

- Add 17 unit tests covering full sequence detection, partial sequences,
  order enforcement, session/workflow windows, condition operators, and
  glob event_type matching.
- Add StepSpec TypedDict to sequence_detector.py matching the approved
  interface spec; export it from primitives __init__

- Add benchmark test: seeds 1,000 CTFEvent rows with composite index,
  runs check_event 100 times, asserts p95 < 10ms
  Current result: p50 ~7ms, p95 ~8ms on SQLite
…, and tests

- IncrementalFraudDetector uses SequenceDetector as the matching engine;
  two-gate design: N below-threshold approvals in session window, then
  cumulative amount check fires when total >= cumulative_threshold

- incremental_fraud.yaml: 300pts, ASI-08, fraud category;
  default config: 3 approvals each <= 9999, cumulative >= 25000

- Extend _emit_delegation_event() in orchestrator.py with context_preview
  field (first 500 chars of enriched context forwarded between agents)

- Emit delegation.context_snapshot business event on every delegation hop
  making context forwarding observable and scoreable by detectors

- Integration tests for IncrementalFraudDetector: full chain, 5-step
  sequence, session isolation, amount gates, rejection filtering

- Unit tests for Delegation Audit: context capture, preview capping,
  event type validation, empty context handling
…_load_amounts

- sequence_detector.py: change re.search to re.fullmatch in the matches
  operator so patterns must match the full value, not a substring.
  re.search allowed conditions like {"matches": "approval"} to falsely
  match "not_approval" or "partial_approval_pending".

- sequence_detector.py: apply all prior review fixes from PR GenAI-Security-Project#522 —
  ANDed condition operators, contains case-normalisation, _CTF_COLUMNS
  as module-level constant, consumed set for order_matters=False, else
  clause for unknown operators.

- incremental_fraud.py: add namespace parameter to _load_amounts and
  filter CTFEvent.namespace == namespace in the query. Guards against
  cross-namespace amount loading after DB restores or migrations.
Detects lateral movement through agent context propagation:
- Gate 1: delegation.context_snapshot with PI score >= threshold (LLM judge)
- Gate 2: tool call in same workflow_id (receiving agent acted)
- Namespace-isolated, target_agent filter, configurable via YAML
- 14 unit tests covering all gates, filters, and edge cases

OWASP: ASI-01 (Goal Hijack), ASI-06 (Memory & Context Poisoning),
        ASI-07 (Insecure Inter-Agent Communication)
- Add finbot/canary/seeder.py: seeds 2 honeypot vendor records per namespace at bootstrap; TINs and routing numbers follow CANARY_[A-Z]{2,6}_[0-9]{4} sentinel pattern; idempotent
- Add finbot/ctf/detectors/implementations/canary_detector.py: fires at confidence 1.0 on any agent tool event containing a sentinel value; watches both tool_call_start and tool_call_success
- Wire seed_canary_vendors_all_namespaces() into scripts/bootstrap.py
- Register CanaryDetector in implementations __init__
- 17 unit tests covering pattern matching, detector fire/no-fire, seeder idempotency, and namespace isolation
The \d escape in the module docstring raised a SyntaxWarning on import
under Python 3.13. Match the character-class form already used in the
seeder docstring.
Wire the CanaryDetector to a playable challenge. The player steers any
FinBot agent into reading one of the seeded honeypot vendors (Apex
Logistics Partners or Meridian Trade Solutions). When a canary sentinel
value passes through an agent tool call, the detector fires at
confidence 1.0.

Validated against ChallengeSchema; detector_class resolves to the
registered CanaryDetector.
Document how to author challenges against the TRACE detectors:
SequenceDetector config reference, IncrementalFraudDetector,
CanaryDetector, and ContextInjectionDetector, plus a validation
snippet for testing a new challenge before boot.
Scores a student's forensic investigation submission (server, tool,
directive) against the expected answer in detector_config, with
independent partial credit per field (33/33/34 by default). Server and
tool match exactly; the free-text directive uses substring matching by
default, or exact when directive_match is set.

Scoring is deterministic and never calls an LLM, so it behaves
identically under any model backend. Evidence records the score and
which fields were correct but never the expected answer, so completion
evidence cannot spoil the challenge.

19 unit tests cover the 33/66/100 thresholds, zero-score submissions,
extra fields that must not inflate the score, directive fuzzy/exact
modes, normalization, the tool_arguments fallback, and config
validation.
…allenge

Wire the PurpleTeamDetector into a playable forensic challenge:

- POST /ctf/api/v1/toolkit/investigation accepts a {server, tool,
  directive} answer and emits business.investigation.submitted with a
  fresh workflow_id per submission, so each attempt is tracked.
- The Debrief challenge is a purple team follow-up to Toxic Transfer.
  It names finstripe/create_transfer as the poisoned tool and grades the
  directive by meaning, not verbatim text.
- Upgrade directive matching from substring to token overlap with light
  prefix stemming so students can paraphrase. A configurable
  directive_threshold (default 0.6) controls how much of the expected
  answer must appear.

24 unit tests pass, including reordered-paraphrase acceptance and
unrelated-text rejection. Full submit-to-score flow verified against the
challenge config.
# Conflicts:
#	finbot/ctf/detectors/implementations/__init__.py
… add ollama provider

Wire IncrementalFraudDetector into the implementations __init__ so its
decorator actually runs (it was defined but never imported, so the
challenge could not score). Add the ollama provider branch to the LLM
client for local Mellum2 runs.
Fills the remaining gap in detector test coverage so every detector in
the registry has a unit regression test, matching the coverage pattern
already used for the earlier-tested detectors.
@stealthwhizz stealthwhizz changed the title feat: add regression tests for 13 pre-existing detectors feat(trace): add regression tests for 13 pre-existing detectors Aug 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant