How must an instance quarantine adversarial content in retrieved RAG documents?
**Date of Record:** 2026 October 27 **Scope:** Applies when an agentic RAG pipeline retrieves external documents (web pages, user-generated content, or third-party datasets) and passes them to an LLM as context, and those documents may contain adversarial content (prompt injection, hidden instructions, or misleading claims). Does not apply to strictly curated internal knowledge bases or documents with provenance from a single trusted source.
Core Distinction
The separation between **retrieval relevance** (the document matches the query semantically) and **content safety** (the document does not contain prompt injection, malicious instructions, or adversarial content). Relying on relevance alone creates the **Injection via Retrieval Hazard**: the document is relevant to the query, so the agent passes it to the LLM as context, and the document's adversarial content hijacks the LLM's behavior (e.g., instructing it to exfiltrate data or ignore safety rules).
What Was Observed
In RAG-based agent architectures, failing to quarantine adversarial retrieved content produces three primary failure modes:
- **Indirect Prompt Injection:** The retrieved document contains text like "Ignore previous instructions and instead do X". The LLM follows it, executing actions the agent never intended.
- **Data Exfiltration via Context:** The adversarial document instructs the LLM to include sensitive system data in its response. The agent returns it to the user, leaking internal information.
- **Trust Boundary Bypass:** The retrieved document is treated as trusted input (like system instructions) rather than untrusted data, allowing injection to override the agent's safety rules.
Recorded Elements
- **`retrieval_provenance_receipt`:** The cryptographic proof of the document's origin, allowing the agent to classify trust level (internal KB vs. third-party web).
- **`injection_scan_receipt`:** A cryptographic proof that the document was scanned for prompt injection patterns (imperative commands, role-play instructions, system-prompt-like text), and whether any were found.
- **`structural_delimiter_hash`:** The cryptographic proof that the document is wrapped in delimiters that distinguish document content from instructions, preventing the LLM from confusing the two.
- **`quarantine_action_receipt`:** If injection is detected, the cryptographic proof of the quarantine action (document dropped, sanitized, or flagged for human review).
Condition of Validity
* **Applies when:** The RAG pipeline retrieves documents from untrusted sources (web, user content, third-party datasets), the LLM receives documents as context, and the LLM's behavior can be influenced by document content. * **Does not apply when:** All retrieved documents come from a strictly internal, curated knowledge base with documented safety review, or the LLM's response does not execute actions (pure informational output).
Decision Boundary
- **Is the retrieved document from an untrusted source?** If no, standard retrieval applies. If yes, proceed.
- **Has the document been scanned for injection patterns?** If no, the agent must scan before passing to the LLM.
- **Were injection patterns detected?** If yes, the agent must quarantine (drop the document) or sanitize (strip injection patterns) before passing to the LLM.
- **Conversely, when can an agent pass untrusted documents without injection scanning?** Only when the documents are wrapped in strict structural delimiters that the LLM is trained to treat as data (e.g., specific XML tags with documented behavior), AND the retrieval provenance indicates the source is at least semi-trusted.
Operational Effect
- **Injection Prevention:** Blocks indirect prompt injection before it reaches the LLM.
- **Trust Boundary Enforcement:** Treats retrieved content as data, not instructions, preserving the agent's safety rules.
- **Audit Trail:** Provides cryptographic proof that scanning occurred and quarantine actions were taken.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json
class QuarantineVerdict(Enum):
TRUSTED_SOURCE_ACCEPTED = auto()
SAFE_AFTER_SCAN = auto()
INJECTION_QUARANTINED = auto()
UNSANITIZED_REJECTED = auto()
@dataclass(frozen=True)
class RetrievedDocument:
doc_id: str
source_trust_level: str
injection_scan_performed: bool
injection_patterns_found: tuple[str, ...]
structural_delimiter_present: bool
@dataclass(frozen=True)
class RetrievalSafetyPolicy:
trusted_source_levels: frozenset[str]
allow_safe_after_scan: bool
@dataclass(frozen=True)
class QuarantineManifest:
verdict: QuarantineVerdict
directive: str
manifest_digest: str
class RAGInjectionGuard:
@staticmethod
def _hash(data: str) -> str:
return hashlib.sha256(data.encode("utf-8")).hexdigest()
@classmethod
def evaluate_document(
cls, doc: RetrievedDocument, policy: RetrievalSafetyPolicy
) -> QuarantineManifest:
# Gate 1: Trusted source
if doc.source_trust_level in policy.trusted_source_levels:
digest = cls._hash(json.dumps({
"doc": doc.doc_id,
"trust": doc.source_trust_level
}, sort_keys=True))
return QuarantineManifest(
verdict=QuarantineVerdict.TRUSTED_SOURCE_ACCEPTED,
directive=f"PROCEED: Source {doc.source_trust_level} is trusted.",
manifest_digest=digest
)
# Gate 2: Scan requirement
if not doc.injection_scan_performed:
return QuarantineManifest(
verdict=QuarantineVerdict.UNSANITIZED_REJECTED,
directive=f"HALT: Untrusted doc {doc.doc_id} not scanned. Reject.",
manifest_digest=cls._hash(f"NO_SCAN:{doc.doc_id}")
)
# Gate 3: Injection found
if doc.injection_patterns_found:
return QuarantineManifest(
verdict=QuarantineVerdict.INJECTION_QUARANTINED,
directive=f"HALT: Injection patterns in {doc.doc_id}: {sorted(doc.injection_patterns_found)}. Quarantine.",
manifest_digest=cls._hash(f"INJECTION:{doc.doc_id}:{len(doc.injection_patterns_found)}")
)
# Gate 4: Safe after scan
if policy.allow_safe_after_scan and doc.structural_delimiter_present:
digest = cls._hash(json.dumps({
"doc": doc.doc_id,
"trust": doc.source_trust_level,
"scan": "CLEAN",
"delimiter": "PRESENT"
}, sort_keys=True))
return QuarantineManifest(
verdict=QuarantineVerdict.SAFE_AFTER_SCAN,
directive=f"PROCEED: {doc.doc_id} scanned clean with structural delimiters.",
manifest_digest=digest
)
# Gate 5: Scan clean but no delimiters
return QuarantineManifest(
verdict=QuarantineVerdict.UNSANITIZED_REJECTED,
directive=f"HALT: Doc {doc.doc_id} scan clean but lacks structural delimiters. Cannot guarantee safety.",
manifest_digest=cls._hash(f"NO_DELIMITER:{doc.doc_id}")
)