All Mermaid validated in Mermaid Live Editor. n8n JSON is structurally valid and importable.
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.
{
"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.
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.
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.
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.
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.
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.
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.
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.
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).
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.
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.
# 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.