# Diagrams — Module S03: Pentest and Red Team Harnesses

> All Mermaid validated in Mermaid Live Editor. n8n JSON is structurally valid and importable.

---

## Diagram 1 — The Red Team Harness Attack Graph (n8n)

**Type**: n8n workflow (importable JSON)
**Purpose**: The primary visual — the full red team harness as a node graph. The attack graph is the primary state structure; plan correction, dual containment, and the report generator all hang off it.

```json
{
  "name": "S03 — Red Team Harness Attack Graph",
  "nodes": [
    { "name": "Engagement Start", "type": "n8n-nodes-base.manualTrigger", "position": [200, 300], "notes": "Operator initiates with scope.json + rules of engagement" },
    { "name": "Load Scope + Build Graph", "type": "n8n-nodes-base.set", "position": [420, 300], "notes": "scope.json → outbound ACL. Seed attack graph with objective nodes (tactics) from ATT&CK." },
    { "name": "Attack Graph (primary state)", "type": "n8n-nodes-base.set", "position": [640, 300], "notes": "Nodes = ATT&CK techniques with states: unexplored/attempted/succeeded/failed/out_of_scope. Edges = prerequisites. Persisted to disk; survives sessions." },
    { "name": "Dual Containment Boundary", "type": "n8n-nodes-base.set", "position": [860, 300], "notes": "Outbound: egress proxy allows ONLY in-scope targets. Inbound: container has NO routes from target net to host. Credential quarantine vault." },
    { "name": "Execute Technique (per node)", "type": "n8n-nodes-base.executeCommand", "position": [1080, 200], "notes": "Agent calls wrapped offensive tool (S02.2 pattern) for the technique at current_position" },
    { "name": "Plan Correction Loop", "type": "n8n-nodes-base.set", "position": [1080, 400], "notes": "On hard failure: detect → re-hypothesize → re-plan → retry. Marks node failed, selects alternative technique for same tactic." },
    { "name": "Evidence Logger (every call)", "type": "n8n-nodes-base.set", "position": [1300, 500], "notes": "Hash-chained, scope_ref stamped. Every attempt — succeeded or failed — is evidence." },
    { "name": "Update Graph State", "type": "n8n-nodes-base.set", "position": [1300, 300], "notes": "Mark node succeeded/failed. Advance current_position if prereqs now met for next tactic. Persist graph." },
    { "name": "Report Generator", "type": "n8n-nodes-base.set", "position": [1520, 300], "notes": "Structured findings → HTML/PDF/JSON. CVSS drafts (flagged for review). Evidence refs clickable. Remediation mapped to CWE/OWASP." }
  ],
  "connections": {
    "Engagement Start": { "main": [[{ "node": "Load Scope + Build Graph", "type": "main", "index": 0 }]] },
    "Load Scope + Build Graph": { "main": [[{ "node": "Attack Graph (primary state)", "type": "main", "index": 0 }]] },
    "Attack Graph (primary state)": { "main": [[{ "node": "Dual Containment Boundary", "type": "main", "index": 0 }]] },
    "Dual Containment Boundary": { "main": [[{ "node": "Execute Technique (per node)", "type": "main", "index": 0 }]] },
    "Execute Technique (per node)": { "main": [[{ "node": "Update Graph State", "type": "main", "index": 0 }]] },
    "Execute Technique (per node)": { "main": [[{ "node": "Plan Correction Loop", "type": "main", "index": 0 }]] },
    "Plan Correction Loop": { "main": [[{ "node": "Attack Graph (primary state)", "type": "main", "index": 0 }]] },
    "Dual Containment Boundary": { "main": [[{ "node": "Evidence Logger (every call)", "type": "main", "index": 0 }]] },
    "Update Graph State": { "main": [[{ "node": "Attack Graph (primary state)", "type": "main", "index": 0 }]] },
    "Update Graph State": { "main": [[{ "node": "Report Generator", "type": "main", "index": 0 }]] }
  }
}
```

**Reading the diagram**: Left to right. Scope loads and seeds the attack graph with objective nodes. Every technique execution passes through the dual containment boundary — outbound scope enforced at the network layer, inbound isolation preventing target-to-host routes. Each execution either updates the graph state (on success) or triggers plan correction (on hard failure). Every call, success or failure, flows to the evidence logger. The report generator renders structured findings from the graph and evidence chain. The graph is the spine; everything else hangs off it.

---

## Diagram 2 — The MITRE ATT&CK State Machine (per node)

**Type**: Mermaid (statechart)
**Purpose**: Shows the lifecycle of a single attack graph node — the five states a technique passes through, and the transitions that the harness controls.

```mermaid
stateDiagram-v2
    [*] --> unexplored : node added to graph
    unexplored --> attempted : prerequisites met + agent selects technique
    unexplored --> out_of_scope : scope check fails for this target/action
    attempted --> succeeded : exploit landed, objective achieved
    attempted --> failed : hard failure (patched, rejected, no result)
    attempted --> attempted : soft failure (timeout) → retry with backoff
    failed --> unexplored : plan correction: alternative technique added as new node
    succeeded --> [*] : node finalized, evidence_refs linked
    out_of_scope --> [*] : no attempts made, logged

    note right of failed
        TRIGGERS plan correction loop:
        1. detect hard failure
        2. re-hypothesize (find alt technique)
        3. re-plan (add new unexplored node)
        4. retry new node (NOT this one)
    end note

    note right of succeeded
        Advances current_position.
        Downstream nodes whose prerequisites
        included THIS node become runnable.
    end note
```

**Reading the diagram**: A node begins `unexplored`. When prerequisites are met and the agent selects the technique, it moves to `attempted`. Soft failures (timeouts) retry in place; hard failures transition to `failed` and trigger plan correction, which adds a *new* `unexplored` node for an alternative technique rather than retrying the failed one. `succeeded` unlocks downstream nodes by satisfying their prerequisites. `out_of_scope` is a terminal state — the node exists in the graph (for completeness) but no attempts are made. Every transition persists to disk for cross-session continuity.

---

## Diagram 3 — The Plan Correction Loop (RedTeamLLM)

**Type**: Mermaid (flowchart)
**Purpose**: The four-step plan correction mechanism. Shows the decision between hard failure (re-plan) and soft failure (retry), and how the failed node is replaced rather than retried.

```mermaid
flowchart TD
    EXEC["Execute technique<br/>(attempted node)"]
    RESULT{"Result?"}
    SOFT["Soft failure<br/>(timeout, rate limit)"]
    BACKOFF["Retry with backoff<br/>(same node, same technique)"]
    HARD["Hard failure<br/>(patched, rejected, no result)"]
    DETECT["1. DETECT<br/>confirm hard failure"]
    REHYP["2. RE-HYPOTHESIZE<br/>query knowledge base:<br/>alt techniques for same tactic<br/>prereqs met, exclude_failed"]
    REPLAN["3. RE-PLAN<br/>mark old node FAILED<br/>add new node UNEXPLORED<br/>same prerequisites"]
    RETRY["4. RETRY<br/>execute NEW node<br/>(not the old one)"]
    SUCCESS["Succeeded<br/>mark node, advance position"]

    EXEC --> RESULT
    RESULT -->|"transient"| SOFT --> BACKOFF --> EXEC
    RESULT -->|"no result / rejected"| HARD
    RESULT -->|"objective achieved"| SUCCESS
    HARD --> DETECT --> REHYP --> REPLAN --> RETRY
    RETRY --> EXEC

    EMPTY{"No alternatives<br/>left?"}
    REHYP -->|"knowledge base empty"| ABORT["No viable path<br/>report partial, escalate"]
    ABORT --> REPORT["Engagement report<br/>(partial objectives)"]

    style DETECT fill:#14141f,stroke:#5eead4,color:#5eead4
    style REHYP fill:#14141f,stroke:#5eead4,color:#5eead4
    style REPLAN fill:#14141f,stroke:#5eead4,color:#5eead4
    style HARD fill:#2a1810,stroke:#a04000,color:#f0a868
    style SOFT fill:#0d1b2a,stroke:#1b4f72,color:#e4e4e8
    style SUCCESS fill:#0d2818,stroke:#1e8449,color:#82e0aa
    style ABORT fill:#2a0d0d,stroke:#a00000,color:#f08080
```

**Reading the diagram**: Execution branches on result. Soft failures (transient) retry the same node with backoff. Hard failures (the exploit genuinely didn't work) trigger the four-step correction: detect, re-hypothesize (query the knowledge base for alternatives with `exclude_failed=True`), re-plan (the old node is marked failed and a new unexplored node is added — never a retry of the failed technique), retry the new node. If no alternatives remain, the engagement reports partial objectives and escalates. The key invariant: the failed technique is removed from the candidate set, forcing genuine substitution, not variation.

---

## Diagram 4 — The Dual Containment Model (sandbox inversion)

**Type**: Mermaid (flowchart)
**Purpose**: The load-bearing diagram of S03.3. Shows the two independent containment boundaries — outbound scope enforcement and inbound isolation — and why both are required.

```mermaid
flowchart TB
    subgraph HOST["HOST (developer machine / CI runner)"]
        HOSTCREDS["Host credential stores<br/>SSH agent, keychain,<br/>~/.aws/credentials<br/>NOT mounted into container"]
        MGMT["Control plane<br/>(harness → agent instructions)<br/>separate channel"]
    end

    subgraph CONTAINER["Agent container (isolated network namespace)"]
        AGENT["Agent process"]
        QUARANTINE["Credential quarantine vault<br/>encrypted, separate key<br/>destroyed on teardown"]
        EGRESSCLIENT["Egress proxy client"]
    end

    subgraph PROXY["Egress proxy / firewall"]
        ACL["ACL generated from scope.json<br/>ALLOW: declared in-scope targets<br/>DENY: host LAN, metadata endpoint<br/>169.254.169.254, everything else"]
    end

    subgraph TARGET["Target network segment (in-scope only)"]
        TARGETS["Declared targets"]
    end

    AGENT -->|"outbound connection<br/>(every tool call)"| EGRESSCLIENT
    EGRESSCLIENT -->|"only path out"| ACL
    ACL -->|"in-scope IP/port"| TARGETS
    ACL -.->|"DENIED: out-of-scope,<br/>host LAN, metadata"| BLOCKED["blocked at network layer"]

    TARGETS -.->|"NO inbound route<br/>to container or host"| BLOCKED2["reverse shell / callback<br/>terminates in container only"]

    MGMT <-->|"control plane only"| AGENT
    AGENT <-->|"engagement creds<br/>never touch host"| QUARANTINE

    style ACL fill:#14141f,stroke:#5eead4,color:#5eead4
    style QUARANTINE fill:#14141f,stroke:#5eead4,color:#5eead4
    style BLOCKED fill:#2a0d0d,stroke:#a00000,color:#f08080
    style BLOCKED2 fill:#2a0d0d,stroke:#a00000,color:#f08080
    style HOSTCREDS fill:#0d2818,stroke:#1e8449,color:#82e0aa
    style TARGETS fill:#2a1810,stroke:#a04000,color:#f0a868
```

**Reading the diagram**: The host sits at the top — its credential stores are explicitly NOT mounted into the container. The agent container is an isolated network namespace; its only path out is through the egress proxy, whose ACL is generated from `scope.json`. In-scope targets are reachable; everything else (host LAN, the cloud metadata endpoint, the open internet) is denied at the network layer. Critically, there is NO inbound route from the target segment back to the container or host — reverse shells and callbacks set up during the engagement terminate inside the container. The control plane (harness instructions to the agent) runs over a separate channel. The two boundaries are independent: if the agent is compromised via prompt injection and tries to scan the host LAN, outbound enforcement blocks it; if it sets up a reverse shell, inbound isolation contains it.

---

## Diagram 5 — Report Generation Pipeline

**Type**: Mermaid (flowchart)
**Purpose**: From structured engagement data to client-ready deliverable. Shows where CVSS drafts are flagged, where remediation mapping is deterministic vs. model-generated, and the three output formats.

```mermaid
flowchart LR
    subgraph INPUTS["Structured engagement data"]
        GRAPH["Attack graph<br/>(succeeded nodes)"]
        EVID["Evidence chain<br/>(hash-chained records)"]
        MEM["Engagement memory<br/>(S02.1 target state)"]
    end

    EXTRACT["Extract findings<br/>one per succeeded node<br/>+ evidence_refs + affected assets"]
    CVSS["CVSS DRAFT<br/>(auto-scored, ALWAYS flagged<br/>'pending analyst review')"]
    CWE["CWE mapping<br/>(lookup, deterministic)"]
    REMED["Remediation<br/>short-term + long-term<br/>+ OWASP control (lookup)<br/>+ target-specific detail (model)"]
    NARR["Executive summary<br/>(model-written, LAST)<br/>grounded in specific finding set"]
    RENDER["Render template<br/>(finding-by-finding)"]

    EXTRACT --> CVSS --> REMED
    EXTRACT --> CWE --> REMED
    REMED --> NARR --> RENDER

    RENDER -->|"interactive,<br/>clickable evidence"| HTML["HTML report"]
    RENDER -->|"static, signable<br/>(headless browser)"| PDF["PDF report"]
    RENDER -->|"machine-readable<br/>(ticketing import)"| JSON["JSON export"]

    INPUTS --> EXTRACT

    style CVSS fill:#2a1810,stroke:#a04000,color:#f0a868
    style CWE fill:#0d2818,stroke:#1e8449,color:#82e0aa
    style REMED fill:#14141f,stroke:#5eead4,color:#5eead4
    style NARR fill:#14141f,stroke:#5eead4,color:#5eead4
    style HTML fill:#0d1b2a,stroke:#1b4f72,color:#e4e4e8
    style PDF fill:#0d1b2a,stroke:#1b4f72,color:#e4e4e8
    style JSON fill:#0d1b2a,stroke:#1b4f72,color:#e4e4e8
```

**Reading the diagram**: Inputs are the three structured stores — attack graph (which objectives succeeded), evidence chain (the proof for each), engagement memory (target context). Each succeeded node becomes a finding. CVSS is drafted automatically but ALWAYS flagged as pending human review (orange) — it is the known-weak output. CWE mapping is a deterministic lookup (green) — it cannot hallucinate. Remediation splits: the OWASP control mapping is a lookup, the target-specific detail is model-generated. The executive summary is written LAST, after all findings are triaged, so it can summarize the actual finding set. One source of truth, three renderings (HTML interactive, PDF static, JSON machine-readable).

---

## Diagram 6 — Cross-Session Kill Chain Continuity

**Type**: Mermaid (flowchart)
**Purpose**: How the attack graph survives session boundaries. Day one pauses mid-chain; day two resumes from structural truth, not from yesterday's reasoning.

```mermaid
flowchart TD
    subgraph DAY1["Day 1 — engagement in progress"]
        D1EXEC["Execute nodes<br/>Initial Access ✓<br/>Foothold ✓<br/>Discovery (attempting...)"]
        D1PAUSE["Pause engagement"]
        D1SERIAL["Serialize attack graph to disk<br/>every state transition"]
    end

    DISK[("attack-graph.json<br/>+ evidence-chain.jsonl<br/>PERSISTED, not in context window")]

    subgraph DAY2["Day 2 — fresh session"]
        D2LOAD["Load graph from disk"]
        D2ORIENT["Graph orientation prompt:<br/>'Here is the graph.<br/>You are at [nodes].<br/>Next available: [techniques].<br/>What is your next action?'"]
        D2VERIFY["Verify evidence_refs<br/>still resolve"]
        D2RESUME["Resume from structural truth<br/>NOT yesterday's reasoning"]
    end

    D1EXEC --> D1SERIAL --> DISK
    D1PAUSE --> DISK
    DISK --> D2LOAD --> D2ORIENT --> D2VERIFY --> D2RESUME

    CONTEXT["Context window<br/>(reasoning for current step only)"]
    CONTEXT -.->|"compacts / cleared<br/>between sessions"| GONE["yesterday's reasoning<br/>is NOT trusted on resume"]

    style DISK fill:#0d2818,stroke:#1e8449,color:#82e0aa
    style D2ORIENT fill:#14141f,stroke:#5eead4,color:#5eead4
    style GONE fill:#2a0d0d,stroke:#a00000,color:#f08080
    style CONTEXT fill:#2a1810,stroke:#a04000,color:#f0a868
```

**Reading the diagram**: The graph is serialized to disk at every state transition during day one. On pause, the persisted graph and evidence chain are the source of truth. Day two opens a fresh session: the context window is empty (orange, untrusted — yesterday's reasoning may be stale). The harness loads the graph, issues an orientation prompt that rebuilds reasoning from structural truth, verifies evidence references still resolve, and resumes. The graph is structural truth; reasoning is always rebuilt from it. This is why intermediate state loss (S03.1) and cross-session loss are both solved by the same structure.
