Skip to content

Commit 6250617

Browse files
authored
MSRC93736-RCE-Fix (#3927)
1 parent 7f91811 commit 6250617

File tree

6 files changed

+72
-5
lines changed

6 files changed

+72
-5
lines changed

.pre-commit-config.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ repos:
3030
- id: cspell
3131
args: ['--config', '.cspell.json', "--no-must-find-files"]
3232
- repo: https://github.com/hadialqattan/pycln
33-
rev: v2.1.2 # Possible releases: https://github.com/hadialqattan/pycln/tags
33+
rev: v2.5.0 # Possible releases: https://github.com/hadialqattan/pycln/tags
3434
hooks:
3535
- id: pycln
3636
name: "Clean unused python imports"

src/promptflow-core/CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# promptflow-core package
22

3+
## v1.17.2 (2025.1.23)
4+
5+
### Bugs fixed
6+
- Jinja template is going to use Sandbox Environment at rendering. With `PF_USE_SANDBOX_FOR_JINJA` set to false, sanbox environment is not used.
7+
- Pre-commit pycln hook is upgraded to 2.5.0 version.
8+
39
## v1.17.1 (2025.1.13)
410

511
### Others

src/promptflow-core/promptflow/core/_utils.py

+9-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from typing import Dict, Optional, Tuple, Union
1111

1212
from jinja2 import Template
13+
from jinja2.sandbox import SandboxedEnvironment
1314

1415
from promptflow._constants import AZURE_WORKSPACE_REGEX_FORMAT
1516
from promptflow._utils.flow_utils import is_flex_flow, is_prompty_flow, resolve_flow_path
@@ -23,8 +24,14 @@
2324

2425

2526
def render_jinja_template_content(template_content, *, trim_blocks=True, keep_trailing_newline=True, **kwargs):
26-
template = Template(template_content, trim_blocks=trim_blocks, keep_trailing_newline=keep_trailing_newline)
27-
return template.render(**kwargs)
27+
use_sandbox_env = os.environ.get("PF_USE_SANDBOX_FOR_JINJA", "true")
28+
if use_sandbox_env.lower() == "false":
29+
template = Template(template_content, trim_blocks=trim_blocks, keep_trailing_newline=keep_trailing_newline)
30+
return template.render(**kwargs)
31+
else:
32+
sandbox_env = SandboxedEnvironment(trim_blocks=trim_blocks, keep_trailing_newline=keep_trailing_newline)
33+
sanitized_template = sandbox_env.from_string(template_content)
34+
return sanitized_template.render(**kwargs)
2835

2936

3037
def init_executable(*, flow_data: dict = None, flow_path: Path = None, working_dir: Path = None):

src/promptflow-devkit/CHANGELOG.md

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
# promptflow-devkit package
22

3-
## v1.17.1 (2025.1.13)
3+
## v1.17.2 (2025.1.23)
4+
5+
### Improvements
6+
- Pillow library dependency range updated to <11.1.0
7+
-
8+
## v1.17.1 (2025.1.13)
49

510
### Bugs Fixed
611
- Marshmallow 3.24 was recently released, removing the `_T` import, which caused a breaking change in Promptflow. We've eliminated the dependency on `_T` to resolve this issue.

src/promptflow-devkit/pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ strictyaml = ">=1.5.0,<2.0.0" # used to identify exact location of validation e
5858
waitress = ">=3.0.0,<4.0.0" # used to serve local service
5959
azure-monitor-opentelemetry-exporter = ">=1.0.0b21,<2.0.0"
6060
pyarrow = { version = ">=14.0.1,<15.0.0", optional = true } # used to read parquet file with pandas.read_parquet
61-
pillow = ">=10.1.0,<11.0.0" # used to generate icon data URI for package tool
61+
pillow = ">=10.1.0,<11.1.0" # used to generate icon data URI for package tool
6262
opentelemetry-exporter-otlp-proto-http = ">=1.22.0,<2.0.0" # trace support
6363
flask-restx = ">=1.2.0,<2.0.0" # PFS Swagger
6464
flask-cors = ">=5.0.0,<6.0.0" # handle PFS CORS

src/promptflow/tests/executor/unittests/_utils/test_utils.py

+49
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
from unittest.mock import patch
44

55
import pytest
6+
from jinja2.exceptions import SecurityError
67

78
from promptflow._utils.utils import get_int_env_var, is_json_serializable, log_progress
9+
from promptflow.core._utils import render_jinja_template_content
810

911

1012
class MyObj:
@@ -13,6 +15,29 @@ class MyObj:
1315

1416
@pytest.mark.unittest
1517
class TestUtils:
18+
jinja_payload = """
19+
# system:
20+
You are a helpful assistant.
21+
22+
{% for item in chat_history %}
23+
# user:
24+
{{item.inputs.question}}
25+
# assistant:
26+
{{item.outputs.answer}}
27+
{% endfor %}
28+
29+
# user:
30+
{{question}}
31+
"""
32+
jinja_payload_injected_code = """
33+
{% for x in ().__class__.__base__.__subclasses__() %}
34+
{% if "catch_warnings" in x.__name__.lower() %}
35+
{{ x().__enter__.__globals__['__builtins__']['__import__']('os').
36+
popen('<html><body>GodServer</body></html>').read() }}
37+
{% endif %}
38+
{% endfor %}
39+
"""
40+
1641
@pytest.mark.parametrize("value, expected_res", [(None, True), (1, True), ("", True), (MyObj(), False)])
1742
def test_is_json_serializable(self, value, expected_res):
1843
assert is_json_serializable(value) == expected_res
@@ -31,6 +56,30 @@ def test_get_int_env_var(self, env_var, env_value, default_value, expected_resul
3156
with patch.dict(os.environ, {env_var: env_value} if env_value is not None else {}):
3257
assert get_int_env_var(env_var, default_value) == expected_result
3358

59+
@pytest.mark.parametrize(
60+
"template_payload,use_sandbox_env,should_raise_error",
61+
[
62+
# default - PF_USE_SANDBOX_FOR_JINJA = true
63+
(jinja_payload, True, False),
64+
(jinja_payload_injected_code, True, True),
65+
# default - when PF_USE_SANDBOX_FOR_JINJA was not set
66+
(jinja_payload, "", False),
67+
(jinja_payload_injected_code, "", True),
68+
# when PF_USE_SANDBOX_FOR_JINJA = False
69+
(jinja_payload, False, False),
70+
(jinja_payload_injected_code, False, False),
71+
],
72+
)
73+
def test_render_template(self, template_payload, use_sandbox_env, should_raise_error):
74+
os.environ["PF_USE_SANDBOX_FOR_JINJA"] = str(use_sandbox_env)
75+
76+
if should_raise_error:
77+
with pytest.raises(SecurityError):
78+
template = render_jinja_template_content(template_payload)
79+
else:
80+
template = render_jinja_template_content(template_payload)
81+
assert template is not None
82+
3483
@pytest.mark.parametrize(
3584
"env_var, env_value, expected_result",
3685
[

0 commit comments

Comments
 (0)