mirror of
https://github.com/hwchase17/langchain.git
synced 2025-06-25 16:13:25 +00:00
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.
This commit is contained in:
parent
ab5309f6f2
commit
0e1aedb9f4
@ -15,19 +15,33 @@ from langchain.utils.formatting import formatter
|
|||||||
def jinja2_formatter(template: str, **kwargs: Any) -> str:
|
def jinja2_formatter(template: str, **kwargs: Any) -> str:
|
||||||
"""Format a template using jinja2.
|
"""Format a template using jinja2.
|
||||||
|
|
||||||
*Security warning*: jinja2 templates are not sandboxed and may lead
|
*Security warning*: As of LangChain 0.0.329, this method uses Jinja2's
|
||||||
to arbitrary Python code execution. Do not expand jinja2 templates
|
SandboxedEnvironment by default. However, this sand-boxing should
|
||||||
using unverified or user-controlled inputs!
|
be treated as a best-effort approach rather than a guarantee of security.
|
||||||
|
Do not accept jinja2 templates from untrusted sources as they may lead
|
||||||
|
to arbitrary Python code execution.
|
||||||
|
|
||||||
|
https://jinja.palletsprojects.com/en/3.1.x/sandbox/
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
from jinja2 import Template
|
from jinja2.sandbox import SandboxedEnvironment
|
||||||
except ImportError:
|
except ImportError:
|
||||||
raise ImportError(
|
raise ImportError(
|
||||||
"jinja2 not installed, which is needed to use the jinja2_formatter. "
|
"jinja2 not installed, which is needed to use the jinja2_formatter. "
|
||||||
"Please install it with `pip install jinja2`."
|
"Please install it with `pip install jinja2`."
|
||||||
|
"Please be cautious when using jinja2 templates. "
|
||||||
|
"Do not expand jinja2 templates using unverified or user-controlled "
|
||||||
|
"inputs as that can result in arbitrary Python code execution."
|
||||||
)
|
)
|
||||||
|
|
||||||
return Template(template).render(**kwargs)
|
# This uses a sandboxed environment to prevent arbitrary code execution.
|
||||||
|
# Jinja2 uses an opt-out rather than opt-in approach for sand-boxing.
|
||||||
|
# Please treat this sand-boxing as a best-effort approach rather than
|
||||||
|
# a guarantee of security.
|
||||||
|
# We recommend to never use jinja2 templates with untrusted inputs.
|
||||||
|
# https://jinja.palletsprojects.com/en/3.1.x/sandbox/
|
||||||
|
# approach not a guarantee of security.
|
||||||
|
return SandboxedEnvironment().from_string(template).render(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
def validate_jinja2(template: str, input_variables: List[str]) -> None:
|
def validate_jinja2(template: str, input_variables: List[str]) -> None:
|
||||||
|
@ -22,9 +22,16 @@ class PromptTemplate(StringPromptTemplate):
|
|||||||
The template can be formatted using either f-strings (default) or jinja2 syntax.
|
The template can be formatted using either f-strings (default) or jinja2 syntax.
|
||||||
|
|
||||||
*Security warning*: Prefer using `template_format="f-string"` instead of
|
*Security warning*: Prefer using `template_format="f-string"` instead of
|
||||||
`template_format="jinja2"`, since jinja2 templates are not sandboxed and may
|
`template_format="jinja2"`, or make sure to NEVER accept jinja2 templates
|
||||||
lead to arbitrary Python code execution. Do not construct a jinja2 `PromptTemplate`
|
from untrusted sources as they may lead to arbitrary Python code execution.
|
||||||
from unverified or user-controlled inputs!
|
|
||||||
|
As of LangChain 0.0.329, Jinja2 templates will be rendered using
|
||||||
|
Jinja2's SandboxedEnvironment by default. This sand-boxing should
|
||||||
|
be treated as a best-effort approach rather than a guarantee of security,
|
||||||
|
as it is an opt-out rather than opt-in approach.
|
||||||
|
|
||||||
|
Despite the sand-boxing, we recommend to never use jinja2 templates
|
||||||
|
from untrusted sources.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@ -196,6 +203,18 @@ class PromptTemplate(StringPromptTemplate):
|
|||||||
) -> PromptTemplate:
|
) -> PromptTemplate:
|
||||||
"""Load a prompt template from a template.
|
"""Load a prompt template from a template.
|
||||||
|
|
||||||
|
*Security warning*: Prefer using `template_format="f-string"` instead of
|
||||||
|
`template_format="jinja2"`, or make sure to NEVER accept jinja2 templates
|
||||||
|
from untrusted sources as they may lead to arbitrary Python code execution.
|
||||||
|
|
||||||
|
As of LangChain 0.0.329, Jinja2 templates will be rendered using
|
||||||
|
Jinja2's SandboxedEnvironment by default. This sand-boxing should
|
||||||
|
be treated as a best-effort approach rather than a guarantee of security,
|
||||||
|
as it is an opt-out rather than opt-in approach.
|
||||||
|
|
||||||
|
Despite the sand-boxing, we recommend to never use jinja2 templates
|
||||||
|
from untrusted sources.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
template: The template to load.
|
template: The template to load.
|
||||||
template_format: The format of the template. Use `jinja2` for jinja2,
|
template_format: The format of the template. Use `jinja2` for jinja2,
|
||||||
|
@ -177,6 +177,17 @@ Will it get confused{ }?
|
|||||||
assert prompt == expected_prompt
|
assert prompt == expected_prompt
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.requires("jinja2")
|
||||||
|
def test_basic_sandboxing_with_jinja2() -> None:
|
||||||
|
"""Test basic sandboxing with jinja2."""
|
||||||
|
import jinja2
|
||||||
|
|
||||||
|
template = " {{''.__class__.__bases__[0] }} " # malicious code
|
||||||
|
prompt = PromptTemplate.from_template(template, template_format="jinja2")
|
||||||
|
with pytest.raises(jinja2.exceptions.SecurityError):
|
||||||
|
assert prompt.format() == []
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.requires("jinja2")
|
@pytest.mark.requires("jinja2")
|
||||||
def test_prompt_from_jinja2_template_multiple_inputs() -> None:
|
def test_prompt_from_jinja2_template_multiple_inputs() -> None:
|
||||||
"""Test with multiple input variables."""
|
"""Test with multiple input variables."""
|
||||||
|
Loading…
Reference in New Issue
Block a user