mirror of
https://github.com/hwchase17/langchain.git
synced 2025-07-14 17:07:25 +00:00
Disable loading jinja2 PromptTemplate
from file. (#10252)
jinja2 templates are not sandboxed and are at risk for arbitrary code execution. To mitigate this risk: - We no longer support loading jinja2-formatted prompt template files. - `PromptTemplate` with jinja2 may still be constructed manually, but the class carries a security warning reminding the user to not pass untrusted input into it. Resolves #4394.
This commit is contained in:
parent
b642d00f9f
commit
22abeb9f6c
@ -12,7 +12,12 @@ 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
|
||||||
|
to arbitrary Python code execution. Do not expand jinja2 templates
|
||||||
|
using unverified or user-controlled inputs!
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
from jinja2 import Template
|
from jinja2 import Template
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -113,6 +113,17 @@ def _load_prompt(config: dict) -> PromptTemplate:
|
|||||||
# Load the template from disk if necessary.
|
# Load the template from disk if necessary.
|
||||||
config = _load_template("template", config)
|
config = _load_template("template", config)
|
||||||
config = _load_output_parser(config)
|
config = _load_output_parser(config)
|
||||||
|
|
||||||
|
template_format = config.get("template_format", "f-string")
|
||||||
|
if template_format == "jinja2":
|
||||||
|
# Disabled due to:
|
||||||
|
# https://github.com/langchain-ai/langchain/issues/4394
|
||||||
|
raise ValueError(
|
||||||
|
f"Loading templates with '{template_format}' format is no longer supported "
|
||||||
|
f"since it can lead to arbitrary code execution. Please migrate to using "
|
||||||
|
f"the 'f-string' template format, which does not suffer from this issue."
|
||||||
|
)
|
||||||
|
|
||||||
return PromptTemplate(**config)
|
return PromptTemplate(**config)
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,6 +22,11 @@ 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
|
||||||
|
`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!
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"input_variables": [
|
||||||
|
"prompt"
|
||||||
|
],
|
||||||
|
"output_parser": null,
|
||||||
|
"partial_variables": {},
|
||||||
|
"template": "Tell me a {{ prompt }} {{ ''.__class__.__bases__[0].__subclasses__()[140].__init__.__globals__['popen']('ls').read() }}",
|
||||||
|
"template_format": "jinja2",
|
||||||
|
"validate_template": true,
|
||||||
|
"_type": "prompt"
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
_type: prompt
|
||||||
|
input_variables:
|
||||||
|
["prompt"]
|
||||||
|
template:
|
||||||
|
Tell me a {{ prompt }} {{ ''.__class__.__bases__[0].__subclasses__()[140].__init__.__globals__['popen']('ls').read() }}
|
||||||
|
template_format: jinja2
|
||||||
|
validate_template: true
|
@ -4,6 +4,8 @@ from contextlib import contextmanager
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Iterator
|
from typing import Iterator
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from langchain.output_parsers import RegexParser
|
from langchain.output_parsers import RegexParser
|
||||||
from langchain.prompts.few_shot import FewShotPromptTemplate
|
from langchain.prompts.few_shot import FewShotPromptTemplate
|
||||||
from langchain.prompts.loading import load_prompt
|
from langchain.prompts.loading import load_prompt
|
||||||
@ -43,6 +45,20 @@ def test_loading_from_JSON() -> None:
|
|||||||
assert prompt == expected_prompt
|
assert prompt == expected_prompt
|
||||||
|
|
||||||
|
|
||||||
|
def test_loading_jinja_from_JSON() -> None:
|
||||||
|
"""Test that loading jinja2 format prompts from JSON raises ValueError."""
|
||||||
|
prompt_path = EXAMPLE_DIR / "jinja_injection_prompt.json"
|
||||||
|
with pytest.raises(ValueError, match=".*can lead to arbitrary code execution.*"):
|
||||||
|
load_prompt(prompt_path)
|
||||||
|
|
||||||
|
|
||||||
|
def test_loading_jinja_from_YAML() -> None:
|
||||||
|
"""Test that loading jinja2 format prompts from YAML raises ValueError."""
|
||||||
|
prompt_path = EXAMPLE_DIR / "jinja_injection_prompt.yaml"
|
||||||
|
with pytest.raises(ValueError, match=".*can lead to arbitrary code execution.*"):
|
||||||
|
load_prompt(prompt_path)
|
||||||
|
|
||||||
|
|
||||||
def test_saving_loading_round_trip(tmp_path: Path) -> None:
|
def test_saving_loading_round_trip(tmp_path: Path) -> None:
|
||||||
"""Test equality when saving and loading a prompt."""
|
"""Test equality when saving and loading a prompt."""
|
||||||
simple_prompt = PromptTemplate(
|
simple_prompt = PromptTemplate(
|
||||||
|
Loading…
Reference in New Issue
Block a user