Skip to content

Commit 0e1aedb

Browse files
authored
Use jinja2 sandboxing by default (#12733)
* This is an opt-in feature, so users should be aware of risks if using jinja2. * Regardless we'll add sandboxing by default to jinja2 templates -- this sandboxing is a best effort basis. * Best strategy is still to make sure that jinja2 templates are only loaded from trusted sources.
1 parent ab5309f commit 0e1aedb

File tree

3 files changed

+52
-8
lines changed

3 files changed

+52
-8
lines changed

libs/langchain/langchain/prompts/base.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,33 @@
1515
def jinja2_formatter(template: str, **kwargs: Any) -> str:
1616
"""Format a template using jinja2.
1717
18-
*Security warning*: jinja2 templates are not sandboxed and may lead
19-
to arbitrary Python code execution. Do not expand jinja2 templates
20-
using unverified or user-controlled inputs!
18+
*Security warning*: As of LangChain 0.0.329, this method uses Jinja2's
19+
SandboxedEnvironment by default. However, this sand-boxing should
20+
be treated as a best-effort approach rather than a guarantee of security.
21+
Do not accept jinja2 templates from untrusted sources as they may lead
22+
to arbitrary Python code execution.
23+
24+
https://jinja.palletsprojects.com/en/3.1.x/sandbox/
2125
"""
2226
try:
23-
from jinja2 import Template
27+
from jinja2.sandbox import SandboxedEnvironment
2428
except ImportError:
2529
raise ImportError(
2630
"jinja2 not installed, which is needed to use the jinja2_formatter. "
2731
"Please install it with `pip install jinja2`."
32+
"Please be cautious when using jinja2 templates. "
33+
"Do not expand jinja2 templates using unverified or user-controlled "
34+
"inputs as that can result in arbitrary Python code execution."
2835
)
2936

30-
return Template(template).render(**kwargs)
37+
# This uses a sandboxed environment to prevent arbitrary code execution.
38+
# Jinja2 uses an opt-out rather than opt-in approach for sand-boxing.
39+
# Please treat this sand-boxing as a best-effort approach rather than
40+
# a guarantee of security.
41+
# We recommend to never use jinja2 templates with untrusted inputs.
42+
# https://jinja.palletsprojects.com/en/3.1.x/sandbox/
43+
# approach not a guarantee of security.
44+
return SandboxedEnvironment().from_string(template).render(**kwargs)
3145

3246

3347
def validate_jinja2(template: str, input_variables: List[str]) -> None:

libs/langchain/langchain/prompts/prompt.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,16 @@ class PromptTemplate(StringPromptTemplate):
2222
The template can be formatted using either f-strings (default) or jinja2 syntax.
2323
2424
*Security warning*: Prefer using `template_format="f-string"` instead of
25-
`template_format="jinja2"`, since jinja2 templates are not sandboxed and may
26-
lead to arbitrary Python code execution. Do not construct a jinja2 `PromptTemplate`
27-
from unverified or user-controlled inputs!
25+
`template_format="jinja2"`, or make sure to NEVER accept jinja2 templates
26+
from untrusted sources as they may lead to arbitrary Python code execution.
27+
28+
As of LangChain 0.0.329, Jinja2 templates will be rendered using
29+
Jinja2's SandboxedEnvironment by default. This sand-boxing should
30+
be treated as a best-effort approach rather than a guarantee of security,
31+
as it is an opt-out rather than opt-in approach.
32+
33+
Despite the sand-boxing, we recommend to never use jinja2 templates
34+
from untrusted sources.
2835
2936
Example:
3037
@@ -196,6 +203,18 @@ def from_template(
196203
) -> PromptTemplate:
197204
"""Load a prompt template from a template.
198205
206+
*Security warning*: Prefer using `template_format="f-string"` instead of
207+
`template_format="jinja2"`, or make sure to NEVER accept jinja2 templates
208+
from untrusted sources as they may lead to arbitrary Python code execution.
209+
210+
As of LangChain 0.0.329, Jinja2 templates will be rendered using
211+
Jinja2's SandboxedEnvironment by default. This sand-boxing should
212+
be treated as a best-effort approach rather than a guarantee of security,
213+
as it is an opt-out rather than opt-in approach.
214+
215+
Despite the sand-boxing, we recommend to never use jinja2 templates
216+
from untrusted sources.
217+
199218
Args:
200219
template: The template to load.
201220
template_format: The format of the template. Use `jinja2` for jinja2,

libs/langchain/tests/unit_tests/prompts/test_prompt.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,17 @@ def test_prompt_from_jinja2_template() -> None:
177177
assert prompt == expected_prompt
178178

179179

180+
@pytest.mark.requires("jinja2")
181+
def test_basic_sandboxing_with_jinja2() -> None:
182+
"""Test basic sandboxing with jinja2."""
183+
import jinja2
184+
185+
template = " {{''.__class__.__bases__[0] }} " # malicious code
186+
prompt = PromptTemplate.from_template(template, template_format="jinja2")
187+
with pytest.raises(jinja2.exceptions.SecurityError):
188+
assert prompt.format() == []
189+
190+
180191
@pytest.mark.requires("jinja2")
181192
def test_prompt_from_jinja2_template_multiple_inputs() -> None:
182193
"""Test with multiple input variables."""

0 commit comments

Comments
 (0)