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
| Level | Check |
| Info | meta.description empty |
| Info | meta.objective empty |
| Info | meta.mitre_version not set |
| Info | Stage has telemetry but no detections (will flag as gap) |
| Warning | Technique ID not found in registry (valid format but not indexed) |
| Warning | Framework/tactic cross-mismatch (ATT&CK technique with ATLAS tactic) |
| Warning | Stage has no expected telemetry (gap analysis will be limited) |
| Warning | Shell stage with no commands |
| Warning | HTTP stage with no target URL |
| Warning | Unrecognized 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.
| Framework | Technique Pattern | Example | Tactic Source |
| MITRE ATT&CK | T\d{4}(.\d{3})? | T1566.001 | ATT&CK 14 tactics |
| MITRE ATLAS | AML.T\d{4} | AML.T0051 | ATLAS 7 tactics |
| OWASP LLM Top 10 | LLM\d{2} | LLM06 | Uses 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
The same analysis result feeds multiple output renderers. Each format serves a different consumer.
Gap Analysis Reports
| Format | Function | Consumer |
| Text (ANSI) | report.GapTextReport() | Terminal, human review |
| JSON | report.GapJSONReport() | APIs, dashboards, downstream tooling |
| SARIF v2.1.0 | report.GapSARIFReport() | GitHub Code Scanning, VS Code |
| JUnit XML | report.GapJUnitReport() | Jenkins, GitHub Actions, GitLab CI |
| HTML | report.GapHTMLReport() | Stakeholder reports, email, archival |
| Markdown | report.GapMarkdownReport() | GitHub wikis, READMEs, PR comments |
Policy Evaluation Reports
| Format | Function | Consumer |
| Text (ANSI) | report.PolicyTextReport() | Terminal, human review |
| JSON | report.PolicyJSONReport() | Programmatic consumption |
| SARIF v2.1.0 | report.PolicySARIFReport() | GitHub Code Scanning |
| JUnit XML | report.PolicyJUnitReport() | CI test reporters |
SARIF Rule IDs
| Rule ID | Name | Level | Gap Type |
TE-GAP-001 | DetectionMissing | warning | Telemetry defined, no detection rules |
TE-GAP-002 | TelemetryMissing | error | No expected telemetry at all |
TE-GAP-003 | TacticUncovered | warning | Entire tactic has zero stage coverage |
Risk Scoring
| Gap Type | Tactic Category | Risk Level |
| Tactic uncovered | initial-access, execution, exfiltration | critical |
| Tactic uncovered | credential-access, lateral-movement, impact | high |
| Tactic uncovered | all other tactics | medium |
| Detection missing | initial-access, execution, exfiltration | high |
| Detection missing | all other tactics | medium |
| Telemetry missing | any tactic | medium |
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
- Pure Go.
CGO_ENABLED=0, statically compiled. The release ships a CLI and an optional agent binary.
- Minimal dependencies.
gopkg.in/yaml.v3, golang.org/x/crypto (SSH), and github.com/masterzen/winrm (WinRM). No database, no daemon, no config server.
- Deterministic output. Same inputs produce identical outputs. Sigma rule IDs are SHA256-derived. Navigator layers sort entries by technique ID.
- CI-first. Every output format maps to a CI integration: SARIF → GitHub Code Scanning, JUnit → test reporters, exit code 2 → policy deny.
- Separation of analysis from execution. Simulation (Noop executor) and live execution (Shell executor) share the same engine, options, and report pipeline.
- Cross-platform. Build matrix covers linux/darwin/windows on amd64/arm64.
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
| Field | Type | Required | Description |
name | string | Yes | Campaign identifier. Used in reports, Sigma rule IDs, gap keys. |
adversary | string | Yes | Threat actor or group name. |
description | string | No | Free-text campaign description. Lint warns if empty. |
objective | string | No | What the campaign tests or proves. |
severity | string | No | critical, high, medium, low, or info. |
mitre_version | string | No | ATT&CK version (e.g., "15.1"). Lint warns if empty. |
tags | list | No | Arbitrary tags for filtering and search. |
authors | list | No | Campaign authors. |
references | list | No | URLs to threat reports, advisories, or emulation plans. |
created | string | No | ISO date. |
modified | string | No | ISO 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.
| Field | Type | Required | Description |
id | string | Yes | Unique identifier within the campaign. |
name | string | Yes | Human-readable stage name. |
technique | string | Yes | Technique ID: ATT&CK (T1566), ATLAS (AML.T0051), OWASP (LLM06). |
tactic | string | Yes | Tactic short name. Must be a valid ATT&CK or ATLAS tactic. |
platform | list | No | windows, linux, macos. If set, stage is skipped on other platforms. |
depends_on | list | No | Stage IDs that must complete first. Defines the DAG edges. |
execute | object | Yes | What to execute. See Execute Types. |
expect | object | No | Expected telemetry, detections, artifacts, IOCs. |
on_failure | string | No | abort (default), continue, or skip. |
timeout | duration | No | Stage execution timeout (e.g., "30s", "5m"). |
delay | duration | No | Wait before executing. |
Technique ID Formats
| Framework | Pattern | Regex | Examples |
| ATT&CK | T + 4 digits + optional . + 3 digits | ^T\d{4}(\.\d{3})?$ | T1566, T1566.001 |
| ATLAS | AML.T + 4 digits | ^AML\.T\d{4}$ | AML.T0051, AML.T0043 |
| OWASP LLM | LLM + 2 digits | ^LLM\d{2}$ | LLM01, LLM06 |
Execute Types
| Field | Type | Description |
type | string | Required. shell, powershell, http, file, registry, service, process, dns, manual. |
commands | list | Shell commands to execute. |
payload | string | Payload content or path. |
target | string | Target URL or host. |
args | map | Key-value arguments (e.g., method, content_type). |
cleanup | list | Commands run after stage completes (best-effort). |
elevated | bool | Whether 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.
| Field | Type | Description |
telemetry | list | Expected telemetry types that should fire. |
detections | list | Expected detection rule names that should alert. |
artifacts | list | Files, registry keys, or other artifacts produced. |
iocs | list | Indicators of compromise (IPs, domains, hashes). |
Telemetry Types (selected)
| Category | Types |
| Process | process_create, process_access, image_load, create_remote_thread |
| File | file_create, file_modify, file_delete, file_read, file_write, file_rename |
| Network | network_connection, dns_query, tls_handshake |
| Registry | registry_set, registry_add, registry_delete, registry_event |
| Auth | logon_event, logoff_event |
| Execution | script_execution, pipe_created, scheduled_task_create |
| Email | email_sent, email_delivered |
| AI Agent | prompt_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
| Field | Type | Required | Description |
name | string | Yes | Agent name or "*" for all agents. |
type | string | No | Agent type: llm, retrieval, orchestrator. |
tools | list | No | Tools the agent can call (documentation only). |
Rule Structure
| Field | Type | Required | Description |
id | string | Yes | Unique rule identifier. |
description | string | No | What this rule enforces. |
effect | string | Yes | deny, allow, or alert. |
priority | int | Yes | Higher number = evaluated first. First match wins. |
match | object | Yes | What this rule matches against. |
conditions | list | No | Additional constraints. |
Effects
| Effect | Behavior | Exit Code | Symbol |
deny | Block the stage. Campaign fails policy evaluation. | 2 | ✗ (red) |
allow | Explicitly permit. No violation recorded. | 0 | ✓ (green) |
alert | Warn 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).
| Field | Type | Description |
tools | list | Tool name patterns (glob). Matched against inferred tools. |
tactics | list | Tactic short names. Exact match. |
actions | list | Action categories. Exact match. |
targets | list | Target URL/host patterns (glob). |
Tool Inference
Campaign stages don't declare tools directly. ThreatEcho infers tools from two sources:
| Execute Type | Inferred Tool | Inferred Action |
shell / powershell | shell_exec | execute |
http | http_request | send |
dns | dns_query | query |
file | file_access | write |
registry | registry_access | write |
service | service_control | execute |
process | process_exec | execute |
Conditions
| Operator | Description | Example |
eq | Exact equality | field: elevated, value: "true" |
ne | Not equal | field: exec_type, value: "http" |
in | Value in comma-separated list | field: tactic, value: "execution,impact" |
not_in | Value not in list | field: platform, value: "windows" |
matches | Glob pattern match | field: 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 ID | Effect | Priority | Description |
deny-shell-exec | deny | 100 | Block all shell execution |
deny-elevated-exec | deny | 100 | Block elevated/root operations |
deny-kb-write | deny | 90 | Block knowledge base writes (RAG poisoning) |
deny-agent-lateral | deny | 90 | Block agent-to-agent messages |
deny-c2-domains | deny | 95 | Block known C2 patterns |
alert-exfiltration | alert | 50 | Alert on exfiltration tactic |
alert-email-send | alert | 50 | Alert on email sending |
alert-credential-access | alert | 50 | Alert on credential access |
allow-http-read | allow | 10 | Allow HTTP requests |
allow-kb-search | allow | 10 | Allow KB read-only searches |
CI Integration
| Exit Code | Meaning |
| 0 | All stages allowed (PASS) or only alerts (WARN) |
| 1 | Error (invalid policy, missing campaign, etc.) |
| 2 | At 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
| Field | Source | Description |
title | Detection name | Snake_case converted to Title Case |
id | SHA256 hash | Deterministic from campaign + stage + detection name |
status | Default | Always experimental |
level | Tactic mapping | High for initial-access/execution/exfil; medium otherwise |
tags | Technique + tactic | Framework-aware: attack.* / atlas.* / owasp.* |
logsource | First telemetry type | Mapped via telemetry-to-logsource table |
detection | Logsource-aware | Scaffold with edit markers for the detection engineer |
Telemetry-to-Logsource Mapping
| Telemetry Type | Sigma Category | Product/Service |
process_create | process_creation | — |
process_access | process_access | — |
image_load | image_load | — |
create_remote_thread | create_remote_thread | — |
file_create | file_event | — |
file_modify | file_change | — |
file_delete | file_delete | — |
file_read | file_access | — |
file_rename | file_rename | — |
network_connection | network_connection | — |
dns_query | dns_query | — |
tls_handshake | network_connection | — |
registry_set | registry_set | windows |
registry_add | registry_add | windows |
registry_delete | registry_delete | windows |
registry_event | registry_event | windows |
logon_event | authentication | windows / security |
logoff_event | authentication | windows / security |
script_execution | process_creation | — |
pipe_created | pipe_created | — |
scheduled_task_create | process_creation | windows |
prompt_log | application | agent |
guardrail_trigger | application | agent |
tool_call | application | agent |
api_call | application | agent |
embedding_query | application | agent |
vector_store_write | application | agent |
inter_agent_message | application | agent |
Any telemetry type not in the table maps to category: application by default.
Selection Scaffolding
| Logsource Category | Generated Fields |
process_creation | Image|endswith, CommandLine|contains |
network_connection | DestinationHostname|contains or DestinationPort |
file_event / file_change / file_access | TargetFilename|contains |
registry_set / registry_add / registry_event | TargetObject|contains |
dns_query | QueryName|contains |
authentication | LogonType |
application | EventType |
Severity Mapping
| Level | Tactics |
high | initial-access, execution, exfiltration, impact, credential-access, lateral-movement, privilege-escalation, ml-attack-staging, ml-model-access |
medium | persistence, defense-evasion, command-and-control, discovery, collection, reconnaissance, resource-development |
Working with Generated Rules
- Replace selection placeholders - fill in patterns from your environment
- Adjust logsource - add
product: and service: if needed
- Tune false positives - replace the generic text with specific exclusions
- 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.