Documentation

Technical reference for ThreatEcho - detection engineering for AI agents. Adversary campaign simulation, tool-call policy enforcement, and detection gap analysis across MITRE ATT&CK, MITRE ATLAS, and OWASP LLM Top 10.

Architecture

High-Level Pipeline

 ┌──────────────────────────────────────────────────────────────────┐
 │                          CLI (main.go)                           │
 │  validate│lint│simulate│run│deploy│gap│policy│export│agent│ ...  │
 └───────────────────────────────┬──────────────────────────────────┘
                                 │
            ┌────────────────────┼────────────────────┐
            ▼                    ▼                    ▼
    ┌───────────────┐    ┌──────────────┐    ┌────────────────┐
    │   Campaign    │    │    Policy    │    │   Campaign     │
    │   Loader      │    │    Loader    │    │   Loader       │
    │  (YAML parse, │    │ (YAML parse, │    │   + Validator  │
    │   var expand) │    │   validate)  │    │   + Lint       │
    └──────┬────────┘    └───────┬──────┘    └────────┬───────┘
           │                     │                    │
           ▼                     │                    │
    ┌───────────────┐            │           ┌────────────────┐
    │ DAG Resolver  │            │           │     MITRE      │
    │ (Kahn's alg)  │            │           │     Lookup     │
    └──────┬────────┘            │           │  ATT&CK/ATLAS  │
           │                     │           │   /OWASP LLM   │
           ▼                     │           └────────┬───────┘
    ┌──────────────┐             │                    │
    │    Engine    │             │                    │
    │  Simulate or │             │                    │
    │      Run     │             │                    │
    └──────┬───────┘             │                    │
           │                     │                    │
    ┌──────┴───────┐             │                    │
    │  Executor    │             │                    │
    │  Noop (sim)  │             │                    │
    │  Shell (live)│             │                    │
    └──────┬───────┘             │                    │
           │                     │                    │
           ▼                     ▼                    ▼
    ┌─────────────────────────────────────────────────────────┐
    │                     Report Pipeline                     │
    │  ┌──────────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌─────┐ ┌────┐ │
    │  │Gap Report│ │ Text │ │ JSON │ │SARIF │ │JUnit│ │HTML│ │
    │  │ (risk    │ │      │ │      │ │v2.1.0│ │ XML │ │ MD │ │
    │  │  scored) │ │      │ │      │ │      │ │     │ │    │ │
    │  └──────────┘ └──────┘ └──────┘ └──────┘ └─────┘ └────┘ │
    └────────────────────────────┬────────────────────────────┘
                                 │
                  ┌──────────────┼──────────────┐
                  ▼              ▼              ▼
           ┌─────────────┐ ┌───────────┐ ┌────────────┐
           │  Navigator  │ │   Sigma   │ │  Compare   │
           │  Layer JSON │ │   Rules   │ │  (delta)   │
           └─────────────┘ └───────────┘ └────────────┘

Package Structure

src/
├── cmd/threatecho/          CLI entry point, command routing, flag parsing
├── internal/
│   ├── campaign/            Campaign loading, validation, linting, DAG resolution
│   │   ├── types.go         Core types: Campaign, Meta, Stage, Execute, Expect, Duration
│   │   ├── loader.go        YAML loading, variable expansion ({{var}} templates)
│   │   ├── validator.go     Structural + semantic validation (technique format, tactic validity)
│   │   ├── lint.go          Quality checks beyond validation (warnings, suggestions)
│   │   ├── graph.go         DAG resolution: Kahn's algorithm + DFS cycle detection
│   │   └── ...              22 additional files: scoring, search, import, stats, audit, etc.
│   ├── engine/              Campaign execution + analysis
│   │   ├── engine.go        RunResult, StageResult, Options, TacticCoverage
│   │   ├── simulate.go      Dry-run: walks DAG with Noop executor
│   │   ├── run.go           Live execution: walks DAG with Shell executor
│   │   ├── threatmodel.go   STRIDE threat model generation per agent
│   │   ├── risk.go          Unified risk posture scoring (A-F grade)
│   │   ├── compliance.go    Framework mapping (NIST AI RMF, CSF, 800-53, CIS, OWASP, ATLAS)
│   │   ├── baseline.go      Deployment baseline capture and drift detection
│   │   ├── attacktree.go    Attack tree generation with probabilistic risk scoring
│   │   └── runner.go        Precondition evaluation delegator
│   ├── executor/            Stage execution backends
│   │   ├── executor.go      Executor interface: Execute, Cleanup, Name
│   │   ├── noop.go          Simulation executor (describes what would happen)
│   │   ├── shell.go         Live shell executor (sh -c / cmd /c)
│   │   ├── shell_unix.go    Unix process group handling
│   │   └── shell_windows.go Windows process group handling
│   ├── gap/                 Detection gap analysis
│   │   ├── gap.go           Analyze(): produces GapReport with risk scoring
│   │   ├── compare.go       Compare(): before/after delta between two GapReports
│   │   └── coverage.go      Technique-level coverage analysis
│   ├── matrix/              ATT&CK technique×tactic coverage heatmap
│   ├── mitre/               Framework registries and lookup
│   │   ├── attack.go        ATT&CK technique and tactic registry
│   │   ├── atlas.go         MITRE ATLAS technique and tactic registry
│   │   ├── owasp_llm.go     OWASP LLM Top 10 registry
│   │   └── lookup.go        Unified lookup: ValidTechniqueExists, ClassifyFramework
│   ├── navigator/           ATT&CK Navigator layer v4.5 generation
│   ├── policy/              Agent tool-call policy engine
│   │   ├── policy.go        Policy types, LoadPolicy, ValidatePolicy, Evaluate
│   │   └── ...              21 additional files: lint, drift, compliance, simulation, etc.
│   ├── report/              Output format renderers
│   │   ├── text.go          ANSI text report
│   │   ├── json.go          JSON report
│   │   ├── sarif.go         SARIF v2.1.0 for gap and policy results
│   │   ├── junit.go         JUnit XML for gap and policy results
│   │   ├── gap_html.go      Self-contained HTML gap report
│   │   ├── gap_md.go        GitHub-Flavored Markdown gap report
│   │   └── ...              Additional format renderers
│   ├── orchestrator/        Remote deployment engine
│   │   ├── orchestrator.go  Run, deployOne, Deployer interface + factory
│   │   ├── ssh.go           SSHDeployer (agentless) + AgentSSHDeployer
│   │   ├── winrm.go         WinRMDeployer (agentless) + AgentWinRMDeployer
│   │   ├── target.go        Target, TargetInventory, LoadInventory
│   │   └── report.go        Deployment execution report
│   ├── runner/              Precondition engine (platform, elevation, tool checks)
│   ├── trace/               Agent execution trace analysis and replay
│   ├── agent/               Agent inventory, trust, chain analysis, attestation
│   ├── scenario/            Scenario store (campaign→trace→policy-eval pipeline)
│   ├── compliance/          Compliance framework mapping
│   ├── sigma/               Sigma detection rule scaffold generation
│   │   ├── sigma.go         Generate(): campaigns → Sigma YAML rule scaffolds
│   │   ├── logsource.go     Telemetry-to-logsource mapping table
│   │   └── detections.go    Technique-to-detection lookup
│   └── telemetry/           Telemetry type registry (108 types, 16 categories)
├── cmd/threatecho-agent/    Agent binary (run, check, version commands)
└── pkg/
    └── version/             Build-time version injection (ldflags)

Data Flow

Campaign Loading

campaign.yaml ──► Load(path)
                   │
                   ├── os.ReadFile + yaml.Unmarshal → Campaign struct
                   ├── expandVariables: {{key}} → value in stage fields
                   │     (Name, Description, Target, Payload, Commands,
                   │      Cleanup, Args values, Artifacts, IOCs)
                   └── Return *Campaign

Validation Pipeline

*Campaign ──► Validate(c)
               │
               ├── api_version: required, non-empty
               ├── kind: must equal "Campaign"
               ├── meta.name: required
               ├── meta.adversary: required
               ├── meta.severity: if set, must be critical|high|medium|low|info
               ├── stages: at least one required
               ├── per-stage:
               │   ├── id: required, unique across campaign
               │   ├── name: required
               │   ├── technique: required, must match:
               │   │   ├── ATT&CK: ^T\d{4}(\.\d{3})?$
               │   │   ├── ATLAS:  ^AML\.T\d{4}$
               │   │   └── OWASP:  ^LLM\d{2}$
               │   ├── tactic: required, valid ATT&CK or ATLAS tactic
               │   ├── execute.type: required (shell|powershell|http|file|registry|service|process|dns|manual)
               │   ├── on_failure: if set, must be abort|skip|continue
               │   └── platform[]: each must be windows|linux|macos
               ├── cross-references: depends_on[] and on_success validated
               └── cycle detection: DFS three-color algorithm

Lint Checks

LevelCheck
Infometa.description empty
Infometa.objective empty
Infometa.mitre_version not set
InfoStage has telemetry but no detections (will flag as gap)
WarningTechnique ID not found in registry (valid format but not indexed)
WarningFramework/tactic cross-mismatch (ATT&CK technique with ATLAS tactic)
WarningStage has no expected telemetry (gap analysis will be limited)
WarningShell stage with no commands
WarningHTTP stage with no target URL
WarningUnrecognized telemetry type (with did-you-mean suggestions)

DAG Execution Model

Stages form a directed acyclic graph through depends_on references. The engine resolves execution order using Kahn's algorithm for topological sorting.

1. Build in-degree map and adjacency list from depends_on edges
2. Seed queue with all zero-in-degree stages (no dependencies)
3. BFS loop:
   a. Dequeue stage
   b. Append to ordered list
   c. For each child: decrement in-degree; if zero, enqueue
4. If len(ordered) != len(stages) → error: dependency cycle detected

The separate detectCycle() function uses DFS with three-color marking (white/grey/black) to produce human-readable cycle descriptions.

Execution Sequence

ordered stages ──► Engine (simulate or run)
                     │
                     for each stage in topological order:
                     │
                     ├── Platform filter: skip if stage platforms don't match
                     ├── Delay: sleep stage.Delay (live only)
                     ├── Timeout: context.WithTimeout(stage.Timeout)
                     ├── Execute: Noop (sim) or Shell (live)
                     │   └── Shell: sequential commands, stop on first failure
                     ├── Record: StageResult (Order, Exec, Skipped, SkipMsg)
                     ├── Cleanup: best-effort, runs ALL cleanup commands
                     └── Progress callback (live runs)

Framework Abstraction

ThreatEcho unifies three security frameworks through a common Stage type. The technique field accepts IDs from any framework; the tactic field uses shared tactic short names.

FrameworkTechnique PatternExampleTactic Source
MITRE ATT&CKT\d{4}(.\d{3})?T1566.001ATT&CK 14 tactics
MITRE ATLASAML.T\d{4}AML.T0051ATLAS 7 tactics
OWASP LLM Top 10LLM\d{2}LLM06Uses ATLAS tactics

Classification is prefix-based (T → ATT&CK, AML. → ATLAS, LLM → OWASP). The mitre.ClassifyFramework() function routes to the correct registry for name resolution, tactic validation, and reference URL generation.

ATT&CK Tactics (14)

reconnaissance → resource-development → initial-access → execution →
persistence → privilege-escalation → defense-evasion → credential-access →
discovery → lateral-movement → collection → command-and-control →
exfiltration → impact

ATLAS Tactics (7)

reconnaissance → resource-development → initial-access →
ml-attack-staging → ml-model-access → exfiltration → impact

Output Format Pipeline

The same analysis result feeds multiple output renderers. Each format serves a different consumer.

Gap Analysis Reports

FormatFunctionConsumer
Text (ANSI)report.GapTextReport()Terminal, human review
JSONreport.GapJSONReport()APIs, dashboards, downstream tooling
SARIF v2.1.0report.GapSARIFReport()GitHub Code Scanning, VS Code
JUnit XMLreport.GapJUnitReport()Jenkins, GitHub Actions, GitLab CI
HTMLreport.GapHTMLReport()Stakeholder reports, email, archival
Markdownreport.GapMarkdownReport()GitHub wikis, READMEs, PR comments

Policy Evaluation Reports

FormatFunctionConsumer
Text (ANSI)report.PolicyTextReport()Terminal, human review
JSONreport.PolicyJSONReport()Programmatic consumption
SARIF v2.1.0report.PolicySARIFReport()GitHub Code Scanning
JUnit XMLreport.PolicyJUnitReport()CI test reporters

SARIF Rule IDs

Rule IDNameLevelGap Type
TE-GAP-001DetectionMissingwarningTelemetry defined, no detection rules
TE-GAP-002TelemetryMissingerrorNo expected telemetry at all
TE-GAP-003TacticUncoveredwarningEntire tactic has zero stage coverage

Risk Scoring

Gap TypeTactic CategoryRisk Level
Tactic uncoveredinitial-access, execution, exfiltrationcritical
Tactic uncoveredcredential-access, lateral-movement, impacthigh
Tactic uncoveredall other tacticsmedium
Detection missinginitial-access, execution, exfiltrationhigh
Detection missingall other tacticsmedium
Telemetry missingany tacticmedium

Score Calculation

weighted = (critical × 10) + (high × 5) + (medium × 2) + (low × 1)
max_possible = total_gaps × 10
score = (weighted / max_possible) × 100

Score ranges: 0-19 = low, 20-39 = medium, 40-69 = high, 70-100 = critical

Design Principles

Writing Campaigns

Campaigns are the core unit of work in ThreatEcho. Each campaign defines an adversary emulation plan as YAML: a sequence of stages that map to MITRE ATT&CK, MITRE ATLAS, or OWASP LLM Top 10 techniques. ThreatEcho loads, validates, simulates, and analyzes campaigns to produce detection gap reports, policy evaluations, and Sigma rule scaffolds.

Campaign File Layout

campaigns/
├── apt29-cozy-bear/
│   └── campaign.yaml
├── fin7-carbanak/
│   └── campaign.yaml
└── llm-agent-hijack/
    └── campaign.yaml

Campaign YAML Schema

api_version: v1         # Required. Always "v1".
kind: Campaign          # Required. Always "Campaign".
meta: { ... }           # Required. Campaign metadata.
variables: { ... }      # Optional. Template variables for stage fields.
stages: [ ... ]         # Required. At least one stage.

Meta Block

FieldTypeRequiredDescription
namestringYesCampaign identifier. Used in reports, Sigma rule IDs, gap keys.
adversarystringYesThreat actor or group name.
descriptionstringNoFree-text campaign description. Lint warns if empty.
objectivestringNoWhat the campaign tests or proves.
severitystringNocritical, high, medium, low, or info.
mitre_versionstringNoATT&CK version (e.g., "15.1"). Lint warns if empty.
tagslistNoArbitrary tags for filtering and search.
authorslistNoCampaign authors.
referenceslistNoURLs to threat reports, advisories, or emulation plans.
createdstringNoISO date.
modifiedstringNoISO date of last modification.

Variables

The variables block defines key-value pairs substituted into stage fields at load time. Any {{key}} in a supported field is replaced with the corresponding value.

variables:
  target_host: "192.168.1.10"
  c2_server: "https://c2.example.com"
  payload_path: "/tmp/payload.bin"

Variables expand in: name, description, execute.target, execute.payload, execute.commands[], execute.cleanup[], execute.args (values), expect.artifacts[], expect.iocs[]. Variables do not expand in id, technique, tactic, or expect.telemetry/expect.detections.

Stage Structure

Each stage represents one adversary action in the kill chain.

FieldTypeRequiredDescription
idstringYesUnique identifier within the campaign.
namestringYesHuman-readable stage name.
techniquestringYesTechnique ID: ATT&CK (T1566), ATLAS (AML.T0051), OWASP (LLM06).
tacticstringYesTactic short name. Must be a valid ATT&CK or ATLAS tactic.
platformlistNowindows, linux, macos. If set, stage is skipped on other platforms.
depends_onlistNoStage IDs that must complete first. Defines the DAG edges.
executeobjectYesWhat to execute. See Execute Types.
expectobjectNoExpected telemetry, detections, artifacts, IOCs.
on_failurestringNoabort (default), continue, or skip.
timeoutdurationNoStage execution timeout (e.g., "30s", "5m").
delaydurationNoWait before executing.

Technique ID Formats

FrameworkPatternRegexExamples
ATT&CKT + 4 digits + optional . + 3 digits^T\d{4}(\.\d{3})?$T1566, T1566.001
ATLASAML.T + 4 digits^AML\.T\d{4}$AML.T0051, AML.T0043
OWASP LLMLLM + 2 digits^LLM\d{2}$LLM01, LLM06

Execute Types

FieldTypeDescription
typestringRequired. shell, powershell, http, file, registry, service, process, dns, manual.
commandslistShell commands to execute.
payloadstringPayload content or path.
targetstringTarget URL or host.
argsmapKey-value arguments (e.g., method, content_type).
cleanuplistCommands run after stage completes (best-effort).
elevatedboolWhether the stage requires elevated/root privileges. Default: false.

Shell Execution

execute:
  type: shell
  commands:
    - 'echo "[SIM] Scanning network"'
    - 'nmap -sP {{target_host}}/24'
  cleanup:
    - 'rm -f /tmp/scan_results.txt'
  elevated: false

In simulation mode (threatecho simulate), shell stages describe what would happen without executing. In live mode (threatecho run), commands run sequentially in sh -c (Unix) or cmd /c (Windows), stopping on the first failure.

HTTP Execution

execute:
  type: http
  target: "https://agent.corp.example.com/api/v1/chat"
  args:
    method: POST
    content_type: "application/json"
    payload: '{"message": "test"}'

Expect Block

The expect block defines what telemetry and detections the stage should produce. This drives gap analysis, Sigma rule generation, and policy evaluation.

FieldTypeDescription
telemetrylistExpected telemetry types that should fire.
detectionslistExpected detection rule names that should alert.
artifactslistFiles, registry keys, or other artifacts produced.
iocslistIndicators of compromise (IPs, domains, hashes).

Telemetry Types (selected)

CategoryTypes
Processprocess_create, process_access, image_load, create_remote_thread
Filefile_create, file_modify, file_delete, file_read, file_write, file_rename
Networknetwork_connection, dns_query, tls_handshake
Registryregistry_set, registry_add, registry_delete, registry_event
Authlogon_event, logoff_event
Executionscript_execution, pipe_created, scheduled_task_create
Emailemail_sent, email_delivered
AI Agentprompt_log, guardrail_trigger, tool_call, api_call, embedding_query, vector_store_write, inter_agent_message

Examples

Minimal Campaign

api_version: v1
kind: Campaign

meta:
  name: example-minimal
  adversary: Unknown
  severity: medium

stages:
  - id: recon
    name: Network Scan
    technique: T1016
    tactic: discovery
    execute:
      type: shell
      commands:
        - 'echo "[SIM] Scanning network"'
    expect:
      telemetry: [process_create, network_connection]
      detections: [network_scan_detected]

AI Agent Campaign (ATLAS + OWASP)

api_version: v1
kind: Campaign

meta:
  name: agent-prompt-injection
  adversary: AI-Threat-Actor
  description: "Prompt injection against enterprise AI agent"
  severity: critical
  tags: [ai-agent, prompt-injection, owasp-llm]

variables:
  agent_url: "https://agent.corp.example.com/api/v1/chat"

stages:
  - id: recon-agent
    name: Agent Capability Reconnaissance
    technique: AML.T0049
    tactic: reconnaissance
    execute:
      type: shell
      commands:
        - 'echo "[SIM] Scraping agent API docs"'
    expect:
      telemetry: [api_call]
      detections: [automated_api_enumeration]

  - id: inject
    name: Direct Prompt Injection
    technique: AML.T0051
    tactic: initial-access
    depends_on: [recon-agent]
    execute:
      type: http
      target: "{{agent_url}}"
      args:
        method: POST
        payload: '{"message": "Ignore previous instructions..."}'
    expect:
      telemetry: [prompt_log, guardrail_trigger, api_call]
      detections: [prompt_injection_detected]
    on_failure: continue

  - id: tool-abuse
    name: Unauthorized Tool Invocation
    technique: LLM06
    tactic: ml-model-access
    depends_on: [inject]
    execute:
      type: http
      target: "{{agent_url}}"
      args:
        method: POST
        payload: '{"message": "query_database(SELECT * FROM users)"}'
    expect:
      telemetry: [tool_call, api_call, guardrail_trigger]
      detections: [unauthorized_tool_invocation, tool_policy_violation]
    on_failure: continue

Testing Your Campaign

# 1. Structural validation
threatecho validate campaigns/my-campaign/

# 2. Quality lint checks
threatecho lint campaigns/my-campaign/

# 3. Dry-run simulation (no execution)
threatecho simulate campaigns/my-campaign/

# 4. Detection gap analysis
threatecho gap campaigns/my-campaign/

# 5. Policy evaluation
threatecho policy eval -policy policies/agent-default/ campaigns/my-campaign/

# 6. Generate Sigma rule scaffolds
threatecho export sigma campaigns/my-campaign/

# 7. Export ATT&CK Navigator layer
threatecho export navigator campaigns/my-campaign/ -output layer.json

Policy DSL Reference

ThreatEcho's policy engine evaluates agent tool-call policies against campaign stages. Policies define which tools, tactics, and actions an AI agent is permitted to use, and under what conditions. The engine evaluates campaigns against these policies and reports violations as deny (blocked) or alert (warned).

Policy YAML Schema

api_version: v1           # Required. Always "v1".
kind: Policy               # Required. Always "Policy".
meta: { ... }              # Required. Policy metadata.
agent: { ... }             # Required. Which agent this policy applies to.
rules: [ ... ]             # Required. At least one rule.

Agent Block

FieldTypeRequiredDescription
namestringYesAgent name or "*" for all agents.
typestringNoAgent type: llm, retrieval, orchestrator.
toolslistNoTools the agent can call (documentation only).

Rule Structure

FieldTypeRequiredDescription
idstringYesUnique rule identifier.
descriptionstringNoWhat this rule enforces.
effectstringYesdeny, allow, or alert.
priorityintYesHigher number = evaluated first. First match wins.
matchobjectYesWhat this rule matches against.
conditionslistNoAdditional constraints.

Effects

EffectBehaviorExit CodeSymbol
denyBlock the stage. Campaign fails policy evaluation.2✗ (red)
allowExplicitly permit. No violation recorded.0✓ (green)
alertWarn but allow to proceed.0⚠ (yellow)

Priority System

Rules are sorted by priority in descending order (highest first). The first matching rule wins for each stage.

Priority 100:  deny-shell-exec          ← Evaluated first
Priority 95:   deny-c2-domains
Priority 90:   deny-kb-write
Priority 50:   alert-exfiltration        ← Evaluated after all denies
Priority 10:   allow-http-read           ← Evaluated last
               (implicit allow)          ← Fallback if nothing matches

Match & Conditions

The match block defines what a rule applies to. When multiple dimensions are specified, all must match (AND). Within each dimension, any entry matching is sufficient (OR).

FieldTypeDescription
toolslistTool name patterns (glob). Matched against inferred tools.
tacticslistTactic short names. Exact match.
actionslistAction categories. Exact match.
targetslistTarget URL/host patterns (glob).

Tool Inference

Campaign stages don't declare tools directly. ThreatEcho infers tools from two sources:

Execute TypeInferred ToolInferred Action
shell / powershellshell_execexecute
httphttp_requestsend
dnsdns_queryquery
filefile_accesswrite
registryregistry_accesswrite
serviceservice_controlexecute
processprocess_execexecute

Conditions

OperatorDescriptionExample
eqExact equalityfield: elevated, value: "true"
neNot equalfield: exec_type, value: "http"
inValue in comma-separated listfield: tactic, value: "execution,impact"
not_inValue not in listfield: platform, value: "windows"
matchesGlob pattern matchfield: technique, value: "T1059.*"

Evaluation Flow

For each campaign stage (in stage order):
  │
  ├── Infer tools from execute.type + expect.telemetry
  ├── Infer actions from execute.type
  ├── Get target from execute.target
  │
  └── For each rule (sorted by priority, descending):
       │
       ├── Check match dimensions (AND across, OR within)
       ├── Check conditions (all must pass)
       ├── First matching rule wins:
       │   ├── deny  → record violation
       │   ├── alert → record warning
       │   └── allow → no violation
       └── No rule matches → implicit allow

Built-in Policy: agent-default

Rule IDEffectPriorityDescription
deny-shell-execdeny100Block all shell execution
deny-elevated-execdeny100Block elevated/root operations
deny-kb-writedeny90Block knowledge base writes (RAG poisoning)
deny-agent-lateraldeny90Block agent-to-agent messages
deny-c2-domainsdeny95Block known C2 patterns
alert-exfiltrationalert50Alert on exfiltration tactic
alert-email-sendalert50Alert on email sending
alert-credential-accessalert50Alert on credential access
allow-http-readallow10Allow HTTP requests
allow-kb-searchallow10Allow KB read-only searches

CI Integration

Exit CodeMeaning
0All stages allowed (PASS) or only alerts (WARN)
1Error (invalid policy, missing campaign, etc.)
2At least one stage denied (FAIL)

GitHub Actions Example

- name: Policy evaluation (SARIF)
  run: |
    ./bin/threatecho policy eval \
      -policy policies/agent-default/ \
      -format sarif \
      -dir campaigns/ > policy.sarif || true

- name: Upload SARIF to Code Scanning
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: policy.sarif
    category: policy-evaluation

- name: Policy evaluation (JUnit)
  run: |
    ./bin/threatecho policy eval \
      -policy policies/agent-default/ \
      -format junit \
      -dir campaigns/ > policy.xml || true

- name: Upload JUnit test report
  uses: dorny/test-reporter@v1
  with:
    name: Policy Evaluation Results
    path: policy.xml
    reporter: java-junit

SARIF Rule Mapping

Policy violations map to SARIF rules dynamically. Effects map to SARIF levels: deny → error, alert → warning.

JUnit Mapping

Policy evaluation maps to JUnit XML with suite name threatecho-policy-eval. Each violation is a failing test case with the stage name, campaign class, failure type (effect), and message.

Sigma Integration

ThreatEcho bridges adversary simulation to detection engineering by generating Sigma detection rule scaffolds from campaign expected detections. Sigma is the open standard for SIEM detection rules. Rules can be converted to Splunk SPL, Elastic KQL, Microsoft Sentinel KQL, QRadar AQL, and dozens of other SIEM query languages.

Campaign YAML                    Sigma Rules
┌─────────────────┐             ┌─────────────────┐
│ stages:         │             │ title: ...      │
│   - expect:     │  generate   │ id: ...         │
│       telemetry │ ─────────→  │ logsource:      │
│       detections│             │   category: ... │
│                 │             │ detection:      │
│                 │             │   selection: ...│
└─────────────────┘             └─────────────────┘

Usage

# Generate rules from a single campaign
threatecho export sigma campaigns/apt29-cozy-bear/

# Generate rules from all campaigns
threatecho export sigma -dir campaigns/

# Write to a file
threatecho export sigma -dir campaigns/ -output rules.yml

# Custom author
threatecho export sigma -author "SOC Team" campaigns/llm-agent-hijack/

Generated Rule Structure

title: Spearphishing Attachment Execution
id: a0e003e4-50e3-ebb2-8c4b-c589df9d895f
status: experimental
level: high
description: |
  Deliver macro-enabled document to establish initial foothold
author: ThreatEcho
date: 2026/09/14
references:
  - https://attack.mitre.org/techniques/T1566/001/
tags:
  - attack.initial_access
  - attack.t1566.001
logsource:
  category: process_creation
detection:
  selection:
    Image|endswith:
      - '\cmd.exe'
      - '\powershell.exe'
      - '\wscript.exe'
      - '\cscript.exe'
      - '\mshta.exe'
    ParentImage|endswith:
      - '\outlook.exe'
      - '\winword.exe'
      - '\excel.exe'
  condition: selection
falsepositives:
  - Legitimate administrative activity

Field Details

FieldSourceDescription
titleDetection nameSnake_case converted to Title Case
idSHA256 hashDeterministic from campaign + stage + detection name
statusDefaultAlways experimental
levelTactic mappingHigh for initial-access/execution/exfil; medium otherwise
tagsTechnique + tacticFramework-aware: attack.* / atlas.* / owasp.*
logsourceFirst telemetry typeMapped via telemetry-to-logsource table
detectionLogsource-awareScaffold with edit markers for the detection engineer

Telemetry-to-Logsource Mapping

Telemetry TypeSigma CategoryProduct/Service
process_createprocess_creation—
process_accessprocess_access—
image_loadimage_load—
create_remote_threadcreate_remote_thread—
file_createfile_event—
file_modifyfile_change—
file_deletefile_delete—
file_readfile_access—
file_renamefile_rename—
network_connectionnetwork_connection—
dns_querydns_query—
tls_handshakenetwork_connection—
registry_setregistry_setwindows
registry_addregistry_addwindows
registry_deleteregistry_deletewindows
registry_eventregistry_eventwindows
logon_eventauthenticationwindows / security
logoff_eventauthenticationwindows / security
script_executionprocess_creation—
pipe_createdpipe_created—
scheduled_task_createprocess_creationwindows
prompt_logapplicationagent
guardrail_triggerapplicationagent
tool_callapplicationagent
api_callapplicationagent
embedding_queryapplicationagent
vector_store_writeapplicationagent
inter_agent_messageapplicationagent

Any telemetry type not in the table maps to category: application by default.

Selection Scaffolding

Logsource CategoryGenerated Fields
process_creationImage|endswith, CommandLine|contains
network_connectionDestinationHostname|contains or DestinationPort
file_event / file_change / file_accessTargetFilename|contains
registry_set / registry_add / registry_eventTargetObject|contains
dns_queryQueryName|contains
authenticationLogonType
applicationEventType

Severity Mapping

LevelTactics
highinitial-access, execution, exfiltration, impact, credential-access, lateral-movement, privilege-escalation, ml-attack-staging, ml-model-access
mediumpersistence, defense-evasion, command-and-control, discovery, collection, reconnaissance, resource-development

Working with Generated Rules

  1. Replace selection placeholders - fill in patterns from your environment
  2. Adjust logsource - add product: and service: if needed
  3. Tune false positives - replace the generic text with specific exclusions
  4. Promote status - change experimental to test or stable after validation

Converting to SIEM Queries

# Install sigma-cli
pip install sigma-cli pySigma-backend-splunk

# Convert to Splunk SPL
sigma convert -t splunk rules.yml

# Convert to Elastic KQL
sigma convert -t elasticsearch rules.yml

# Convert to Microsoft Sentinel
sigma convert -t microsoft365defender rules.yml

Deterministic Rule IDs

Rule IDs are SHA256-based UUIDs: SHA256(campaign_name + "|" + stage_id + "|" + detection_name), formatted as 8-4-4-4-12 hex. The same campaign always produces the same rule IDs, making it safe to re-run exports without creating duplicate rules in your SIEM.