mirror of
https://github.com/hwchase17/langchain.git
synced 2025-06-24 15:43:54 +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:
|
||||
"""Format a template using jinja2.
|
||||
|
||||
*Security warning*: jinja2 templates are not sandboxed and may lead
|
||||
to arbitrary Python code execution. Do not expand jinja2 templates
|
||||
using unverified or user-controlled inputs!
|
||||
*Security warning*: As of LangChain 0.0.329, this method uses Jinja2's
|
||||
SandboxedEnvironment by default. However, this sand-boxing should
|
||||
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:
|
||||
from jinja2 import Template
|
||||
from jinja2.sandbox import SandboxedEnvironment
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"jinja2 not installed, which is needed to use the jinja2_formatter. "
|
||||
"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:
|
||||
|
@ -22,9 +22,16 @@ class PromptTemplate(StringPromptTemplate):
|
||||
The template can be formatted using either f-strings (default) or jinja2 syntax.
|
||||
|
||||
*Security warning*: Prefer using `template_format="f-string"` instead of
|
||||
`template_format="jinja2"`, since jinja2 templates are not sandboxed and may
|
||||
lead to arbitrary Python code execution. Do not construct a jinja2 `PromptTemplate`
|
||||
from unverified or user-controlled inputs!
|
||||
`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.
|
||||
|
||||
Example:
|
||||
|
||||
@ -196,6 +203,18 @@ class PromptTemplate(StringPromptTemplate):
|
||||
) -> PromptTemplate:
|
||||
"""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:
|
||||
template: The template to load.
|
||||
template_format: The format of the template. Use `jinja2` for jinja2,
|
||||
|
@ -177,6 +177,17 @@ Will it get confused{ }?
|
||||
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")
|
||||
def test_prompt_from_jinja2_template_multiple_inputs() -> None:
|
||||
"""Test with multiple input variables."""
|
||||
|
Loading…
Reference in New Issue
Block a user