ADR-027: Red Team Structured Logging Boundary¶
Status¶
accepted (amended 2026-05-17 by ADR-033)
Date¶
2026-05-09 (amended 2026-05-17)
Status update—2026-05-17¶
The SIEM-transport portion of this ADR (rsyslog-to-Wazuh forwarding
from the Kali container, Wazuh-agent-on-Kali ingestion, and the
kali_redteam_rules.xml decoder loaded by the Wazuh manager) is
superseded by ADR-033 under the non-contamination principle. Red
activity must not bleed into the blue defensive stack's awareness via
the SIEM, because that injection inflates blue's artificial picture
of red and contaminates purple-loop experiments.
What stays from this ADR is the OCSF schema work:
- The classifier (
mcp/mcp-red/src/classifier.ts) and extractor (mcp/mcp-red/src/extractor.ts) tables. - The activity taxonomy with MITRE technique/tactic mappings.
SeverityId0–6 aligned withsrc/aptl/core/detection.py.- The
OcsfRedTeamRecordshape including theaptlenvelope. - The
postToolHookarchitecture for emitting records. - Cross-language redaction at the serialization boundary (ADR-029).
- Best-effort guarantee: classification / extraction / sink failures do not break tool execution.
- Raw-session outcome handling: the Kali
postToolHookmust use the effective mode surfaced in thesession_commandresult envelope. The request argumentrawis only a per-call override and does not reveal inherited raw mode from a session created with raw mode enabled.
What changes is the sink:
- The default sink composite now writes to stderr (with
[OCSF]sentinel—local dev visibility) AND to a per-run JSONL at<state>/runs/<trace_id>/mcp-side/ocsf.jsonl—never to a SIEM. mcp/mcp-red/src/logger.tsexportslocalOcsfJsonlSink(env)anddefaultRedTeamSinks(env); the existingstderrJsonlSinkis retained as a building block but no longer the default by itself.- The
SiemSinktype name is intentionally kept to avoid a churning rename across tests; the behaviour under that name is non-SIEM-bound. A future ADR may rename the type if there is appetite.
The "Do not replace the existing Kali Wazuh agent, bash-history
ingestion, or low-level kali_redteam_rules.xml until the OCSF path
is proven to cover equivalent reconstruction needs" guardrail
(below) is reversed: those red-side SIEM pipes are removed as part
of ADR-033's implementation. The equivalent reconstruction surface
now lives in the per-run capture directory (PTY typescripts, pcaps,
auditd events, OCSF JSONL, tool-call JSONL), which is more complete
than the previous SIEM ingestion path.
Everything below this section is the original decision text, preserved for historical context.
Status update - 2026-05-18¶
For *_session_command, OCSF status must be derived from the effective command
mode and observed command outcome, not from request arguments alone. If the
session_command result envelope reports session_mode: "raw", the OCSF
record must emit status_id=0 / status="Unknown" even when the raw-mode SSH
result contains exit_code: 0.
This keeps the common MCP session layer as the source of truth for execution
mode and keeps mcp-red as a best-effort observer. Do not duplicate the common
session mode rules in mcp-red, do not parse session ids or query session state
from the logger, and do not let OCSF logging failures affect tool execution.
Context¶
AI agents execute arbitrary red-team commands through the Kali MCP server. Existing Kali telemetry is split across:
- Wazuh-forwarded bash command history and
kali_redteam_rules.xml - MCP tool spans emitted through
aptl-mcp-commonOpenTelemetry wrappers - SDL and detection models that already use OCSF-aligned names such as
product_name,analytic_uid, andseverity_id
Issue #162 adds OCSF-shaped red-team activity events for SIEM correlation. The main design risk is creating a second command execution path, second schema vocabulary, or second redaction/error-handling policy beside the common MCP infrastructure.
Decision¶
Red-team structured logging is a SIEM event boundary attached to MCP command execution, not a replacement for OpenTelemetry tracing and not a new command runner.
Implementations must:
- Reuse the
aptl-mcp-commonSSH command tool boundary for commands executed through MCP. Classification and metadata extraction may be enabled only for red-team/Kali tools, but command execution itself remains in the common handler/session infrastructure. - Keep OpenTelemetry spans as observability data and OCSF red-team records as Wazuh/OpenSearch SIEM data. A single command may produce both, but neither format should be derived by scraping the other.
- Use OCSF field names consistently with the Python detection and attack
models. In particular, keep
severity_idon the OCSF 0-6 scale already used bysrc/aptl/core/detection.pyandsrc/aptl/core/attacks.py. - Treat the red-team taxonomy document as the human-readable source of truth for supported activity classes, MITRE mappings, required fields, and fallback behavior. TypeScript classifier/extractor tables should mirror that taxonomy, not invent competing labels.
- Emit a generic OCSF Process Activity record for unclassified commands rather than failing command execution or dropping the event entirely.
- Make SIEM logging best-effort. Classification, extraction, serialization, or transport failures must be visible on stderr/telemetry but must not prevent the requested MCP command from running or returning its result.
- Reuse the shared TypeScript redaction policy from
mcp/aptl-mcp-common/src/redaction.tsat every serialization boundary that can contain command lines, outputs, errors, usernames, tokens, passwords, cookies, API keys, or private key material.
Guardrails¶
- Do not log secret values to SIEM. For credential-oriented commands, record credential type, username/account when safe, and redaction markers or hashes where needed for correlation; do not preserve plaintext passwords, bearer tokens, cookies, API keys, private keys, or wordlist contents.
- Do not duplicate the
SeverityIdconcept with incompatible numeric values. If TypeScript needs constants, name them as OCSF severity IDs and keep their values aligned to the Python enum. - Do not parse command strings by splitting on whitespace alone. Compound commands, quotes, pipes, redirects, IPv6 addresses, CIDRs, and port ranges are part of the required input space.
- Do not couple taxonomy classification to Wazuh rule IDs. OCSF
class_uid/activity_id/MITRE mappings describe the red action; Wazuh rule IDs describe downstream detection and alerting. - Do not replace the existing Kali Wazuh agent, bash-history ingestion, or
low-level
kali_redteam_rules.xmluntil the OCSF path is proven to cover equivalent reconstruction needs.
Consequences¶
Positive¶
- Command execution, sessions, timeout handling, and MCP response shape remain
centralized in
aptl-mcp-common. - Red-team events and detection events can correlate through shared OCSF and MITRE vocabulary without forcing tracing data into SIEM schemas.
- Logging failures degrade gracefully and cannot break red-team workflows.
- Secret handling follows the existing cross-language telemetry guardrail.
Negative¶
aptl-mcp-commonneeds a narrow extension point for post-command SIEM event emission, ormcp-redneeds a wrapper that delegates back to common handlers.- The first implementation must keep the taxonomy document and TypeScript tables in sync until a generated-schema path exists.
Non-Goals¶
- Full shell AST interpretation.
- Complete OCSF coverage for every red-team tool.
- Replacing detection scoring, Wazuh rules, OpenTelemetry spans, or existing raw archive indexing.
- Logging non-MCP manual shell activity beyond the existing Kali bash-history and Wazuh-agent path.