From d660df8966f0c2f645cb3c5b31601523e4ad2970 Mon Sep 17 00:00:00 2001 From: Cheruku Sri Charan Reddy <185475071+sricharanreddycheruku@users.noreply.github.com> Date: Wed, 7 Oct 2026 19:47:43 +0000 Subject: [PATCH] Avoid following symlink targets when link traversal is disabled --- CHANGES.rst | 2 ++ CHANGES_1.in.rst | 2 ++ README-dist.rst | 2 ++ pathspec/util.py | 2 +- tests/test_01_util.py | 25 +++++++++++++++++++++++++ 5 files changed, 32 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index b3e7efc..b71cd38 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -20,6 +20,8 @@ New features: Bug fixes: +- Avoid following symbolic link targets when checking file types in ``iter_tree_files(follow_links=False)``. + - Honor `on_error` when opening a directory fails during tree traversal. - `Pull #123`_: Ignore invalid gitignore bracket ranges for `GitIgnoreSpec`. diff --git a/CHANGES_1.in.rst b/CHANGES_1.in.rst index 2267bb6..64eb8fe 100644 --- a/CHANGES_1.in.rst +++ b/CHANGES_1.in.rst @@ -17,6 +17,8 @@ New features: Bug fixes: +- Avoid following symbolic link targets when checking file types in ``iter_tree_files(follow_links=False)``. + - Honor `on_error` when opening a directory fails during tree traversal. - `Pull #123`_: Ignore invalid gitignore bracket ranges for `GitIgnoreSpec`. diff --git a/README-dist.rst b/README-dist.rst index 7dcca83..2497721 100644 --- a/README-dist.rst +++ b/README-dist.rst @@ -236,6 +236,8 @@ Major changes: Bug fixes: +- Avoid following symbolic link targets when checking file types in ``iter_tree_files(follow_links=False)``. + - `Pull #123`_: Ignore invalid gitignore bracket ranges for `GitIgnoreSpec`. diff --git a/pathspec/util.py b/pathspec/util.py index 52782d4..2332cb5 100644 --- a/pathspec/util.py +++ b/pathspec/util.py @@ -476,7 +476,7 @@ def _iter_tree_files_next( # files. yield from _iter_tree_files_next(root_full, node_rel, memo, on_error, follow_links) - elif node_ent.is_file(): + elif node_ent.is_file(follow_symlinks=follow_links): # Child node is a file, yield it. yield node_rel diff --git a/tests/test_01_util.py b/tests/test_01_util.py index b0e4ff8..f84d5b3 100644 --- a/tests/test_01_util.py +++ b/tests/test_01_util.py @@ -624,6 +624,31 @@ def test_02_link_8_no_follow_links_2_files(self): 'DirX', ]))) + def test_02_link_9_no_follow_loop_links_2_files(self): + """ + Unfollowed links with cyclic targets are yielded without inspecting targets. + """ + self.require_symlink() + for case, links in [ + ('Self', [('Loop', 'Loop')]), + ('Pair', [('First', 'Second'), ('Second', 'First')]), + ]: + with self.subTest(case=case): + self.make_dirs([case, f'{case}/Dir']) + self.make_files([f'{case}/kept.txt', f'{case}/Dir/child.txt']) + self.make_links([ + (f'{case}/{link}', f'{case}/{target}') + for link, target in links + ]) + errors = [] + results = set(iter_tree_files( + self.temp_dir / case, follow_links=False, on_error=errors.append, + )) + self.assertEqual(results, { + 'kept.txt', ospath('Dir/child.txt'), *[link for link, _ in links], + }) + self.assertEqual(errors, []) + def test_03_subdir_1_from_filesystem_root(self): """ raverse only the requested subtree when the root ends in a separator.