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
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ jobs:
- name: Compile backend
run: python -m compileall -q backend

- name: Run backend unit tests
working-directory: backend
run: python -m unittest discover -s tests -v

frontend:
runs-on: ubuntu-latest
steps:
Expand Down
20 changes: 17 additions & 3 deletions backend/services/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,16 +73,30 @@ def extract_zip(upload_path: Path):
raise
return repo_id, dest

def _resolve_repo_file(root: Path, candidate: Path):
root = root.resolve()
try:
resolved = candidate.resolve()
resolved.relative_to(root)
except (OSError, ValueError):
return None
if not resolved.is_file():
return None
return resolved


def iter_files(root: Path):
root = root.resolve()
for p in root.rglob("*"):
if not p.is_file(): continue
rel = p.relative_to(root)
if any(part in SKIP_DIRS for part in rel.parts): continue
try: size = p.stat().st_size
resolved = _resolve_repo_file(root, p)
if resolved is None: continue
try: size = resolved.stat().st_size
except OSError: continue
if size > 300_000: continue
if p.suffix.lower() in TEXT_EXTENSIONS or p.name.lower() in {"dockerfile","makefile"}:
yield p
yield resolved

def read_text(path: Path): return path.read_text(encoding="utf-8", errors="replace")

Expand Down
39 changes: 39 additions & 0 deletions backend/tests/test_repository_boundary.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import tempfile
import unittest
from pathlib import Path

from services.github import iter_files


class RepositoryFileBoundaryTests(unittest.TestCase):
def test_external_symlink_is_not_indexed(self):
with tempfile.TemporaryDirectory() as temp_dir:
root = Path(temp_dir)
repo = root / "repo"
repo.mkdir()
outside = root / "outside.py"
outside.write_text("SECRET = True\n", encoding="utf-8")
link = repo / "linked.py"
try:
link.symlink_to(outside)
except (OSError, NotImplementedError):
self.skipTest("symlinks are unavailable on this platform")

indexed = list(iter_files(repo))

self.assertEqual(indexed, [])

def test_internal_file_is_still_indexed(self):
with tempfile.TemporaryDirectory() as temp_dir:
repo = Path(temp_dir)
source = repo / "src" / "main.py"
source.parent.mkdir()
source.write_text("print('ok')\n", encoding="utf-8")

indexed = list(iter_files(repo))

self.assertEqual(indexed, [source.resolve()])


if __name__ == "__main__":
unittest.main()
Loading