# Diagrams — Module B3: Memory and Context Poisoning

**Module**: B3 — Memory and Context Poisoning
**Diagram count**: 5
**Tool**: Mermaid (primary). Each diagram validated in [Mermaid Live Editor](https://mermaid.live).

---

## Diagram 1 — The Two Memory Surfaces

**Type**: Comparison / overlay
**Purpose**: The foundational mental model of the module. An agent has two memory surfaces, and the distinction is load-bearing: **long-term memory persists across sessions** (survives logout/login), **the context window is per-session** (dies with the session). Persistence is the single property that makes memory a categorically different attack surface from the context window. B2's five layers operate inside the context window; this module's attacks and defenses extend into the persistent surface.
**Reading the diagram**: Two horizontal bands. The top (long-term memory) is durable — it survives the session boundary. The bottom (context window) is ephemeral — it dies when the session ends. The arrow between them is the memory load that happens at session start, *before* the user types anything. Note that B2's layers all sit inside the context window and never touch the load path.

```mermaid
flowchart LR
  subgraph LTM["LONG-TERM MEMORY — persists across sessions"]
    direction TB
    L1["stored OUTSIDE the model<br/>DB · vector store · KV cache · files"]
    L2["survives logout / login / refresh<br/>version bump / session timeout"]
    L3["loaded into context at session START<br/>before the user types anything"]
  end
  subgraph CW["CONTEXT WINDOW — per-session"]
    direction TB
    C1["system prompt · conversation history<br/>retrieved context · tool outputs (this turn)"]
    C2["born at session start<br/>DIES at session end"]
    C3["B2's 5 layers operate HERE<br/>tag · isolate · taint · gate · minimize"]
  end

  LTM ===|"memory load<br/>at session start<br/>(trusted by default)"| CW

  style LTM fill:#14141f,stroke:#f08080,stroke-width:1.5px,color:#e4e4e8
  style CW fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#e4e4e8
  style L1 fill:#101018,stroke:#f08080,color:#e4e4e8
  style L2 fill:#101018,stroke:#f08080,color:#e4e4e8
  style L3 fill:#101018,stroke:#f08080,color:#e4e4e8
  style C1 fill:#101018,stroke:#5eead4,color:#e4e4e8
  style C2 fill:#101018,stroke:#5eead4,color:#e4e4e8
  style C3 fill:#101018,stroke:#5eead4,color:#e4e4e8
```

> **Note**: The asymmetry is the point. Everything B2 defends is inside the context window and dies with the session. Long-term memory survives — and because it is loaded as trusted by default, a poisoned entry routes around all five B2 layers. B3 extends the defense into the persistent surface.

---

## Diagram 2 — The Sleeper Attack Across Sessions

**Type**: Timeline / sequence
**Purpose**: The centerpiece attack of the module, made visual. The sleeper attack (Course 1 Module 4.3): inject in session 1, the payload sleeps in memory, it activates in session 2. The fuse crosses the session boundary. The critical failure is not the injection in session 1 — it is the **unvalidated read in session 2**, where the memory loader promotes a poisoned entry to trusted context without re-checking it.
**Reading the diagram**: Two sessions separated by the session boundary (the fuse). In session 1 the seed is planted and is inert. Persistence carries it across. In session 2 the seed loads as trusted and fires. The red callout marks the load-bearing failure: the read path trusts the write path unconditionally.

```mermaid
flowchart TB
  subgraph S1["SESSION 1 — THE INJECTION"]
    direction TB
    A1["attacker delivers payload"]
    A2["agent writes SEED to memory<br/>(via save_to_memory, or ingest, or summary)"]
    A3["seed is INERT this session<br/>nothing visible happens"]
    A1 --> A2 --> A3
  end

  BOUND["SESSION BOUNDARY — THE FUSE<br/>logout · login · days pass<br/>user forgets session 1"]:::warn

  subgraph S2["SESSION 2 — THE ACTIVATION"]
    direction TB
    B1["user starts fresh session"]
    B2["memory loader runs<br/>pulls seed into context as TRUSTED"]
    B3["no tag (B2 L1 missed it)<br/>no gate (B2 L3 missed it)<br/>seed is already in the window"]
    B4["SEED FIRES<br/>agent obeys as if system prompt"]
    B1 --> B2 --> B3 --> B4
  end

  FAIL["THE LOAD-BEARING FAILURE<br/>unvalidated READ in session 2<br/>memory trusted by default<br/>nothing re-checks the seed"]:::danger

  S1 --> BOUND --> S2
  B2 -. fails here .-> FAIL

  classDef warn fill:#14141f,stroke:#f0a868,stroke-width:1.5px,color:#f0a868
  classDef danger fill:#14141f,stroke:#f08080,stroke-width:1.5px,color:#f08080
  style S1 fill:#14141f,stroke:#f0a868,color:#e4e4e8
  style S2 fill:#14141f,stroke:#f08080,color:#e4e4e8
  style A1 fill:#101018,stroke:#f0a868,color:#e4e4e8
  style A2 fill:#101018,stroke:#f0a868,color:#e4e4e8
  style A3 fill:#101018,stroke:#f0a868,color:#e4e4e8
  style B1 fill:#101018,stroke:#f08080,color:#e4e4e8
  style B2 fill:#101018,stroke:#f08080,color:#e4e4e8
  style B3 fill:#101018,stroke:#f08080,color:#e4e4e8
  style B4 fill:#101018,stroke:#f08080,stroke-width:1.5px,color:#f08080
```

> **Note**: The two events may be days apart, in different contexts, under different account states — which makes the sleeper attack harder to attribute than a same-session injection. By the time it fires, the trail back to session 1 is cold. The defense (B3.3) does not try to catch the injection in session 1; it ensures the seed can never be written, and that any residual poison is tagged on read.

---

## Diagram 3 — Retrieval Contamination Flow

**Type**: Data-flow / pipeline
**Purpose**: How a poisoned chunk travels from a writable source through the retrieval store into the context window. The retrieval store is written by many paths (user uploads, crawled content, agent summaries, cross-tenant leaks) — not all trusted. Once a poisoned chunk is in the index, retrieval is the activation mechanism: the attacker crafts the chunk to be semantically similar to the victim's queries, so the poison is fetched, inserted as data-but-obeyed-as-instruction, and the agent complies. This is indirect injection (B2's most dangerous class) with the persistence property of memory.
**Reading the diagram**: Left to right — the write paths into the index (red = untrusted), the poisoned chunk persisting, the retrieval fetching it on a relevant query, and the injection into the context window. The green callout marks the defense: retrieved-content taint tagging (B2 Layer 1) connects B3 retrieval defense to the B2 stack.

```mermaid
flowchart LR
  subgraph WRITE["WRITE PATHS INTO THE INDEX"]
    direction TB
    W1["user-uploaded doc"]:::danger
    W2["crawled / synced content<br/>(compromised wiki page)"]:::danger
    W3["agent-generated summary<br/>(laundered injection)"]:::danger
    W4["cross-tenant leak<br/>(weak isolation)"]:::danger
  end

  IDX[("RETRIEVAL STORE<br/>vector / keyword index<br/>PERSISTS — poison stays<br/>until found + removed")]:::warn

  subgraph RETRIEVE["RETRIEVAL — THE ACTIVATION"]
    direction TB
    R1["victim issues a query"]
    R2["poisoned chunk is semantically similar<br/>→ it is FETCHED"]
    R3["chunk inserted into context window<br/>as DATA — but obeyed as INSTRUCTION"]
    R1 --> R2 --> R3
  end

  DEF["DEFENSE — taint tagging<br/>retrieved chunk tagged UNTRUSTED (B2 L1)<br/>taint gate (B2 L3) blocks high-impact calls<br/>provenance filtering + re-ranking upstream"]:::good

  WRITE --> IDX
  IDX --> RETRIEVE
  R3 -. defended by .-> DEF

  classDef danger fill:#101018,stroke:#f08080,color:#e4e4e8
  classDef warn fill:#14141f,stroke:#f0a868,stroke-width:1.5px,color:#f0a868
  classDef good fill:#14141f,stroke:#82e0aa,stroke-width:1.5px,color:#82e0aa
  style W1 fill:#101018,stroke:#f08080,color:#e4e4e8
  style W2 fill:#101018,stroke:#f08080,color:#e4e4e8
  style W3 fill:#101018,stroke:#f08080,color:#e4e4e8
  style W4 fill:#101018,stroke:#f08080,color:#e4e4e8
  style IDX fill:#14141f,stroke:#f0a868,stroke-width:2px,color:#f0a868
  style R1 fill:#101018,stroke:#5eead4,color:#e4e4e8
  style R2 fill:#101018,stroke:#f08080,color:#e4e4e8
  style R3 fill:#101018,stroke:#f08080,stroke-width:1.5px,color:#f08080
```

> **Note**: The retrieval store is a memory surface, and it is the most commonly writable one — ingestion pipelines (sync, crawl, bulk import) may bypass the model-propose/harness-validate gate entirely. The defense is not to make the store unwritable; it is to treat every retrieved chunk as untrusted by default (B2 Layer 1) unless its provenance proves otherwise, so the taint gate applies.

---

## Diagram 4 — The Harness-Managed-Write Gate

**Type**: Process / control flow
**Purpose**: The central defense of the module, made visual. The model PROPOSES memory writes; the harness VALIDATES them; only the harness COMMITS. Three checks in order — provenance (where did it come from), trust level (is the source trusted), content (does it look like an injection). The conjunction of *untrusted provenance AND instruction-like content* is the sleeper-attack signature that CHECK 3 blocks deterministically. The model never touches the store directly.
**Reading the diagram**: The model emits a proposal (content + justification + provenance). The gate runs three checks. Any failure → DENY (logged, surfaced). All pass → COMMIT with provenance + trust + decision tag. The unconditional write path (the `save_to_memory` tool) does not exist.

```mermaid
flowchart TB
  PROP["MODEL PROPOSES a write<br/>content + justification + provenance<br/>(the model NEVER touches the store)"]:::accent

  GATE["HARNESS VALIDATION GATE<br/>three checks, in order"]

  C1["CHECK 1 — PROVENANCE<br/>where did the content come from?<br/>user_direct · retrieval · tool · agent_inferred"]:::check
  C2["CHECK 2 — TRUST LEVEL<br/>trusted · semi_trusted · untrusted<br/>(derived from provenance)"]:::check
  C3["CHECK 3 — CONTENT<br/>does it look like an injection?<br/>detector + sleeper-signature heuristic"]:::check

  DENY["DENY — log + surface<br/>injection_detected · untrusted_instruction_like<br/>· provenance_violation"]:::danger

  COMMIT["COMMIT — only the harness writes<br/>entry tagged: source + trust_level<br/>+ timestamp + writer + validation_result"]:::good

  READ["READ PATH (separate)<br/>load entry → tag by trust level<br/>untrusted entries get B2 L1 tag<br/>→ taint gate applies on next session"]:::check

  PROP --> GATE
  GATE --> C1 --> C2 --> C3
  C1 -. fail .-> DENY
  C2 -. untrusted + instruction-like .-> DENY
  C3 -. fail .-> DENY
  C3 -. all pass .-> COMMIT
  COMMIT -. read later .-> READ

  classDef accent fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#5eead4
  classDef check fill:#101018,stroke:#5eead4,color:#e4e4e8
  classDef danger fill:#14141f,stroke:#f08080,stroke-width:1.5px,color:#f08080
  classDef good fill:#14141f,stroke:#82e0aa,stroke-width:1.5px,color:#82e0aa
  style GATE fill:#14141f,stroke:#5eead4,stroke-width:2px,color:#5eead4
  style PROP fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#5eead4
  style C1 fill:#101018,stroke:#5eead4,color:#e4e4e8
  style C2 fill:#101018,stroke:#5eead4,color:#e4e4e8
  style C3 fill:#101018,stroke:#5eead4,color:#e4e4e8
  style DENY fill:#14141f,stroke:#f08080,stroke-width:1.5px,color:#f08080
  style COMMIT fill:#14141f,stroke:#82e0aa,stroke-width:1.5px,color:#82e0aa
  style READ fill:#101018,stroke:#f0a868,color:#e4e4e8
```

> **Note**: The load-bearing line is CHECK 3's conjunction: `if (trustLevel === "untrusted" && looksLikeInstruction(...))`. A trusted user directly stating a preference passes. An untrusted chunk that happens to be factual passes. The conjunction catches the payload that is *both* untrusted-sourced and instruction-shaped — which is exactly what a sleeper seed is. The gate's strength is not perfection; it is the removal of the unconditional write path.

---

## Diagram 5 — Cascading Hallucination (ASI06)

**Type**: Propagation / cascade
**Purpose**: Why a single poisoned or hallucinated result is worse than a localized error — the downstream risk OWASP ranks as ASI06. The agent retrieves or recalls one false premise, treats it as ground truth, and builds all subsequent reasoning on top of it. The error propagates through the chain: false premise → flawed reasoning → flawed action → recorded result → further reasoning. By the time the user sees the output, the original error is buried under layers of plausible derivation, and if the premise was persisted to memory, the cascade recurs every session.
**Reading the diagram**: Top to bottom — the cascade. The poison enters (from memory or retrieval), becomes a premise, drives reasoning, drives an action, the action's result is recorded, and further reasoning builds on it. Each step inherits the original error. The amber callout marks the source: poisoned memory is the most reliable cascade trigger, because the agent treats its own memory as authoritative.

```mermaid
flowchart TB
  SRC["POISONED / HALLUCINATED PREMISE<br/>from memory (ASI04) or retrieval (RAG poison)"]:::danger

  P1["agent treats premise as GROUND TRUTH<br/>(memory = authoritative; retrieval = data-but-obeyed)"]:::danger
  R1["agent REASONS on the false premise"]
  ACT["agent takes an ACTION<br/>calls a tool · generates output<br/>based on the flawed reasoning"]
  REC["action result is RECORDED<br/>(to memory · to context · to a log)"]
  R2["agent reasons FURTHER<br/>on top of the recorded result"]
  OUT["USER SEES THE OUTPUT<br/>internally consistent chain<br/>but the FOUNDATION was poisoned"]:::warn

  CASCADE["THE CASCADE<br/>every downstream step inherits<br/>the original error<br/>the longer it runs, the deeper it buries"]:::danger

  SRC --> P1 --> R1 --> ACT --> REC --> R2 --> OUT
  R2 -. loop .-> R1
  P1 -. drives .-> CASCADE

  classDef danger fill:#14141f,stroke:#f08080,stroke-width:1.5px,color:#f08080
  classDef warn fill:#14141f,stroke:#f0a868,stroke-width:1.5px,color:#f0a868
  style SRC fill:#14141f,stroke:#f08080,stroke-width:1.5px,color:#f08080
  style P1 fill:#101018,stroke:#f08080,color:#f08080
  style R1 fill:#101018,stroke:#f08080,color:#e4e4e8
  style ACT fill:#101018,stroke:#f08080,color:#e4e4e8
  style REC fill:#101018,stroke:#f0a868,color:#e4e4e8
  style R2 fill:#101018,stroke:#f08080,color:#e4e4e8
  style OUT fill:#14141f,stroke:#f0a868,stroke-width:1.5px,color:#f0a868
  style CASCADE fill:#14141f,stroke:#f08080,stroke-width:1.5px,color:#f08080
```

> **Note**: A single hallucination is a localized error the user corrects. A cascading hallucination is a contaminated reasoning chain — and the cascade is faster and harder to interrupt when the foundation is the agent's own prior memory. The defense is not to catch each hallucination individually; it is to prevent poisoned premises from entering memory (B3.3 gate) and to detect propagation when it starts (audit the reasoning chain, purge the poisoned premise from the store).
