Skip to main content
The chain module provides a declarative framework for defining and tracing multi-step attack paths that link audit scanners and inject techniques into exploitation sequences.

File layout

src/counteragent/chain/
├── cli.py         # Typer subcommands (validate, list-templates, run, blast-radius, detect)
├── models.py      # Data models (ChainDefinition, ChainStep, ChainResult, enums)
├── loader.py      # YAML discovery, parsing, and structural validation
├── validator.py   # Semantic validation — module refs, graph analysis
├── tracer.py      # Dry-run tracer — walks success path, tracks trust boundaries
└── templates/     # Built-in chain YAML files (3 templates)

Data flow

CLI command

  ├─ validate ──► load_chain() ──► validate_chain() ──► error report

  ├─ list-templates ──► load_all_chains() ──► filter ──► Rich table

  └─ run ──► load_chain() ──► validate_chain() ──► trace_chain() ──► JSON

Key components

ChainDefinition and ChainStep (models.py)

ChainDefinition holds the chain metadata (id, name, category, description) and an ordered list of ChainStep objects. Each step specifies a module (audit or inject), a technique, optional trust boundary, routing (on_success/on_failure), and whether it’s terminal. ChainCategory enum defines four architecture patterns: rag_pipeline, agent_delegation, mcp_ecosystem, hybrid.

Loader (loader.py)

load_chain(path) parses a single YAML file into a ChainDefinition:
  1. YAML safe-load with error handling for encoding and parse failures
  2. Validates required top-level fields (id, name, category, description, steps)
  3. Validates category against the ChainCategory enum
  4. Parses each step dict, enforcing required fields (id, name, module, technique)
  5. Ensures step ID uniqueness within the file
load_all_chains() discovers all YAML files in the templates directory and enforces chain ID uniqueness across files.

Validator (validator.py)

validate_chain(chain) performs six ordered checks, returning a list of ValidationError objects:
CheckWhat it validates
1. Module refsstep.module must be audit or inject
2. Technique refsTechnique must exist in the audit scanner registry or inject technique enum
3. Graph refson_success/on_failure targets must be valid step IDs or abort
4. Cycle detectionDFS with three-color marking to detect circular step references
5. ReachabilityBFS from the first step to ensure all steps are reachable
6. Terminal stepsAt least one step must be marked terminal or be the implicit last step
The validator lazily imports from counteragent.audit.scanner.registry and counteragent.inject.models to cross-check module references without creating circular dependencies.

Graph algorithms

Cycle detection uses iterative DFS with a three-color state machine (0=unvisited, 1=in-stack, 2=done). When a back-edge is found (visiting a node already in-stack), the cycle path is reported. Reachability uses BFS from the first step over the adjacency graph. The adjacency graph includes explicit on_success/on_failure edges plus implicit next-in-order edges for steps without explicit routing.

Dry-run tracer (tracer.py)

trace_chain(chain) walks the success path without making network calls or sending payloads:
  1. Starts at the first step in list order
  2. For each step: records the step ID, module, technique, and trust boundary
  3. Follows on_success routing (or implicit next-in-order)
  4. Stops at terminal steps or end of list
  5. Returns a TraceResult with the ordered step path and unique trust boundaries crossed
The tracer is purely read-only — it produces a trace JSON showing what a live execution would do.