AID-D-001.001: Per-Prompt Content, Intent & Obfuscation Analysis
Pillars: app
Lifecycle: operation
Scope boundary
Owns synchronous per-prompt findings for harmful content, injection, jailbreak, instruction bypass, intent switching, privilege-escalation intent, and bounded obfuscation analysis. It emits evidence but does not authorize or reject the prompt.
AID-H-002.002: AID-D-001.001 analyzes the prompt and emits calibrated content, intent, and obfuscation findings; AID-H-002.002 consumes those findings and owns sanitize, block, downgrade, review, or admission behavior. A detector verdict is policy input rather than the admission decision.
AID-D-001.003: AID-D-001.001 analyzes explicit content, intent, and recovered obfuscation views; AID-D-001.003 scores statistical distance or density in a governed embedding space. Semantic or lexical policy signals and vector-distribution anomalies are complementary findings with different baselines and failure modes.
AID-D-001.006: AID-D-001.001 produces a finding for the current prompt; AID-D-001.006 correlates prompt-gate verdict telemetry across requests, sessions, and identities. Per-request analysis identifies the immediate signal, while correlation identifies cumulative probing and repeated near misses.
Implementation guidance
- AID-D-001.001-G001: Run a pinned, calibrated secondary classifier for prompt content, intent switching, instruction bypass, goal manipulation, and privilege-escalation signals, and emit a structured finding without making the admission decision.
- AID-D-001.001-G002: Recursively decode bounded encoding layers, normalize Unicode, and score every recovered view for obfuscation and prompt-policy signals without logging or executing decoded content.
AID-D-001.001-G001
Concept
This detector scores one prompt and publishes evidence. It does not reject the request. Load the exact classifier revision and category thresholds from a versioned deployment policy, preserve a content digest rather than raw prompt text, and return FINDING, NO_FINDING, or ERROR. The consuming AID-H-002.002 policy decides whether an error or finding blocks, sanitizes, downgrades, or routes the request for review.
Build a pinned classifier and evidence contract
from __future__ import annotations
# File: detect/prompt_intent_detector.py
import hashlib
import json
import os
from dataclasses import asdict, dataclass
from pathlib import Path
from typing import Literal
from transformers import pipeline
ControlStatus = Literal["PASS", "ERROR"]
FindingStatus = Literal["FINDING", "NO_FINDING", "UNKNOWN"]
MODEL_ID = os.environ["PROMPT_DETECTOR_MODEL_ID"]
MODEL_REVISION = os.environ["PROMPT_DETECTOR_MODEL_REVISION"]
POLICY_PATH = Path(os.environ["PROMPT_DETECTOR_POLICY_PATH"])
classifier = pipeline(
"text-classification",
model=MODEL_ID,
tokenizer=MODEL_ID,
revision=MODEL_REVISION,
top_k=None,
truncation=True,
)
@dataclass(frozen=True)
class PromptFinding:
schema_version: str
control_id: str
request_id: str
prompt_sha256: str
control_status: ControlStatus
finding_status: FindingStatus
categories: list[str]
scores: dict[str, float]
model_id: str
model_revision: str
policy_version: str
error_class: str | None = None
def _load_policy() -> dict:
policy = json.loads(POLICY_PATH.read_text(encoding="utf-8"))
required = {"policy_version", "category_thresholds"}
if set(policy) != required or not isinstance(policy["category_thresholds"], dict):
raise ValueError("prompt detector policy schema mismatch")
for category, threshold in policy["category_thresholds"].items():
if not isinstance(category, str) or not isinstance(threshold, (int, float)):
raise ValueError("invalid prompt detector threshold")
if not 0.0 <= float(threshold) <= 1.0:
raise ValueError("prompt detector threshold outside [0,1]")
return policy
def _flatten(raw: object) -> list[dict]:
if isinstance(raw, list) and raw and isinstance(raw[0], list):
raw = raw[0]
if not isinstance(raw, list):
raise ValueError("unexpected classifier result shape")
return [item for item in raw if isinstance(item, dict)]
def detect_prompt(request_id: str, prompt: str) -> PromptFinding:
digest = hashlib.sha256(prompt.encode("utf-8")).hexdigest()
try:
policy = _load_policy()
predictions = _flatten(classifier(prompt))
scores = {
str(item["label"]): round(float(item["score"]), 6)
for item in predictions
if "label" in item and "score" in item
}
matched = sorted(
category
for category, threshold in policy["category_thresholds"].items()
if scores.get(category, 0.0) >= float(threshold)
)
return PromptFinding(
schema_version="aidefend.prompt_finding.v1",
control_id="AID-D-001.001",
request_id=request_id,
prompt_sha256=digest,
control_status="PASS",
finding_status="FINDING" if matched else "NO_FINDING",
categories=matched,
scores=scores,
model_id=MODEL_ID,
model_revision=MODEL_REVISION,
policy_version=policy["policy_version"],
)
except Exception as exc:
return PromptFinding(
schema_version="aidefend.prompt_finding.v1",
control_id="AID-D-001.001",
request_id=request_id,
prompt_sha256=digest,
control_status="ERROR",
finding_status="UNKNOWN",
categories=[],
scores={},
model_id=MODEL_ID,
model_revision=MODEL_REVISION,
policy_version="unavailable",
error_class=type(exc).__name__,
)
def publish_finding(request_id: str, prompt: str, sink) -> None:
sink(asdict(detect_prompt(request_id, prompt)))
Promotion and verification
Calibrate category thresholds on a versioned corpus containing benign, harmful, prompt-injection, jailbreak, goal-switching, privilege-escalation, multilingual, and unsupported-language segments. Record per-segment false-positive and false-negative rates, model/revision, policy digest, corpus digest, and shadow-versus-production comparison. Replay known positive, benign, timeout, malformed-output, and unsupported-input cases and verify the exact status and evidence fields.
Action: Publish the finding synchronously before prompt admission. Do not include an allow field and do not log raw prompt text. Configure AID-H-002.002 to consume FINDING, NO_FINDING, and ERROR under its own signed policy.
AID-D-001.001-G002
Concept
Attackers split payloads across Base64, hex, Unicode escapes, mixed scripts, or multiple nested layers. Decode only recognized complete candidates under strict depth, candidate, byte, and time budgets. Analyze both the raw and recovered views, store hashes instead of recovered text, and treat a decoding or detector failure as ERROR rather than a clean result.
Bounded detector
from __future__ import annotations
# File: detect/obfuscation_detector.py
import base64
import binascii
import hashlib
import re
import time
import unicodedata
from collections import deque
from dataclasses import dataclass
from typing import Callable, Literal
BASE64_RE = re.compile(r"(?<![A-Za-z0-9+/])(?:[A-Za-z0-9+/]{4}){4,}(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?(?![A-Za-z0-9+/=])")
HEX_RE = re.compile(r"(?<![0-9a-fA-F])(?:[0-9a-fA-F]{2}){8,}(?![0-9a-fA-F])")
UNICODE_ESCAPE_RE = re.compile(r"(?:\u[0-9a-fA-F]{4}|\x[0-9a-fA-F]{2}){4,}")
@dataclass(frozen=True)
class DetectorPolicy:
policy_version: str
max_input_chars: int
max_depth: int
max_candidates: int
max_decoded_bytes: int
max_elapsed_ms: int
finding_score: int
def _decode(candidate: str, encoding: str) -> str | None:
try:
if encoding == "base64":
raw = base64.b64decode(candidate, validate=True)
return raw.decode("utf-8")
if encoding == "hex":
return binascii.unhexlify(candidate).decode("utf-8")
if encoding == "unicode_escape":
return candidate.encode("ascii").decode("unicode_escape")
except (UnicodeError, binascii.Error, ValueError):
return None
return None
def _candidates(text: str) -> list[tuple[str, str]]:
found: list[tuple[str, str]] = []
for encoding, pattern in (
("base64", BASE64_RE),
("hex", HEX_RE),
("unicode_escape", UNICODE_ESCAPE_RE),
):
found.extend((encoding, match.group(0)) for match in pattern.finditer(text))
return found
def detect_obfuscation(
prompt: str,
request_id: str,
policy: DetectorPolicy,
content_detector: Callable[[str], dict],
) -> dict:
started = time.monotonic()
if not isinstance(prompt, str) or len(prompt) > policy.max_input_chars:
return {
"control_id": "AID-D-001.001",
"request_id": request_id,
"control_status": "ERROR",
"finding_status": "UNKNOWN",
"reason": "unsupported_or_oversized_input",
"policy_version": policy.policy_version,
}
queue = deque([(unicodedata.normalize("NFKC", prompt), 0, "raw")])
seen: set[str] = set()
evidence: list[dict] = []
decoded_bytes = 0
score = 0
try:
while queue:
if (time.monotonic() - started) * 1000 > policy.max_elapsed_ms:
raise TimeoutError("obfuscation budget exceeded")
text, depth, encoding = queue.popleft()
if text in seen:
continue
seen.add(text)
detector_result = content_detector(text)
if (
not isinstance(detector_result, dict)
or detector_result.get("control_status") != "PASS"
or detector_result.get("finding_status")
not in {"FINDING", "NO_FINDING"}
):
raise RuntimeError("content detector dependency failed")
if detector_result["finding_status"] == "FINDING":
score += 40
if encoding != "raw":
score += 15
evidence.append({
"encoding": encoding,
"depth": depth,
"content_sha256": hashlib.sha256(text.encode("utf-8")).hexdigest(),
"content_detector_control_status": detector_result["control_status"],
"content_detector_finding_status": detector_result["finding_status"],
})
if depth >= policy.max_depth:
continue
for child_encoding, candidate in _candidates(text):
if len(evidence) + len(queue) >= policy.max_candidates:
break
decoded = _decode(candidate, child_encoding)
if decoded is None:
continue
decoded = unicodedata.normalize("NFKC", decoded)
decoded_bytes += len(decoded.encode("utf-8"))
if decoded_bytes > policy.max_decoded_bytes:
raise ValueError("decoded byte budget exceeded")
queue.append((decoded, depth + 1, child_encoding))
return {
"control_id": "AID-D-001.001",
"request_id": request_id,
"control_status": "PASS",
"finding_status": "FINDING" if score >= policy.finding_score else "NO_FINDING",
"score": score,
"policy_version": policy.policy_version,
"views": evidence,
}
except Exception as exc:
return {
"control_id": "AID-D-001.001",
"request_id": request_id,
"control_status": "ERROR",
"finding_status": "UNKNOWN",
"reason": type(exc).__name__,
"policy_version": policy.policy_version,
"views": evidence,
}
Verification and handoff
Replay nested Base64, hex, Unicode escapes, homoglyph normalization, oversized input, candidate explosion, timeout, detector outage, benign encoded data, and true injection cases. Verify resource limits, status semantics, evidence hashes, and that no decoded payload reaches logs or execution. Load the policy values from a signed versioned artifact.
Action: Publish the evidence before context assembly. AID-H-002.002 owns the fail-closed admission response; this detector never emits allow or executes decoded content.
Open-source tools
- NVIDIA NeMo Guardrails
Source-available / open-weight tools
- Granite Guardian 4.1 (Apache-2.0; open-weight)
- Qwen3Guard (Apache-2.0; open-weight)
- gpt-oss-safeguard (Apache-2.0; open-weight)
- Meta Llama Guard 4 (Llama 4 Community License; open-weight)
- Meta Llama Prompt Guard 2 (Llama 4 Community License; open-weight)
- Google ShieldGemma (Gemma Terms of Use; open-weight)
Commercial / hosted tools
- OpenAI Moderations API (omni-moderation-latest)
- Check Point AI Guardrails
- Palo Alto Networks Prisma AIRS
- F5 AI Guardrails
- Securiti LLM Firewall
- Google Cloud Model Armor
- Amazon Bedrock Guardrails
- Azure AI Content Safety - Prompt Shields
Threat mappings
MITRE ATLAS
- AML.T0051 LLM Prompt Injection
- AML.T0051.000 LLM Prompt Injection: Direct
- AML.T0051.001 LLM Prompt Injection: Indirect
- AML.T0051.002 LLM Prompt Injection: Triggered
- AML.T0054 LLM Jailbreak
- AML.T0053 AI Agent Tool Invocation (intent analysis identifies prompts seeking unauthorized tool use or privilege escalation)
- AML.T0068 LLM Prompt Obfuscation
- AML.T0069.000 Discover LLM System Information: Special Character Sets (obfuscation analysis detects character encoding and distribution patterns used for probing)
- AML.T0015 Evade AI Model (per-prompt obfuscation analysis detects adversarial evasion inputs)
- AML.T0123 Obfuscated Files or Information (layered-encoding, homoglyph, and high-entropy analysis targets payloads obfuscated to evade filters)
MAESTRO
- Input Validation Attacks (L3)
- Reprogramming Attacks (L1)
- Agent Goal Manipulation (L7)
- Privilege Escalation (Cross-Layer) (intent analysis detects requests to exceed the caller or agent authority envelope)
- Adversarial Examples (L1) (prompt obfuscation is a form of adversarial input)
- Evasion of Security AI Agents (L6) (obfuscation analysis detects attempts to evade security AI)
OWASP LLM Top 10 2026
- LLM01:2026 Prompt Injection
OWASP ML Top 10 2023
- ML01:2023 Input Manipulation Attack
OWASP Top 10 for Agentic Applications 2026
- ASI01:2026 Agent Goal Hijack (adversarial prompts can redirect agent goals)
- ASI03:2026 Identity and Privilege Abuse
NIST Adversarial Machine Learning 2025
- NISTAML.018 Prompt Injection
- NISTAML.022 Evasion (obfuscation techniques are a form of evasion)
- NISTAML.015 Indirect Prompt Injection (obfuscation analysis catches hidden indirect injections)
- NISTAML.025 Black-box Evasion (obfuscated prompts are used in evasion attempts)
Cisco Integrated AI Security and Safety Framework
- AISubtech-1.1.1 Instruction Manipulation
- AISubtech-1.1.2 Obfuscation
- AISubtech-1.1.5 Context Manipulation
- AISubtech-15.1.25 General Harms (prompt-injection and prompt-safety categories implemented by the pinned classifier)
- AITech-1.1 Direct Prompt Injection and Jailbreak (Prompt-based Manipulation)
- AITech-15.1 Harmful Content (prompt-injection and prompt-safety categories implemented by the pinned classifier)
- AITech-9.2 Detection Evasion (normalization and recovered obfuscation views expose classifier-evasion attempts)
Google Secure AI Framework 2.0 - Risks
- PIJ: Prompt Injection
- MEV: Model Evasion (obfuscation analysis detects evasion techniques)
- RA: Rogue Actions (intent analysis identifies prompts seeking unauthorized agent action)
Databricks AI Security Framework 3.0
- Model Serving - Inference requests 9.1: Prompt inject
- Model Serving - Inference requests 9.12: LLM Jailbreak
- Model Serving - Inference requests 9.13: Excessive agency
- Agents - Core 13.2: Tool Misuse
- Agents - Core 13.3: Privilege Compromise
- Agents - Core 13.6: Intent Breaking & Goal Manipulation
- Agents - Tools MCP Server 13.16: Prompt Injection