Strategies
Strategies control how attack sequences are ordered, filtered, and delivered. They sit between generation (plugins) and execution (targets) in the pipeline. A strategy receives a list of AttackSequence objects and returns a transformed list.
How Strategies Work
Each strategy implements one method:
def transform(self, sequences: List[AttackSequence], context: EngagementContext) -> List[AttackSequence]:
The engine applies strategies in order. Each strategy receives the output of the previous one:
for strategy in strategies:
sequences = strategy.transform(sequences, context)
Strategies can reorder, filter, duplicate, split, or modify sequences. They can also read context.previous_results and context.discovered_services to make dynamic decisions.
Strategy Types
Three strategy patterns are planned:
Sequential
Execute sequences in order, one at a time. Each phase must complete before the next begins. This is the default behavior when no strategy is configured.
Use case: structured assessments where recon must finish before exploitation begins.
Parallel
Fan-out execution. Multiple sequences run concurrently. Results merge back into the shared context.
Use case: scanning multiple ports or services simultaneously for speed.
Adaptive
Dynamic reordering based on grades from previous steps. If a recon phase discovers an interesting service, the adaptive strategy can promote exploitation sequences targeting that service to the front of the queue.
Use case: autonomous engagements where the attack path adjusts based on discoveries.
Strategy Registry
Strategies register via the @register_strategy decorator:
from blackrainbow.strategies import register_strategy
from blackrainbow.strategies.base import StrategyBase
@register_strategy
class MyStrategy(StrategyBase):
strategy_id = "my-strategy"
description = "Custom delivery strategy"
...
Registry functions:
| Function | Description |
|---|---|
register_strategy(cls) | Decorator. Registers a strategy class by its strategy_id. |
get_strategy(strategy_id, config=None) | Instantiates a strategy by ID. Raises ValueError if not found. |
list_strategies() | Returns metadata for all registered strategies. |
YAML Configuration
Strategies are listed by ID in the config:
strategies:
- sequential
- adaptive
An empty list means no transformation, sequences execute in generation order:
strategies: []
Writing a Custom Strategy
"""Priority-based strategy example."""
from typing import List, Optional, Dict, Any
from blackrainbow.strategies import register_strategy
from blackrainbow.strategies.base import StrategyBase
from blackrainbow.plugins.base import AttackSequence, EngagementContext
@register_strategy
class PriorityStrategy(StrategyBase):
strategy_id = "priority"
description = "Reorder sequences by severity (critical first)"
def transform(
self,
sequences: List[AttackSequence],
context: EngagementContext,
) -> List[AttackSequence]:
severity_order = {
"critical": 0,
"high": 1,
"medium": 2,
"low": 3,
"info": 4,
}
return sorted(
sequences,
key=lambda s: severity_order.get(s.severity.value, 5),
)
StrategyBase Class Reference
| Attribute | Type | Default | Description |
|---|---|---|---|
strategy_id | str | "" | Unique identifier, used in config and registry |
description | str | "" | Human-readable description |
| Method | Required | Description |
|---|---|---|
transform(sequences, context) | Yes | Transform the sequence list |
The constructor accepts an optional config dict for strategy-specific settings.