tripline

What it does
It replays recorded attack activity through detection rules every time the rules change, and fails the build if a rule that should fire does not.
Why it matters
A rule that never fires looks the same as a rule that works, right up until the attack.
What was found
A public SigmaHQ rule for setuid abuse flagged 0 of the 2 attack events in a recording of 36 events. The replacement rule in the repository flags both.

Try it Read the limits

Among 36 events recorded from Atomic Red Team's T1548.001 tests on a GitHub-hosted Linux runner, 2 are the attack itself (the two chmod executions). SigmaHQ's stock Setuid and Setgid rule flagged 0 of those 2; the replacement rule flags both. The other 34 events are not attack events.

As found: fail recording run 41838714456 failed 2026-11-04 main CI run 43100952362 passed 2026-11-11

The recording run's gate failed because the stock rule missed the attack: that failure is the finding. The second run is the latest passing CI run on main, which replays the committed recording with the replacement rule.

  • not expected and not matched
  • expected but not matched
  • matched

0 of 2 attack events flagged, among 36 recorded events

2 of 2 attack events flagged, among 36 recorded events

Try it

Pick a command line and see whether the stock rule and the replacement flag it.

Plate 1
Recorded verdicts: what each rule decides on 19 command lines (evaluated by tripline 0.3.0, commit a0d6622)
Command lineStock rule c21c4eaaReplacement rule
Recorded: sh -c wrapper
sh -c sudo touch /tmp/evilBinary; sudo chown root /tmp/evilBinary; sudo chmod u+xs /tmp/evilBinary
Image /usr/bin/dash
Not flagged

Has 'chown root' but not the literal ' chmod u+s' or ' chmod g+s'; the rule needs both.

Not flagged

The mode would match, but Image does not end with /chmod or /busybox, so this event is not a chmod execution.

Recorded: u+xs
chmod u+xs /tmp/evilBinary
Image /usr/bin/chmod
Not flagged

Has neither 'chown root' nor the literal ' chmod u+s' or ' chmod g+s'.

Flagged

Image ends with /chmod or /busybox and the mode sets setuid or setgid (symbolic form).

Recorded: g+xs
chmod g+xs /tmp/evilBinary
Image /usr/bin/chmod
Not flagged

Has neither 'chown root' nor the literal ' chmod u+s' or ' chmod g+s'.

Flagged

Image ends with /chmod or /busybox and the mode sets setuid or setgid (symbolic form).

chown root, then chmod u+s
sh -c chown root /tmp/x && chmod u+s /tmp/x
Image /usr/bin/dash
Flagged

Has both 'chown root' and the literal ' chmod u+s' or ' chmod g+s'.

Not flagged

The mode would match, but Image does not end with /chmod or /busybox, so this event is not a chmod execution.

Octal 4755
chmod 4755 /tmp/x
Image /usr/bin/chmod
Not flagged

Has neither 'chown root' nor the literal ' chmod u+s' or ' chmod g+s'.

Flagged

Image ends with /chmod or /busybox and the mode sets setuid or setgid (octal form).

ug+s
chmod ug+s /tmp/x
Image /usr/bin/chmod
Not flagged

Has neither 'chown root' nor the literal ' chmod u+s' or ' chmod g+s'.

Flagged

Image ends with /chmod or /busybox and the mode sets setuid or setgid (symbolic form).

a+s
chmod a+s /tmp/x
Image /usr/bin/chmod
Not flagged

Has neither 'chown root' nor the literal ' chmod u+s' or ' chmod g+s'.

Flagged

Image ends with /chmod or /busybox and the mode sets setuid or setgid (symbolic form).

Octal 3755
chmod 3755 /tmp/x
Image /usr/bin/chmod
Not flagged

Has neither 'chown root' nor the literal ' chmod u+s' or ' chmod g+s'.

Flagged

Image ends with /chmod or /busybox and the mode sets setuid or setgid (octal form).

Octal 04755
chmod 04755 /tmp/x
Image /usr/bin/chmod
Not flagged

Has neither 'chown root' nor the literal ' chmod u+s' or ' chmod g+s'.

Flagged

Image ends with /chmod or /busybox and the mode sets setuid or setgid (octal form).

Clause list g+w,u+s
chmod g+w,u+s /tmp/x
Image /usr/bin/chmod
Not flagged

Has neither 'chown root' nor the literal ' chmod u+s' or ' chmod g+s'.

Flagged

Image ends with /chmod or /busybox and the mode sets setuid or setgid (symbolic form).

Full path, u+xs
/usr/bin/chmod u+xs /tmp/x
Image /usr/bin/chmod
Not flagged

Has neither 'chown root' nor the literal ' chmod u+s' or ' chmod g+s'.

Flagged

Image ends with /chmod or /busybox and the mode sets setuid or setgid (symbolic form).

Doc path appended
chmod 4755 /tmp/x /usr/share/doc/x
Image /usr/bin/chmod
Not flagged

Has neither 'chown root' nor the literal ' chmod u+s' or ' chmod g+s'.

Flagged

Image ends with /chmod or /busybox and the mode sets setuid or setgid (octal form).

Only under /usr/share/doc/
chmod u+s /usr/share/doc/pkg/helper
Image /usr/bin/chmod
Not flagged

Has neither 'chown root' nor the literal ' chmod u+s' or ' chmod g+s'.

Not flagged

Excluded: every file operand is under /usr/share/doc/.

Benign 644
chmod 644 /tmp/x
Image /usr/bin/chmod
Not flagged

Has neither 'chown root' nor the literal ' chmod u+s' or ' chmod g+s'.

Not flagged

No setuid or setgid mode: no symbolic s bit and no octal mode 2xxx to 7xxx.

Sweep: busybox multicall
busybox chmod u+s f
Image /usr/bin/busybox
Not flagged

Has the chmod text but no 'chown root'; the rule needs both.

Flagged

Image ends with /chmod or /busybox and the mode sets setuid or setgid (symbolic form).

Sweep: clustered flags -vR
chmod -vR 4755 /tmp/evilBinary
Image /usr/bin/chmod
Not flagged

Has neither 'chown root' nor the literal ' chmod u+s' or ' chmod g+s'.

Flagged

Image ends with /chmod or /busybox and the mode sets setuid or setgid (octal form).

Sweep: flag after mode
chmod u+s -v /tmp/evilBinary
Image /usr/bin/chmod
Not flagged

Has neither 'chown root' nor the literal ' chmod u+s' or ' chmod g+s'.

Flagged

Image ends with /chmod or /busybox and the mode sets setuid or setgid (symbolic form).

Sweep: install -m 4755 (missed)
install -m 4755 /tmp/src /tmp/dst
Image /usr/bin/install
Not flagged

Has neither 'chown root' nor the literal ' chmod u+s' or ' chmod g+s'.

Not flagged

Image does not end with /chmod or /busybox and no setuid or setgid mode is present.

Sweep: renamed copy (missed)
/tmp/.c u+s f
Image /tmp/.c
Not flagged

Has neither 'chown root' nor the literal ' chmod u+s' or ' chmod g+s'.

Not flagged

Image does not end with /chmod or /busybox and no setuid or setgid mode is present.

All 36 recorded events and what each rule decided
Applicable events from the recorded run, in order; command lines trimmed to 110 characters
EventImageCommand lineStock ruleReplacement rule
1/usr/bin/sudosudo pwsh -NoProfile -Command Import-Module /tmp/AtomicRedTeam/invoke-atomicredteam/Invoke-AtomicRedTeam.psd1 …Not flaggedNot flagged
2/opt/microsoft/powershell/7/pwshpwsh -NoProfile -Command Import-Module /tmp/AtomicRedTeam/invoke-atomicredteam/Invoke-AtomicRedTeam.psd1 -Forc…Not flaggedNot flagged
3/usr/bin/hostname/usr/bin/hostnameNot flaggedNot flagged
4/usr/bin/hostname/usr/bin/hostnameNot flaggedNot flagged
5/usr/bin/hostname/usr/bin/hostnameNot flaggedNot flagged
6/usr/bin/whoami/usr/bin/whoamiNot flaggedNot flagged
7/usr/bin/id/usr/bin/id -uNot flaggedNot flagged
8/usr/bin/dashsh -c sudo touch /tmp/evilBinary; sudo chown root /tmp/evilBinary; sudo chmod u+xs /tmp/evilBinaryNot flaggedNot flagged
9/usr/bin/sudosudo touch /tmp/evilBinaryNot flaggedNot flagged
10/usr/bin/touchtouch /tmp/evilBinaryNot flaggedNot flagged
11/usr/bin/sudosudo chown root /tmp/evilBinaryNot flaggedNot flagged
12/usr/bin/chownchown root /tmp/evilBinaryNot flaggedNot flagged
13/usr/bin/sudosudo chmod u+xs /tmp/evilBinaryNot flaggedNot flagged
14 (expected)/usr/bin/chmodchmod u+xs /tmp/evilBinaryNot flaggedFlagged
15/usr/bin/ip/usr/sbin/ip -4 -br addr showNot flaggedNot flagged
16/usr/bin/sed/usr/bin/sed -n -e s/^.*UP\s* //pNot flaggedNot flagged
17/usr/bin/cut/usr/bin/cut -d / -f 1Not flaggedNot flagged
18/usr/bin/sleepsleep 2Not flaggedNot flagged
19/usr/bin/sudosudo pwsh -NoProfile -Command Import-Module /tmp/AtomicRedTeam/invoke-atomicredteam/Invoke-AtomicRedTeam.psd1 …Not flaggedNot flagged
20/opt/microsoft/powershell/7/pwshpwsh -NoProfile -Command Import-Module /tmp/AtomicRedTeam/invoke-atomicredteam/Invoke-AtomicRedTeam.psd1 -Forc…Not flaggedNot flagged
21/usr/bin/hostname/usr/bin/hostnameNot flaggedNot flagged
22/usr/bin/hostname/usr/bin/hostnameNot flaggedNot flagged
23/usr/bin/hostname/usr/bin/hostnameNot flaggedNot flagged
24/usr/bin/whoami/usr/bin/whoamiNot flaggedNot flagged
25/usr/bin/id/usr/bin/id -uNot flaggedNot flagged
26/usr/bin/dashsh -c sudo touch /tmp/evilBinary; sudo chown root /tmp/evilBinary; sudo chmod g+xs /tmp/evilBinaryNot flaggedNot flagged
27/usr/bin/sudosudo touch /tmp/evilBinaryNot flaggedNot flagged
28/usr/bin/touchtouch /tmp/evilBinaryNot flaggedNot flagged
29/usr/bin/sudosudo chown root /tmp/evilBinaryNot flaggedNot flagged
30/usr/bin/chownchown root /tmp/evilBinaryNot flaggedNot flagged
31/usr/bin/sudosudo chmod g+xs /tmp/evilBinaryNot flaggedNot flagged
32 (expected)/usr/bin/chmodchmod g+xs /tmp/evilBinaryNot flaggedFlagged
33/usr/bin/ip/usr/sbin/ip -4 -br addr showNot flaggedNot flagged
34/usr/bin/sed/usr/bin/sed -n -e s/^.*UP\s* //pNot flaggedNot flagged
35/usr/bin/cut/usr/bin/cut -d / -f 1Not flaggedNot flagged
36/usr/bin/sleepsleep 2Not flaggedNot flagged
The stock rule looks for the literal text chmod u+s, so u+xs slips past it; the replacement flags the setuid and setgid forms in this table and misses two documented ones. The first three command lines are copied from docs/findings.md F-001 (telemetry from run 41838714456); the last five come from the evasion sweep in docs/robustness.md, including the two the replacement misses. Among the 36 applicable recorded events, 2 are the attack (the two chmod executions): the stock rule flagged 0 of them and the replacement flagged both. Four more events (two sudo and two dash lines) contain the command text but are not the chmod execution, and neither rule flags them. With JavaScript on, this plate becomes a bench where any command line can be tried. The browser demo re-implements the two rules' conditions in JavaScript; the compiler in the tripline repository is the source of truth. Rules: stock, replacement. Parity: scripts/test_bench_tripline.mjs checks the demo against the compiler's verdicts on 355 command lines.
Checked against
SigmaHQ stock rule; 2 attack events among 36 recorded events
Found, then left
As found Stock rule flagged 0 of the 2 attack events (36 events recorded)
As left Replacement rule flags 2 of 2. Gate: 13 of 14 scenarios, 98% weighted, 52% on unmodified SigmaHQ rules alone
Limits
One recording per scenario; 675-event benign corpus, Linux only; 1 Windows scenario pending Full limits
Technical description and verification record: instrument, reference standard, date, how to reproduce (self-issued, not accredited)

A CI gate for detection rules. It replays recorded attack telemetry through Sigma rules on every pull request and fails the build when a rule that should fire does not. Version 0.3.0 adds a benign corpus, a sweep of evasion variants and a coverage figure split by who wrote the passing rules.

Instrument
tripline v0.3.0, commit a0d6622
Reference standard
SigmaHQ rule c21c4eaa (Setuid and Setgid), replayed on telemetry recorded from Atomic Red Team T1548.001 tests on a GitHub-hosted Linux runner
Measured on
2026-11-11, Python 3.12.2, fresh clone at tag v0.3.0
Issued by
The author. Self-issued verification record, not accredited.
Reproduce
tripline replay --all
tripline replay t1548-001-setuid-setgid --show-events
tripline benign
tripline robustness

As foundThe stock setuid rule matched nothing, and false positives and evasion were not measured

SigmaHQ's Setuid and Setgid rule looks for the literal text chmod u+s. Atomic Red Team's T1548.001 tests run chmod u+xs, and chmod 4755, ug+s or a+s would slip past the same way. Replaying the recorded fixture through both rules (a small script against tripline's own API). The output counts every applicable event; of the 36, two are the attack (the two chmod executions), four are wrapper lines (sudo and dash) that contain the command text, and 30 are unrelated:

setuid and setgid are file mode bits that make a program run with the privileges of its owner or group. A root-owned program with the bit set is a classic way to keep root access (ATT&CK T1548.001).
Setuid and Setgid                                    applicable  36  matched 0
Setuid or Setgid Bit Set via Chmod (Any Mode Form)   applicable  36  matched 2

The first is the vendored upstream rule, unchanged. The second is the replacement described below. Write-up: docs/findings.md, F-001.

The gate itself, as of v0.2.1, also had gaps that the setuid finding did not show. The rows below are what a check of that version found.

Table, 7 rows: Reading ledger, as found (v0.2.1)
Reading ledger, as found (v0.2.1)
CheckExpectedObservedVerdictEvidence
Stock SigmaHQ rule c21c4eaa on the 2 attack events among 36 applicable eventsA match on the two chmod executions0 of 2 flaggedAs found: failrun 41838714456 failed 2026-11-04
False positivesA count of benign events that matchNo benign corpus. A rule that fired on every event would also passAs found: not measuredv0.2.1 README, limitations
EvasionA count of attack variants that still matchNo variants beyond the three command lines of F-001As found: not measuredv0.2.1 README, limitations
WeightsEach of the 62 weight points traceable to a sourceThe README says the weights are taken from Red Canary's 2026 ranking, mapped by hand; 17 of the 62 points are for techniques outside that ranking, and no document said whichAs found: partialv0.2.1 README
ATT&CK versionThe current matrixNavigator layer on ATT&CK v16, with the tactic Defense Evasion (since split in two) and T1562.008 (since replaced)As found: out of datev0.2.1 Navigator layer
Workflow supply chainThird-party actions pinned to a commit18 action references pinned to version tags only; zizmor 1.32.0 (auditor persona) reports 42 findings, 19 of them highAs found: unpinnedCHANGELOG 0.3.0, Pipeline hygiene
Windows scenariosRecorded telemetry for all 73 recorded, 4 pending (T1059.001, T1059.003, T1027, T1053.005); 10 of 14 scenarios passing, 68% weighted coverageAs found: 4 pendingv0.2.1 scorecard

As leftA replacement rule matches the two chmod runs, and the gaps are now measured

The replacement rule anchors on the chmod binary (and, since the sweep below found the gap, on busybox chmod) and handles symbolic and octal forms. Its two matches on the recorded fixture are the two chmod executions. The upstream rule stays in the rule set, so a future upstream fix is picked up automatically.

Rules written in this repository carry part of the headline. Six rules were written for this project (the setuid replacement, three AWS rules and two Windows rules), and five of the 13 passing scenarios depend on them. Coverage is therefore shown as two figures, always together: 98% of weight points have a passing scenario (61 of 62), but only 52% (32 of 62, 8 scenarios) pass on unmodified SigmaHQ rules alone. tripline computes the split from each expected rule's author field.

Table, 5 rows: The five passing scenarios that need a rule written in this repository (29 of 62 weight points)
The five passing scenarios that need a rule written in this repository (29 of 62 weight points)
ScenarioWeightRule written in this repositoryTelemetry
T1078.004 console login without MFA10AWS Console Login Without MFAPublic CloudTrail replay
T1059.003 cmd output redirect8CMD Output Redirected to a Relative Path From a Suspicious Working Directory; the SigmaHQ rule matched 0 of 11 applicable eventsRecorded, run 43090565998 failed 2026-11-11
T1530 S3 bucket exposure7AWS S3 Bucket Made Public via ACL or Bucket Policy; AWS S3 Bucket Enumeration by IAM User from Non-AWS ToolingPublic CloudTrail replay
T1053.005 scheduled task2Scheduled Task Created With a Command Interpreter or LOLBin Action; the SigmaHQ rule matched 0 of 6Recorded, run 43090565998 failed 2026-11-11
T1548.001 setuid and setgid2Setuid or Setgid Bit Set via Chmod (Any Mode Form); the SigmaHQ rule flagged 0 of the 2 attack eventsRecorded, run 41838714456 failed 2026-11-04

The two Windows rules were written from the first Windows run's failures and tuned to the command shape of those Atomic tests (F-003); a pass on them shows the rule fits that recording, not that it generalizes. The third newly recorded Windows scenario, T1059.001, passes on the vendored SigmaHQ rule Suspicious Execution of Powershell with Base64 (3 of 22 applicable events, including a real powershell.exe -e command line), after the scenario was narrowed: its download-cradle rule matched 0 of 75 script blocks because no automated atomic runs a cradle, so that half was removed rather than claimed.

Table, 9 rows: Reading ledger, as left (v0.3.0)
Reading ledger, as left (v0.3.0)
CheckExpectedObservedVerdictEvidence
Replacement rule on the same 2 attack eventsA match on the two chmod executions2 of 2 flaggedAs left: passdocs/findings.md F-001
Gate over the committed fixturesEvery scenario with a fixture passes13 passed, 0 failed, 1 pending (AWS 4 of 4, Linux 3 of 3, Windows 6 of 7)As left: passrun 43100952362 passed 2026-11-11
Weighted coverage, with its splitBoth figures stated together98% (61 of 62 points); 52% (32 of 62, 8 scenarios) on unmodified SigmaHQ rules aloneAs left: splitdocs/scorecard.md
False positives on a benign corpusA count2 matches from 1 of 6 evaluated rules on 675 events (488 process creations); 20 of 26 rules not evaluatedAs left: measureddocs/benign-corpus.md; run 43090057704 passed 2026-11-11
Evasion sweep, 251 command-line variantsA count of variants still caught200 caught: setuid 106 of 108, history clearing 71 of 103, cron 23 of 40; 0 of 4 alternative-tool variantsAs left: measureddocs/robustness.md
WeightsEach point traceable45 of 62 points follow Red Canary's public 2026 top 10 (weight = 11 minus rank, seven scenarios); 17 are hand-assigned (seven scenarios)As left: documenteddocs/weights.md
ATT&CK versionThe current matrixATT&CK v20: Defense Evasion split into Stealth and Defense Impairment; T1562.008 became T1685.002; the tactic grid uses the v20 namesAs left: v20docs/attack-version.md
Workflow supply chainActions pinned to a commitAll 21 action references pinned to a full commit SHA; zizmor 1.32.0: 42 findings before, 0 afterAs left: pinnedCHANGELOG 0.3.0
Windows scenariosRecorded telemetry for all 76 of 7 recorded and passing; T1027 still pending (its rule matched 0 of 15 events in the latest recording)As left: 1 pendingF-003; run 43090565998 failed 2026-11-11

The gate over the committed fixtures, run on 2026-11-11 from a fresh clone at tag v0.3.0 (commit a0d6622), Python 3.12.2:

Output, 18 lines: $ tripline replay --all
$ tripline replay --all
[   PASS] t1003-001-lsass-memory-dump (T1003.001, windows) - 70 events
     + 646ea171-dded-4578-8a4d-65e9822892e3  Process Memory Dump Via Comsvcs.DLL  (2/6 applicable events)
     + 5ef9853e-4d0e-4a70-846f-a9ca37d876da  Potential Credential Dumping Activity Via LSASS  (1/4 applicable events)
[PENDING] t1027-obfuscated-powershell-oneliner (T1027, windows) - 0 events
     ~ 44e24481-6202-4c62-9127-5a0ae8e3fe3d  Obfuscated PowerShell OneLiner Execution  (no fixtures)
[   PASS] t1053-003-cron-persistence (T1053.003, linux) - 42 events
     + 6b14bac8-3e3a-4324-8109-42f0546a347f  Scheduled Cron Task/Job - Linux  (1/26 applicable events)
     + 6c4e2f43-d94d-4ead-b64d-97e53fa2bd05  New Cron File Created  (1/3 applicable events)
[   PASS] t1059-003-cmd-suspicious-redirect (T1059.003, windows) - 26 events
     + 467585b7-9de8-40e5-bb2a-6626902ae295  CMD Output Redirected to a Relative Path From a Suspicious Working Directory  (2/11 applicable events)
[   PASS] t1078-004-console-login-without-mfa (T1078.004, aws) - 7 events
     + 3b1f0d2e-7c4a-4d5b-9e8f-2a6c1d0b9f41  AWS Console Login Without MFA  (1/7 applicable events)
     + 8ad1600d-e9dc-4251-b0ee-a65268f29add  AWS Root Credentials  (6/7 applicable events)
[   PASS] t1548-001-setuid-setgid (T1548.001, linux) - 84 events
     + 7f3c2b1a-6d4e-4a9b-9c8d-1e2f3a4b5c6d  Setuid or Setgid Bit Set via Chmod (Any Mode Form)  (2/36 applicable events)
...
gate passed

Trimmed: 6 of the 14 scenarios shown. The full run reports 13 passed, 0 failed, 1 pending, exit code 0.

Plate 2
  • passing
  • pending, no accepted recording

13 passed, 1 pending, 0 failed

The 14 scenarios: 13 pass and 1 is pending because no Windows recording has been accepted for it. Counts from docs/scorecard.json. 5 of the 13 passes depend on a rule written in this repository.

Plate 3
  • caught by an expected rule
  • not caught by any expected rule
  • One tick is one variant, in the order the sweep generates them.

23 of 40 command-line variants caught (T1053.003)

71 of 103 command-line variants caught (T1070.003)

106 of 108 command-line variants caught (T1548.001); the stock rule caught 0

0 of 4 alternative-tool variants caught (not part of the 251)

Evasion sweep: an expected rule catches 200 of 251 command-line variants of the three Linux attacks, and none of 4 alternative-tool variants. Each tick is one variant, in the order tripline robustness generates them (seed 20261111); re-running the sweep for this page reproduced docs/robustness.md exactly. Hatched ticks are variants no expected rule caught: for cron, crontab given a path outside /tmp/; for history clearing, shell builtins, redirects and whitespace or quoting in the command line; for setuid, a file under /usr/share/doc/ (excluded on purpose) and a renamed copy of chmod. The variant lists are hand-written, replayed as audit events and not executed, and not exhaustive, so a high count is not proof of robustness. The setuid replacement missed busybox chmod u+s until this sweep found it; that is fixed. The other misses are documented in F-004 and not fixed.

Plate 4
  • matched no rule
  • matched a rule (false positive)
  • Each tick is about 10 events; the counts are in the labels.

2 of 675 events matched a rule: New Cron File Created, /etc/cron.daily/chkrootkit

False positives: 2 of 675 benign events matched a rule, both from SigmaHQ's New Cron File Created on a chkrootkit package install. The corpus is one 33-second auditd recording of ordinary work (package installs, git, a build, chmod without setuid, reads of cron files) on one GitHub-hosted Ubuntu runner, run 43090057704 passed 2026-11-11. Only 6 of the 26 rules had applicable events: the 20 Windows and AWS rules were not evaluated, so this is a Linux-only figure, and zero matches on the other five rules does not show they are quiet on a real fleet. The workload deliberately avoids setuid chmod, so it does not test near misses.

Plate 5 tripline coverage report: 98 percent weighted coverage next to 52 percent on unmodified SigmaHQ rules alone, 13 of 14 scenarios passing, 2 false-positive matches on 675 benign events, 200 of 251 evasion variants caught, and an ATT&CK v20 matrix by tactic with Stealth and Defense Impairment columns
The coverage report shows 98% and 52% side by side. Top of the live report, generated by tripline report from the committed scorecard (2026-11-11 21:11 UTC, tripline 0.3.0). Open the live page.
Table, 9 rows: Results
Results
MeasureValueSource
Tests212 (91% line+branch coverage)CHANGELOG 0.3.0; run 43100952362 passed 2026-11-11
Scenarios14 techniques: 4 AWS (public replay), 3 Linux and 7 Windows (recorded on runners)docs/scorecard.md
Gate13 passed, 0 failed, 1 pendingdocs/scorecard.json
Weighted coverage98% of weight points have a passing scenario (61 of 62); 52% (32 of 62) on unmodified SigmaHQ rules alone, from 8 of the 13 passing scenariosdocs/scorecard.json
False positives2 matches from 1 of 6 evaluated rules, 675 events; 20 of 26 rules not evaluateddocs/benign-corpus.md; run 43090057704 passed 2026-11-11
Evasion sweep200 of 251 command-line variants caught; 0 of 4 alternative-tool variantsdocs/robustness.md
Stock setuid ruleStock rule: 0 of the 2 attack events among 36 recorded events, and 0 of 108 sweep variants. Replacement: 2 of 2 and 106 of 108docs/findings.md F-001, F-004; emulate-linux run 41838714456 failed 2026-11-04 (failed, see limits)
Windows fixtures6 of 7 scenarios recorded and passing (T1003.001, T1047, T1105, T1059.001, T1059.003, T1053.005)emulate-windows run 42917025570 failed 2026-11-10 and run 43090565998 failed 2026-11-11 (both show failed, see limits)
Releasev0.3.0GitHub release
Technical details

How it works

design, implementation points and diagram

The problem

Detection teams count rules and ATT&CK tags. Neither number says whether a rule still fires on the behavior it is named after, and a syntax linter passes rules that never matched anything.

tripline treats a rule like code under test: each scenario names a technique, the Sigma rules that must fire, and recorded telemetry to replay. If an expected rule misses, the exit code is 1. Separately, every rule is replayed over a benign corpus and over generated variants of the attack commands, and the counts are reported.

It is not the first tool to test detections against recorded or generated telemetry: SigmaHQ's regression data, rsigma backtest, SCYTHE's pipeline and Splunk's attack_data each do a version of this. tripline adds a small scenario layer (the technique, the expected rules and the fixture live together in one repository), a plain-Python evaluator that runs offline, and a weighted scorecard. It does not replace any of them (comparison).

What it does

14 scenarios cover 14 ATT&CK techniques across AWS, Linux and Windows. Linux and Windows telemetry comes from Atomic Red Team tests run on GitHub-hosted CI runners; the AWS plane replays public CloudTrail data (Stratus Red Team detonation logs and the flaws.cloud dataset). Rules are compiled to Python and evaluated offline against committed NDJSON, so the whole gate runs in seconds with no SIEM. Output is a JSON scorecard, JUnit XML, an ATT&CK Navigator layer (ATT&CK v20) and a static HTML report.

Coverage is weighted by how common each technique is, so breaking a common technique moves the headline more than breaking a rare one. Weights run from 1 to 10: 45 of the 62 points follow Red Canary's public 2026 top-10 ranking (weight = 11 minus rank, seven scenarios) and 17 are hand-assigned low values for the seven scenarios outside that top 10 (docs/weights.md).

Plate 6tripline data flowRecorded telemetry is committed as fixtures, Sigma rules are compiled to Python predicates, a logsource router decides which events each rule sees, and the scorer fails the build if an expected rule misses.Emulate and recordAtomic Red Team on CI runners;public AWS logsAFixtures committed to the reporun.ndjson and manifest.json,reviewed by a personBCompile Sigma rules topredicatespySigma parses; tripline walksthe condition treeCLogsource routera rule only sees events itslogsource matchesDReplay and score per scenarioweights from technique prevalenceEGate: exit 1 if an expectedrule missesscorecard, JUnit, ATT&CKNavigator layer, HTMLF
Recorded telemetry is committed as fixtures, Sigma rules are compiled to Python predicates, a logsource router decides which events each rule sees, and the scorer fails the build if an expected rule misses.
Describe this diagram
  1. Emulate and record. Atomic Red Team on CI runners; public AWS logs.
  2. Fixtures committed to the repo. run.ndjson and manifest.json, reviewed by a person.
  3. Compile Sigma rules to predicates. pySigma parses; tripline walks the condition tree.
  4. Logsource router. a rule only sees events its logsource matches.
  5. Replay and score per scenario. weights from technique prevalence.
  6. Gate: exit 1 if an expected rule misses. scorecard, JUnit, ATT&CK Navigator layer, HTML.

Three implementation points

Compiling the Sigma condition tree into Python predicates

pySigma parses each rule and applies value modifiers. tripline then walks the resulting condition tree and turns every leaf into a closure over an event (tripline/engine/compiler.py): contains, startswith, endswith, all, re, windash, base64offset, cidr, fieldref, numeric comparisons, 1 of and all of. Anything it cannot evaluate raises UnsupportedRuleError, and a scenario whose expected rule failed to compile counts as failed. A gate that cannot evaluate a rule must not pass it. A code review of 0.2.1 found tripline breaking its own rule: timestamp part modifiers such as |hour compiled but never matched. They now evaluate or raise.

Telemetry sliced by run GUID

The Linux workflow wraps every Atomic test in two marker commands, tripline-marker RUN TEST start and ... end. They are ordinary execve events, so attribution lives inside the audit log itself. The collector merges auditd records by serial, hex-decodes EXECVE arguments and keeps only what falls between a pair of markers carrying the run's GUID. Before 0.2.1 a marker from another run in the same log could open or close a window, or leak events from an earlier run; markers with a foreign GUID are now ignored. For cloud data, tripline cloud build shifts eventTime while keeping offsets and appends tripline/<run-guid> to userAgent.

A routing problem is not a missed detection

A rule is evaluated only on events its logsource matches, and results read 1/7 applicable events. If a rule has no applicable events at all, that is reported as a routing problem (wrong product, category or service). 0/7 applicable events (rule did not match) is the real miss. They are separate values of outcome in scorecard.json, the CLI, JUnit and the HTML report, because the fix for each is different. The benign corpus follows the same rule: a rule with no applicable benign events is listed as not evaluated, never as zero false positives.

Sigma is an open, vendor-neutral format for detection rules. A rule's logsource says which kind of log it applies to.

Tolerance and limitsWhat the numbers do not show

  • Three emulation runs linked above show failed. The Linux run 41838714456 failed because the stock setuid rule missed (the finding itself). The two Windows recording runs failed their last step, which asserts that expected rules fire. In the later one, 43090565998, that step failed four scenarios (T1027, T1053.005, T1059.001, T1059.003) against the scenario definitions of that commit; the scenarios were then changed (two now expect rules written in this repository, one was narrowed) and the committed fixtures pass against the changed definitions. The workflows still upload the recorded telemetry, and a maintainer reviews it before committing it as a fixture. The passing gate is the ci run, which replays the committed fixtures.
  • 98% is not the whole story. Only 52% of weight points pass on unmodified SigmaHQ rules alone; the rest depend on rules written in this repository, two of them tuned to the Atomic tests' command shape. The pending scenario counts as zero in both figures.
  • A pass is a recall check on one recording. A scenario passes when each expected rule fires on at least one event. The benign corpus is one 33-second Linux recording, so a Windows or AWS rule that fired on everything would still pass.
  • The evasion sweep covers three Linux scenarios. Its variants are hand-written, evaluated as audit events and never executed, and a catch rate over them does not measure real-world evasion.
  • One Windows scenario is still pending. T1027 has no accepted recording: its expected rule matched 0 of 15 events in the latest recording, and re-labelling it to expect another rule would raise the number without testing an obfuscation rule (F-003).
  • Small coverage. 14 techniques out of several hundred. The 98% and the 52% are progress against this repo's own list, not a statement about any organization. 17 of the 62 weight points are hand-assigned, and the ranking behind the other 45 is Red Canary's customer detections, a proxy for prevalence in other environments.
  • The cloud plane is replayed, not detonated by this project. The records are real CloudTrail from public datasets, but the cloud rules written for the project sit beside fixtures from the same datasets, so those passes show the wiring works, not that the rules generalize.
  • A different evaluator from production. There is no parity test against a real SIEM backend, so a pass is necessary, not sufficient.
Evidence

Where each number comes from

12 links

Chips show the state of each GitHub Actions run as captured when this page was built. The page does not call GitHub at runtime.

Release
v0.3.0
Linux recording run (setuid finding)
run 41838714456 failed 2026-11-04
Benign corpus recording run
run 43090057704 passed 2026-11-11
Windows recording run (first three)
run 42917025570 failed 2026-11-10
Windows recording run (three more)
run 43090565998 failed 2026-11-11
Findings F-001 to F-004
docs/findings.md
Evasion sweep
docs/robustness.md
Changelog
CHANGELOG.md