GHSA-556j-vv39-8rqv: Path Traversal
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: 1. self.indexpath (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(). 2. In scan(), discovered .jinja files are opened and indexed without checking if path.issymlink() or if the resolved path escapes the registry root. A symlink pointing to a sensitive file outside the root is read and indexed. 3. In set(), promptfile.writetext(...) is called without checking if promptfile 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 python import os from pathlib import Path from banks.registries.directory import DirectoryPromptRegistry, DEFAULTINDEXNAME from banks.prompt import Prompt
Disclose external file via symlink in prompt directory regdir = Path("/tmp/registry") regdir.mkdir(existok=True) secret = Path("/tmp/secret.txt") secret.writetext("SECRETAPITOKEN")
os.symlink(secret, regdir / "leak.0.jinja") reg = DirectoryPromptRegistry(regdir, forcereindex=True) print("Disclosed content:", reg.get(name="leak", version="0").raw)
Overwrite external file via symlink index target = Path("/tmp/target.conf") target.writetext("ORIGINAL") (regdir / DEFAULTINDEXNAME).unlink(missingok=True) os.symlink(target, regdir / DEFAULTINDEXNAME) reg.set(prompt=Prompt("pwn", name="test", version="1")) print("Target overwritten:", target.readtext())
Remediation 1. In validateindexpath(): verify indexpath is not a symlink and resolves within path. 2. In scan(): reject path.issymlink() and check path.resolve().isrelativeto(root). 3. 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
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
pip/banksto a version that resolves this vulnerability.Fixed in 2.5.1 - Compensating control
In DirectoryPromptRegistry._scan(), reject discovered .jinja paths that are symbolic links and reject paths whose resolved location is outside the registry root by checking path.is_symlink() and path.resolve().is_relative_to(root).
- Compensating control
In DirectoryPromptRegistry._validate_index_path(), verify that _index_path is not a symbolic link and that its resolved path remains within _path.
- Compensating control
In DirectoryPromptRegistry.set(), reject an existing symbolic link at prompt_file before calling prompt_file.write_text(...), preventing writes through links outside the registry root.
Event History
Frequently Asked Questions
Which deployments are most exposed to this issue?
Deployments are exposed when the prompt directory contains or accepts untrusted files, such as unpacked archives, shared repositories, or multi-tenant folders. An attacker needs the ability to place or control symbolic links in that directory.
What can an attacker do with a malicious symbolic link?
A symbolic link named as a discovered .jinja prompt can cause files outside the registry root to be read and indexed through _scan() or get(). An existing symbolic-link prompt target can also be overwritten when set() writes prompt content.
How can I check whether a registry may already be affected?
Inspect index.json and discovered .jinja files in the prompt directory for symbolic links. Treat links whose resolved targets are outside the registry root as unsafe, especially if index.json is linked to an external configuration or other sensitive file.