Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions api/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,11 @@ def iterate_files(

results: list[str] = []
for p in Path(root_dir).rglob("*"):
if p.is_symlink():
# A repository-controlled symlink can point outside the clone;
# following it would read/embed a host file the repository
# author does not own. Skip it rather than resolve+open it.
continue
if not p.is_file():
continue
if extensions and p.suffix.lower() not in extensions:
Expand Down
25 changes: 25 additions & 0 deletions tests/test_config.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import os

import pytest

from api.config import iterate_files
Expand Down Expand Up @@ -60,3 +62,26 @@ def test_iterate_files_excluded_dirs(exclude_test_config, tmp_path, excluded_dir
make_repo(tmp_path)
files = set(iterate_files(root_dir=str(tmp_path), excluded_dirs=excluded_dirs))
assert files == {"README.md", "CHANGELOG.md"}


def test_iterate_files_does_not_follow_symlink_out_of_repository(
exclude_test_config, tmp_path
):
"""A repository-controlled symlink pointing outside the clone must not
be listed for indexing. Regression for the symlink-escape bug: the
walker used to call is_file() (which follows the link) with no
is_symlink() guard, so the indexer would open and embed the external
target's content."""
make_repo(tmp_path)

outside_dir = tmp_path.parent / "outside_the_clone"
outside_dir.mkdir(exist_ok=True)
outside_secret = outside_dir / "secret.py"
outside_secret.write_text("DB_PASSWORD = 'outside-secret'")

alias = tmp_path / "innocuous_link.py"
os.symlink(outside_secret, alias)

files = set(iterate_files(root_dir=str(tmp_path)))
assert "innocuous_link.py" not in files
assert files == {"README.md", "CHANGELOG.md", "folder/code.py"}