From aa3a13b6655d6d8b95a8b1d15a4decf112d60039 Mon Sep 17 00:00:00 2001 From: Harshit Sharma <66710144+harshitethic@users.noreply.github.com> Date: Thu, 5 Nov 2026 06:07:00 +0530 Subject: [PATCH 1/2] Prune ignored directories before traversing repository source files --- backend/services/github.py | 21 ++++++++++--------- backend/tests/test_source_traversal.py | 28 ++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 10 deletions(-) create mode 100644 backend/tests/test_source_traversal.py diff --git a/backend/services/github.py b/backend/services/github.py index e544c3d..ec8d428 100644 --- a/backend/services/github.py +++ b/backend/services/github.py @@ -423,16 +423,17 @@ def _extract_zip_locked(upload_path: Path): def iter_files(root: Path): # Never follow a repository-controlled symbolic link outside its tree. trusted_root = root.resolve() - for p in root.rglob("*"): - if p.is_symlink() or not p.is_file(): continue - if trusted_root not in p.resolve().parents: continue - rel = p.relative_to(root) - if any(part.casefold() in SKIP_DIRS for part in rel.parts[:-1]): continue - try: size = p.stat().st_size - except OSError: continue - if size > 300_000: continue - if p.suffix.lower() in TEXT_EXTENSIONS or p.name.lower().endswith(".env.example") or p.name.lower() in {"dockerfile","makefile",".gitignore"}: - yield p + for directory, subdirs, files in os.walk(root, followlinks=False): + subdirs[:] = [name for name in subdirs if name.casefold() not in SKIP_DIRS] + for name in files: + p = Path(directory) / name + if p.is_symlink() or not p.is_file(): continue + if trusted_root not in p.resolve().parents: continue + try: size = p.stat().st_size + except OSError: continue + if size > 300_000: continue + if p.suffix.lower() in TEXT_EXTENSIONS or p.name.lower().endswith(".env.example") or p.name.lower() in {"dockerfile","makefile",".gitignore"}: + yield p def read_text(path: Path): return path.read_text(encoding="utf-8", errors="replace") diff --git a/backend/tests/test_source_traversal.py b/backend/tests/test_source_traversal.py new file mode 100644 index 0000000..4b4c5a6 --- /dev/null +++ b/backend/tests/test_source_traversal.py @@ -0,0 +1,28 @@ +import os +import tempfile +import unittest +from pathlib import Path +from unittest.mock import patch + +from services.github import iter_files + + +class SourceTraversalTests(unittest.TestCase): + def test_walk_skips_ignored_subtrees_before_opening_them(self): + with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + ignored = root / "src" / "Node_Modules" + ignored.mkdir(parents=True) + (ignored / "vendor.js").touch() + source = root / "src" / "app.js" + source.touch() + scanned = [] + scandir = os.scandir + + def record_scan(path): + scanned.append(Path(path)) + return scandir(path) + + with patch("os.scandir", side_effect=record_scan): + self.assertEqual(list(iter_files(root)), [source]) + self.assertNotIn(ignored, scanned) From 0e164cc96b9426fbcfe88632b47b1781bd8d953d Mon Sep 17 00:00:00 2001 From: Harshit Sharma <66710144+harshitethic@users.noreply.github.com> Date: Thu, 5 Nov 2026 06:07:32 +0530 Subject: [PATCH 2/2] Verify excluded files receive no source-discovery probes across Python versions --- backend/tests/test_source_traversal.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/backend/tests/test_source_traversal.py b/backend/tests/test_source_traversal.py index 4b4c5a6..4c3a724 100644 --- a/backend/tests/test_source_traversal.py +++ b/backend/tests/test_source_traversal.py @@ -1,4 +1,3 @@ -import os import tempfile import unittest from pathlib import Path @@ -8,21 +7,22 @@ class SourceTraversalTests(unittest.TestCase): - def test_walk_skips_ignored_subtrees_before_opening_them(self): + def test_walk_skips_file_checks_in_ignored_subtrees(self): with tempfile.TemporaryDirectory() as directory: root = Path(directory) ignored = root / "src" / "Node_Modules" ignored.mkdir(parents=True) - (ignored / "vendor.js").touch() + dependency = ignored / "vendor.js" + dependency.touch() source = root / "src" / "app.js" source.touch() - scanned = [] - scandir = os.scandir + inspected = [] + is_file = Path.is_file - def record_scan(path): - scanned.append(Path(path)) - return scandir(path) + def track_file_check(path): + inspected.append(path) + return is_file(path) - with patch("os.scandir", side_effect=record_scan): + with patch.object(Path, "is_file", track_file_check): self.assertEqual(list(iter_files(root)), [source]) - self.assertNotIn(ignored, scanned) + self.assertNotIn(dependency, inspected)