A high-severity template injection flaw (CVE-2025-65106) quietly shipped in LangChain ≤0.3.79 and 1.0.0-1.0.6, letting attackers break out of prompt templates and execute arbitrary Python inside your agent process. Because the exploit rides on ordinary-looking template variables, it can slip past prompt filters and travel through RAG pipelines, tool wrappers, or multi-agent message buses. If you run LangChain-backed assistants in production, treat this as an immediate patch-or-isolate event.
How the Attack Works
LangChain’s PromptTemplate engine compiles Jinja2-style strings into native Python before the LLM ever sees them. CVE-2025-65106 exists because user-supplied text can be placed inside double-curly braces {{ }} without further validation. An attacker who controls any field that ends up in the template—query parameters, document metadata, tool outputs, or even a chat message—can inject Python expressions that the template renderer will execute with the full privileges of the agent process.
A minimal proof-of-concept that exfiltrates the OPENAI_API_KEY environment variable looks like this:
{
"user_query": "{{ self.__init__.__globals__.__builtins__.__import__('os').environ.get('OPENAI_API_KEY') }}"
}
When the prompt is rendered, the expression is evaluated and the key is interpolated into the prompt that is forwarded to the LLM, effectively leaking secrets in-band. More sophisticated payloads can write files, spawn reverse shells, or patch LangChain’s internal classes to persist across turns.
The vulnerability is especially sneaky inside Retrieval-Augmented Generation pipelines. A poisoned document stored in a vector DB can carry the payload in its metadata; when the retriever embeds that chunk into the prompt template, the injection fires without the end user ever typing a suspicious character.
Real-World Impact for Agent Deployments
Production agents often run with service-level cloud credentials and have network egress allowed to LLM endpoints, model registries, and internal APIs. Successful exploitation therefore grants an attacker a privileged foothold that can pivot laterally, steal model weights, or tamper with downstream tool calls. Because the exploit is “stateless” and travels inside the prompt itself, it propagates through multi-agent routers, shared memory stores, and even human-in-the-loop review steps.
Incident-response teams should assume that any user-controllable string that can reach a prompt template is a potential lateral movement channel. If you operate shared SaaS agents, one malicious customer can compromise the node running other tenants’ chains unless sandboxing is in place. Likewise, autonomous agents that browse the web or ingest third-party XML/JSON feeds can pull the payload from external content and self-compromise.
Immediate Defensive Measures
- Patch or pin: Upgrade to LangChain 0.3.80 or 1.0.7+ where the template renderer switches to a sandboxed Jinja2 environment that whitelists only safe builtins.
- Audit every PromptTemplate, ChatPromptTemplate, and FewShotPromptTemplate in your codebase for dynamic user input.
- Route templates that must remain dynamic through a sanitization wrapper that blocks curly braces and parentheses using a regex such as
re.sub(r'[{}()]', '', untrusted). - Drop ambient privileges: run agents inside containers with read-only root filesystems, no
CAP_SYS_ADMIN, and secrets mounted through tmpfs rather than env vars.
A guardrail middleware pattern can add a second line of defense without touching every prompt:
from langchain.agents.middleware import after_agent, AgentState
from langchain.pydantic_v1 import BaseModel, ValidationError
import re, logging
class TemplateSafety(BaseModel):
safe: bool
reason: str | None = None
@after_agent
def template_safety_guard(ctx: AgentState) -> AgentState:
last_msg = ctx.messages[-1].content
if re.search(r'\{\{.*[_(].*\}\}', last_msg):
logging.warning("Template injection signature detected, refusing")
ctx.messages[-1].content = "Request blocked by security policy."
return ctx
# Register globally
from langgraph.runtime import Runtime
Runtime.register_middleware(template_safety_guard)
This intercepts any agent message that contains {{ followed by underscores or parentheses—strong indicators of attribute traversal—and replaces it with a safe refusal before the prompt reaches the LLM.
Longer-Term Hardening Patterns
Shift templates from runtime strings to compile-time constants stored in version-controlled YAML. Adopt the principle of least power: if you only need variable substitution, use Python’s built-in string.Template with the safe_substitute method instead of Jinja2. When dynamic logic is unavoidable, ship it as a signed, audited template file and verify its hash at startup.
Consider moving sensitive tool execution to a micro-service that holds the credentials and communicates with the agent over a tightly scoped gRPC interface. Even if template injection occurs, the attacker remains inside the prompt layer and cannot directly touch the key material.
Finally, add structured observability: emit a post-render prompt checksum and length metric. Sudden spikes or checksum drift across identical user queries can reveal ongoing exploit attempts and let you revoke sessions before damage propagates.
Key Takeaways
- CVE-2025-65106 is a code-execution vector hidden in plain template syntax; it affects every LangChain release through 0.3.79 and 1.0.6.
- Upgrade to 0.3.80/1.0.7 or later immediately, then audit your prompt paths for user-controlled injection surfaces.
- Combine patching with runtime guardrails, sandboxed containers, and template allow-lists to shrink the blast radius of future parser bugs.
For the official advisory and patch diff, see the NVD entry at https://nvd.nist.gov/vuln/detail/CVE-2025-65106.