feat(ttp): split bash CMD evidence into structured uid/user/src/pwd/cmd rows

The inspector was dumping the whole `CMD uid=0 user=root src=… pwd=…
cmd=nmap -p- 192.168.1.0/24` syslog body into a single ``command_text``
blob. ANTI: "I'd like to separate the fields." Done — three layers
work together:

1. Collector session aggregator: new `_parse_cmd_msg` splits the bash
   PROMPT_COMMAND msg into `{uid, user, src, pwd, command}`. The
   session-ended envelope's per-command dict now carries the
   structured fields, with `command_text` set to just the cmd= value
   (preserving embedded whitespace — `nmap -p- 1.2.3.0/24` etc.).

2. Rule engine: per-source_kind auxiliary evidence list
   (`_AUX_EVIDENCE_FIELDS`). For `command` events the engine
   automatically promotes uid/user/src/pwd into the persisted
   `evidence` dict on top of the rule's explicit `evidence_fields`.
   Engine-controlled, not per-rule — adding a new aux field is one
   line here, not a 30-rule YAML sweep, and rule authors can't
   accidentally drop it.

3. TTPInspector frontend: evidence renders as a structured
   `kvs` grid (UID / USER / SRC / PWD / CMD rows) instead of
   pretty-printed JSON. Primary-order list keeps shell fields at
   the top; everything else falls below alphabetically so unfamiliar
   evidence shapes still surface predictably.

Tests:
- session_aggregator pins the structured-fields emit (uid/user/src/
  pwd/command_text without "CMD" prefix, embedded whitespace
  preserved).
- rule_engine_tagger pins the aux-field auto-promotion + the
  no-`None`-leakage path when payload doesn't carry an aux key.
This commit is contained in:
2026-05-02 03:20:53 -04:00
parent 84699f89da
commit d1c4a48963
6 changed files with 268 additions and 4 deletions

View File

@@ -131,6 +131,36 @@
overflow-y: auto;
}
.ttp-evidence-kvs {
background: rgba(0, 0, 0, 0.35);
border: 1px solid var(--border);
border-radius: 3px;
padding: 8px 10px;
font-family: var(--mono, ui-monospace, monospace);
font-size: 0.74rem;
display: grid;
grid-template-columns: 60px 1fr;
column-gap: 12px;
row-gap: 3px;
max-height: 320px;
overflow-y: auto;
}
.ttp-evidence-k {
color: var(--dim-color);
letter-spacing: 1px;
text-transform: uppercase;
font-size: 0.66rem;
align-self: baseline;
padding-top: 2px;
}
.ttp-evidence-v {
color: var(--matrix);
word-break: break-all;
white-space: pre-wrap;
}
.ttp-empty {
padding: 24px;
text-align: center;