Skip to main content

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:

FunctionDescription
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

AttributeTypeDefaultDescription
strategy_idstr""Unique identifier, used in config and registry
descriptionstr""Human-readable description
MethodRequiredDescription
transform(sequences, context)YesTransform the sequence list

The constructor accepts an optional config dict for strategy-specific settings.