Skip to main content

Exit Codes & Outcomes

The single source of truth for how a test execution's raw exit code, EDR correlation flags, stage labels in the UI, and final outcomes in the Defense Score and SB compliance report relate to each other.

1. Exit codes — what the agent reports​

CodeNameMeaningCategory
0NormalExitRan, exited normally — outcome varies by contextinconclusive
1BinaryNotRecognizedBinary not recognized / permission deniedcontextual
101UnprotectedAttack succeeded — endpoint did nothingfailed
105FileQuarantinedOnExtractionAV quarantined the file during extractionprotected
126ExecutionPreventedExecution blocked/prevented by AV/EDR policyprotected
127QuarantinedOnExecutionQuarantined at execution attemptprotected
200NoOutputUpload confirmation — not a test outcomeexcluded from everything
259StillActiveProcess timeout (Windows STILL_ACTIVE)inconclusive
260BlockedPreExecutionBlocked before launch by an ASR/app-control policy — technique never evaluatedinconclusive
999UnexpectedTestErrorProcess died unexpectedly — crash, missing prereqs, or EDR killerror
> 2³¹−1 (e.g. 0xE0000027)(unsigned NTSTATUS-style)EDR kill codes and similar — coerced to 999 at ingestion, original preserved in event.ERROR_rawerror
Exit codes never prove prevention

999 is ambiguous by itself — it means "something killed the process or it broke." Attribution only ever comes from EDR alert evidence (§2), never from the exit code. This is the "evidence, not exit codes" principle.

2. Correlation flags — written by the enrichment pass​

The enrichment pass (every 5 minutes) scans recent test docs and correlates them against synced EDR alerts:

FlagSet whenQualification
defender_detected / crowdstrike_detectedAlert evidence matches hostname + bundle UUID in filename/path, within −5/+30 min of the test—
*_stage_detectedAlert evidence names the stage's own binary (<bundle-uuid>-<control>.exe)—
crowdstrike_prevented / defender_prevented (+ *_prevention_action)Matched alert carries a vendor-confirmed prevention dispositionStage-binary matches always qualify. Bundle-wide matches only if the stage never reached a conclusive outcome (exit ∉ {101, 105, 126, 127}). A failed kill (CS kill_action_failed) never qualifies

Vendor prevention dispositions, normalized to a shared vocabulary (process_killed / quarantined / blocked):

Normalized actionCrowdStrike (pattern_disposition_details)Defender (evidence[].remediationStatus)
process_killedkill_process ("Prevention, process killed.")blocked (blocked while executing)
blockedprocess_blocked / *_blockedprevented (never ran)
quarantinedquarantine_fileremediated (completed)
(absent — detection only)no disposition / kill_action_failedactive, none, pendingApproval, …

Alert docs also carry portal_url — the exact per-alert console deep link (Falcon's falcon_host_link, Defender's alertWebUrl) — used by the alert drawers and execution detail panels.

Which documents are scanned: exit codes [0, 1, 101, 105, 126, 127, 259, 999] — so EDR-killed stages (999) are eligible. Deliberately not scanned:

  • 200 — upload confirmation, not a test outcome
  • 260 — a policy block before execution, including the ASR rule the platform excludes by design (see Defender ASR exclusion); counting it would conflate our own configuration with detection

Time-window semantics: an alert correlates with a test when its created_at falls in the −5/+30-minute window, or when its last-update time does and the alert is still active (new/in_progress/reopened). Terminal alerts (resolved/closed) re-enter a window via updates only when the update happened more than 60 s after the alert's resolution time — a post-resolution bump is a genuine re-fire of an aggregated alert (Defender reuses one alert for repeat detections of the same binary on a host, bumping its last-update time without reopening it), while the resolution itself — including our own auto-resolve — never qualifies. In the execution detail panels such alerts are annotated from the re-fire time, e.g. "re-fired 3m after · first seen Oct 10", rather than by their original creation time. (Defender only — see docs/edr-alert-refire-watermark.md; no alert aggregation has been observed for CrowdStrike.)

3. Stage labels in the UI​

Each stage row in the executions view resolves to exactly one label, by precedence (top wins):

LabelConditionMeaning
Protectedexit ∈ {105, 126, 127}Endpoint blocked/quarantined at the OS level
Preventedcrowdstrike_prevented or defender_prevented (or detected flags + confirmed prevention action)Vendor-confirmed kill/quarantine/block — shows the action, e.g. "Prevented (process killed)"
Detected*_stage_detected or *_detectedThe EDR saw it and alerted, but didn't stop it
Unprotectedexit 101Attack ran to completion, nothing reacted
Inconclusiveeverything else (0, 1, 259, 260, 999, missing)Can't judge — error, timeout, policy block, or unconfirmed kill

Bundle rows roll up with the same precedence: Protected > Prevented > Detected > Unprotected.

4. Final outcomes​

Defense Score (Analytics dashboards)​

  • Strict score: protected (105/126/127) / conclusive (101 + 105 + 126 + 127)
  • Combined score ("detected counts as protected"): adds defender_detected OR crowdstrike_detected to the protected side. *_prevented ⊆ *_detected for both vendors, so prevented stages are covered automatically.
  • Error codes (0, 1, 259, 999) feed the Error Rate, not the score.
  • 200 and 260 are excluded from both.

SB compliance report​

Outcomes: PREVENTED / DETECTED / MISSED / EXPOSED / NOT_RUN / ERROR.

ExitEDR flagsOutcomeAttribution
105 / 126 / 127anyPREVENTEDpreventedBy: vendor(s)
999 / 259 / 0 / nonecrowdstrike_prevented or defender_preventedPREVENTEDpreventedBy: the preventing vendor(s), both when both flagged
101 / 0detected flag(s)DETECTEDdetectedBy: vendor(s), both when both flagged
101 / 0none, tactic ∈ {exfiltration, impact}EXPOSED—
101 / 0none, other tacticsMISSED—
200 / 259 / 260anyERROR (with "inconclusive" note)—
— (no document)—NOT_RUN—

5. Worked examples​

ScenarioExitEvidenceLabel / SB outcome
Falcon kills the ransomware stage at launch0xE0000027 → 999Stage-binary alert match, process_killed dispositionPrevented (process killed) / PREVENTED
Defender blocks a stage binary mid-execution999Stage-binary alert match, remediationStatus: blockedPrevented (process killed) / PREVENTED
Sibling stage already ran to completion before the kill101Bundle-wide alert match onlyDetected / DETECTED — a succeeded stage is never upgraded to Prevented
Stage runs, nothing reacts101, no alertnoneUnprotected / MISSED (or EXPOSED for exfiltration/impact tactics)
Stage dies, no alert anywhere999, no alertnoneInconclusive / ERROR — a kill is never assumed
Defender ASR policy blocks launch260policy, not detectionInconclusive / ERROR — never counted
Defender quarantines at extraction105—Protected / PREVENTED