Replicant v0.5.0
Replicant emulates a TTP by writing the telemetry the attack would have produced, so the shape of that telemetry is the product. v0.4.0 fixed when events arrive. This fixes how much of the behaviour you get when you ask for a window of it.
Fixed: four use cases ignored --duration
Asking for a two hour simulation has to produce two hours of telemetry. Four of the twenty-four use cases silently planned something else:
| use case | asked 2h, planned | why |
|---|---|---|
| REP-005 / UC-004 | 5h 51m | pinned to a fixed six hour off-hours window, flag ignored |
| REP-014 / UC-013 | 5h 59m | read the value as a per-session length, then multiplied by session count |
| REP-019 / UC-018 | 2d 10h | span was probe count times a random gap |
| REP-023 / UC-022 | 2h 59m | span was session count times a fixed interval |
A flag that works on twenty entries is worse than one that works on none: an operator learns to trust it and is then wrong four times in twenty-four without being told which. tests/test_duration.py now asserts all 24 by parameter, so a regression names the technique rather than arriving as "something is long".
The rule this establishes
--durationbounds the span. Where the interval between events is the detection signal, the interval is preserved and the event count falls.
That is what separates it from --speed, and the difference decides whether a rule can fire:
| event count | intervals | |
|---|---|---|
--duration 2h |
falls | preserved |
--speed 6 |
preserved | divided by 6 |
A two hour beacon under duration is 24 callbacks five minutes apart. The same thing under speed is 240 callbacks fifty seconds apart, which no interval-keyed rule recognises. Where the gap between events is the signal, duration is the one to reach for.
Added: --duration on scenarios
A scenario's span used to be whatever its catalog stage offsets happened to add up to. SCEN-001 was 12h 14m and nothing could ask for two hours.
Duration scales the composition: stage offsets move proportionally and each stage is planned for a proportionally shorter window, so the chain keeps its order and its relative spacing while every technique inside it keeps its own interval and emits fewer events.
SCEN-003: natural 6h 56m (653 events) -> asked 2h: 1h 59m (619 events)
Two passes, because the scale factor cannot be known until the natural chain has been built: stage spans come from each technique's preset, not from the catalog. The untimed path stays a single pass and is byte-identical, which a test asserts.
A stage pinned to the clock outranks the request, and says so
REP-005 is off-hours bulk transfer, and off-hours is 00:00-06:00. It advances in whole days to clear that window, so SCEN-001 cannot be compressed below the jump:
SCEN-001: natural 12h 14m -> asked 2h: 7h 16m
manifest: "requested duration 7200s, composed 26214s: stage(s) REP-005 pin to an
absolute window and were advanced whole days to clear it, which the
scenario timeline cannot scale away"
Returning a quietly twelve hour run for a two hour request would be the same class of defect as the pacing one v0.4.0 opened with. A single off-hours technique asked for more than six hours is capped at the window for the same reason: spilling into the working day would stop it being the thing it demonstrates.
ScenarioManifest records the requested duration (safety rule 5), because two runs of the same scenario and seed can now cover very different windows.
Upgrade note
Four techniques now plan a different length for the same flags, and scenarios accept an option they previously rejected. Nothing was removed and nothing errors, which is why this is a minor bump rather than a patch.
Verification
728 Python tests and 118 frontend tests, black / ruff / mypy / shellcheck / tsc -b clean. All 10 CI jobs green on the tagged commit.
Known limitation
eventtime is integer epoch seconds, so one second is the finest gap a plan can express. That bounds both compression and how short a faithful run can be.
Replicant generates synthetic telemetry for detection engineering in a lab. It writes log text and never executes attacks.