Docs: respect path boundary when clearing exclusions

add_directory cleared exclusions with a raw path.startswith(directory)
test, which also matched sibling directories sharing a name prefix —
adding /docs would silently un-exclude files under /docs2. Match the
directory itself or paths under it (directory + os.sep) instead.
This commit is contained in:
SurprisedDuck
2026-06-02 13:35:44 +02:00
committed by GitHub
parent 78747b56ca
commit 62f06ab740
2 changed files with 60 additions and 2 deletions

View File

@@ -246,8 +246,15 @@ class PersonalDocsManager:
# Normalize the path # Normalize the path
directory = os.path.abspath(directory) directory = os.path.abspath(directory)
# Clear any exclusions for files in this directory # Clear any exclusions for files in this directory. Match on a path
self.excluded_files = {p for p in self.excluded_files if not p.startswith(directory)} # boundary (the directory itself or paths under it) rather than a raw
# string prefix: a bare ``startswith(directory)`` also matches sibling
# directories that merely share a name prefix (e.g. adding ``/docs``
# would wrongly un-exclude files under ``/docs2``).
self.excluded_files = {
p for p in self.excluded_files
if not (p == directory or p.startswith(directory + os.sep))
}
self._save_excluded() self._save_excluded()
if directory not in self.indexed_directories: if directory not in self.indexed_directories:

View File

@@ -0,0 +1,51 @@
"""Regression: add_directory must not un-exclude files in sibling directories.
``add_directory`` clears exclusions for files inside the directory being added.
It previously used a raw ``path.startswith(directory)`` test, which also matched
sibling directories sharing a name prefix — so adding ``/docs`` would silently
drop exclusions for files under ``/docs2``. The match must respect a path
boundary.
"""
import os
from src import personal_docs
def _make_manager(tmp_path):
mgr = personal_docs.PersonalDocsManager(str(tmp_path))
# Pre-seed the directory as already tracked so add_directory takes the
# cheap "already indexed" branch (no indexing / refresh side effects); the
# exclusion-clearing logic under test runs unconditionally before that.
return mgr
def test_sibling_directory_exclusions_survive(tmp_path):
docs = tmp_path / "docs"
docs2 = tmp_path / "docs2"
docs.mkdir()
docs2.mkdir()
sibling_excluded = os.path.abspath(str(docs2 / "secret.txt"))
mgr = _make_manager(tmp_path)
mgr.indexed_directories = [os.path.abspath(str(docs))]
mgr.excluded_files = {sibling_excluded}
mgr.add_directory(str(docs))
# The sibling-directory exclusion must remain — /docs2 is not under /docs.
assert sibling_excluded in mgr.excluded_files
def test_own_directory_exclusions_are_cleared(tmp_path):
docs = tmp_path / "docs"
docs.mkdir()
own_excluded = os.path.abspath(str(docs / "old.txt"))
mgr = _make_manager(tmp_path)
mgr.indexed_directories = [os.path.abspath(str(docs))]
mgr.excluded_files = {own_excluded}
mgr.add_directory(str(docs))
# A file genuinely inside the added directory should be un-excluded.
assert own_excluded not in mgr.excluded_files