fix(core): handle symlinks in deprecated prompt save path (#36585)

Resolve symlinks before validating file extensions in the deprecated
`save()` method on prompt classes.

Credit to Jeff Ponte (@JDP-Security) for reporting the symlink
resolution issue.
This commit is contained in:
ccurme
2026-04-07 10:45:42 -04:00
committed by GitHub
parent ce21bf469d
commit 7629c74726
2 changed files with 21 additions and 4 deletions

View File

@@ -389,11 +389,12 @@ class BasePromptTemplate(
directory_path = save_path.parent
directory_path.mkdir(parents=True, exist_ok=True)
if save_path.suffix == ".json":
with save_path.open("w", encoding="utf-8") as f:
resolved_path = save_path.resolve()
if resolved_path.suffix == ".json":
with resolved_path.open("w", encoding="utf-8") as f:
json.dump(prompt_dict, f, indent=4)
elif save_path.suffix.endswith((".yaml", ".yml")):
with save_path.open("w", encoding="utf-8") as f:
elif resolved_path.suffix.endswith((".yaml", ".yml")):
with resolved_path.open("w", encoding="utf-8") as f:
yaml.dump(prompt_dict, f, default_flow_style=False)
else:
msg = f"{save_path} must be json or yaml"

View File

@@ -325,6 +325,22 @@ def test_symlink_jinja2_rce_is_blocked(tmp_path: Path) -> None:
load_prompt_from_config(config, allow_dangerous_paths=True)
def test_save_symlink_to_py_is_blocked(tmp_path: Path) -> None:
"""Test that save() resolves symlinks before checking the file extension."""
target = tmp_path / "malicious.py"
symlink = tmp_path / "output.json"
symlink.symlink_to(target)
prompt = PromptTemplate(input_variables=["name"], template="Hello {name}")
with (
suppress_langchain_deprecation_warning(),
pytest.raises(ValueError, match="must be json or yaml"),
):
prompt.save(symlink)
assert not target.exists()
def test_loading_few_shot_prompt_from_yaml() -> None:
"""Test loading few shot prompt from yaml."""
with change_directory(EXAMPLE_DIR), suppress_langchain_deprecation_warning():