deepset-ai / deepset-ai/haystack
FileSystemSkillStore advertises escaping symlinks as readable bundled files
@sjrl is already working on this.
Since Sep 20, 2026.
- Dominant language
- Python
- Stars
- 26.6k
- Forks
- 3.2k
- Avg merge
- 1d 3h
- Merged PRs (30d)
- 194
Description
FileSystemSkillStore._list_skill_files() includes symlinks whose resolved targets are outside the skill directory. However, read_skill_file() correctly rejects those same paths as traversal.
As a result, load_skill() gives the model a manifest containing a file it can never read. The model may select that advertised file, waste a tool call, and receive a PermissionError.
Reproduction
from pathlib import Path
from tempfile import TemporaryDirectory
from haystack.skill_stores.file_system.skill_store import FileSystemSkillStore
with TemporaryDirectory() as d:
root = Path(d)
skill = root / "demo"
skill.mkdir()
(skill / "SKILL.md").write_text(
"---\ndescription: demo\n---\nbody", encoding="utf-8"
)
secret = root / "outside.txt"
secret.write_text("outside", encoding="utf-8")
(skill / "outside-link.txt").symlink_to(secret)
store = FileSystemSkillStore(root)
print(store.load_skill("demo")[1])
store.read_skill_file("demo", "outside-link.txt")
Output:
['outside-link.txt']
PermissionError: Cannot read 'outside-link.txt' from skill 'demo': the path resolves outside the skill directory.
Expected
The bundled-file manifest should only contain paths that read_skill_file() can access within the skill root.
Proposed fix
Resolve each discovered path and exclude entries whose targets are outside the resolved skill directory. I have a small patch and regression test ready.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.