From f34df17aeeb782cd76e09848f07971b2dcc56ca9 Mon Sep 17 00:00:00 2001 From: Harshit Sharma <66710144+harshitethic@users.noreply.github.com> Date: Sat, 24 Oct 2026 04:08:15 -0400 Subject: [PATCH 1/5] fix(demo): refuse to overwrite user-selected output and symlinks --- examples/create_demo_zip.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/examples/create_demo_zip.py b/examples/create_demo_zip.py index d2a8b59..b779da0 100644 --- a/examples/create_demo_zip.py +++ b/examples/create_demo_zip.py @@ -13,13 +13,19 @@ def create_demo_zip(destination: Path) -> Path: destination = Path(destination) destination.parent.mkdir(parents=True, exist_ok=True) - with zipfile.ZipFile(destination, "w", compression=zipfile.ZIP_DEFLATED) as archive: - for path, contents in DEMO_FILES.items(): - archive.writestr(path, contents) + # Exclusive creation refuses existing files and symlinks; a first-run demo + # should never replace another file at a caller-selected output path. + with destination.open("xb") as output: + with zipfile.ZipFile(output, "w", compression=zipfile.ZIP_DEFLATED) as archive: + for path, contents in DEMO_FILES.items(): + archive.writestr(path, contents) return destination if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--output", required=True, type=Path) args = parser.parse_args() - print(create_demo_zip(args.output)) + try: + print(create_demo_zip(args.output)) + except FileExistsError: + parser.error(f"Output already exists: {args.output}. Choose another --output path.") From aacdc0f915e5fbc8b347406fee3a08785d921ea0 Mon Sep 17 00:00:00 2001 From: Harshit Sharma <66710144+harshitethic@users.noreply.github.com> Date: Sat, 24 Oct 2026 04:08:29 -0400 Subject: [PATCH 2/5] test(demo): cover existing file and symlink preservation --- backend/tests/test_public_demo_zip.py | 33 +++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/backend/tests/test_public_demo_zip.py b/backend/tests/test_public_demo_zip.py index 5f1178d..a8c0632 100644 --- a/backend/tests/test_public_demo_zip.py +++ b/backend/tests/test_public_demo_zip.py @@ -44,6 +44,39 @@ def test_generator_is_reproducible_and_contains_searchable_source(self): all(not p.startswith("/") and ".." not in Path(p).parts for p in paths) ) + def test_generator_does_not_overwrite_existing_output(self): + with tempfile.TemporaryDirectory() as directory: + destination = Path(directory) / "existing.zip" + sentinel = b"EXISTING_DOCUMENT_DO_NOT_TOUCH" + destination.write_bytes(sentinel) + result = subprocess.run( + [sys.executable, str(GENERATOR), "--output", str(destination)], + cwd=ROOT, capture_output=True, text=True, timeout=20 + ) + self.assertNotEqual(result.returncode, 0) + self.assertIn("already exists", result.stderr.lower()) + self.assertEqual(destination.read_bytes(), sentinel) + + + @unittest.skipIf(sys.platform == "win32", "Windows symlink setup needs special privileges") + def test_generator_does_not_follow_existing_symlink(self): + with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + target = root / "important.txt" + sentinel = b"DO_NOT_REPLACE_SYMLINK_TARGET" + target.write_bytes(sentinel) + link = root / "fictional-catalog.zip" + link.symlink_to(target) + result = subprocess.run( + [sys.executable, str(GENERATOR), "--output", str(link)], + cwd=ROOT, capture_output=True, text=True, timeout=20 + ) + self.assertNotEqual(result.returncode, 0) + self.assertIn("already exists", result.stderr.lower()) + self.assertTrue(link.is_symlink()) + self.assertEqual(target.read_bytes(), sentinel) + + class PublishedDemoHttpTests(unittest.IsolatedAsyncioTestCase): async def test_published_zip_imports_searches_and_deletes_without_ollama(self): From 1d157f4f31e62581295aa2d9d1ce802719ecefaa Mon Sep 17 00:00:00 2001 From: Harshit Sharma <66710144+harshitethic@users.noreply.github.com> Date: Sat, 24 Oct 2026 04:08:32 -0400 Subject: [PATCH 3/5] docs(first-run): explain exclusive demo output behavior --- docs/first-run.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/first-run.md b/docs/first-run.md index df268e9..74c9e98 100644 --- a/docs/first-run.md +++ b/docs/first-run.md @@ -79,6 +79,8 @@ Import **fictional-catalog.zip** through the UI, then search for **find_title**. The search result should show **src/catalog.py**, line **2**. These are real source lines from a fictional fixture, **not** an AI model answer. +The generator refuses to overwrite an existing output path, including a symlink. If you run the command again, choose a different filename or manually remove only your disposable sample ZIP first. + CI checks this exact public ZIP generator and its real FastAPI ZIP-import/search/ deletion journey, without starting Ollama: From 92f68a00fc1d13b074011dddd0680b0e58a28c2f Mon Sep 17 00:00:00 2001 From: Harshit Sharma <66710144+harshitethic@users.noreply.github.com> Date: Sat, 24 Oct 2026 04:11:48 -0400 Subject: [PATCH 4/5] fix(demo): pin ZIP member timestamp and file mode for reproducible archives --- examples/create_demo_zip.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/examples/create_demo_zip.py b/examples/create_demo_zip.py index b779da0..9bf8060 100644 --- a/examples/create_demo_zip.py +++ b/examples/create_demo_zip.py @@ -18,7 +18,13 @@ def create_demo_zip(destination: Path) -> Path: with destination.open("xb") as output: with zipfile.ZipFile(output, "w", compression=zipfile.ZIP_DEFLATED) as archive: for path, contents in DEMO_FILES.items(): - archive.writestr(path, contents) + # An explicit DOS timestamp and file mode keep ZIP members + # reproducible across seconds and independent of host OS. + member = zipfile.ZipInfo(path, date_time=(1980, 1, 1, 0, 0, 0)) + member.compress_type = zipfile.ZIP_DEFLATED + member.create_system = 3 + member.external_attr = 0o100644 << 16 + archive.writestr(member, contents) return destination if __name__ == "__main__": From 10e3e52fab164288aea2e1275caf0d440f8e5922 Mon Sep 17 00:00:00 2001 From: Harshit Sharma <66710144+harshitethic@users.noreply.github.com> Date: Sat, 24 Oct 2026 04:12:04 -0400 Subject: [PATCH 5/5] test(demo): assert fixed ZIP metadata to avoid timing-dependent failures --- backend/tests/test_public_demo_zip.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/backend/tests/test_public_demo_zip.py b/backend/tests/test_public_demo_zip.py index a8c0632..a176699 100644 --- a/backend/tests/test_public_demo_zip.py +++ b/backend/tests/test_public_demo_zip.py @@ -40,6 +40,12 @@ def test_generator_is_reproducible_and_contains_searchable_source(self): self.assertIn("src/catalog.py", paths) self.assertIn("src/api.py", paths) self.assertIn("find_title", archive.read("src/catalog.py").decode("utf-8")) + self.assertTrue(all( + member.date_time == (1980, 1, 1, 0, 0, 0) + and member.create_system == 3 + and member.external_attr >> 16 == 0o100644 + for member in archive.infolist() + )) self.assertTrue( all(not p.startswith("/") and ".." not in Path(p).parts for p in paths) )