fix pathlib exception handling with symlinks (#4161)

Fixes #4077
This commit is contained in:
Daniel Krzeminski 2024-01-22 11:46:57 -06:00 committed by GitHub
parent 6f3fb78444
commit 8fe602b1fa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 37 additions and 9 deletions

View File

@ -29,6 +29,8 @@
<!-- Changes to how Black can be configured --> <!-- Changes to how Black can be configured -->
- Fix symlink handling, properly catch and ignore symlinks that point outside of root
(#4161)
- Fix cache mtime logic that resulted in false positive cache hits (#4128) - Fix cache mtime logic that resulted in false positive cache hits (#4128)
### Packaging ### Packaging

View File

@ -49,6 +49,7 @@
find_user_pyproject_toml, find_user_pyproject_toml,
gen_python_files, gen_python_files,
get_gitignore, get_gitignore,
get_root_relative_path,
normalize_path_maybe_ignore, normalize_path_maybe_ignore,
parse_pyproject_toml, parse_pyproject_toml,
path_is_excluded, path_is_excluded,
@ -700,7 +701,10 @@ def get_sources(
# Compare the logic here to the logic in `gen_python_files`. # Compare the logic here to the logic in `gen_python_files`.
if is_stdin or path.is_file(): if is_stdin or path.is_file():
root_relative_path = path.absolute().relative_to(root).as_posix() root_relative_path = get_root_relative_path(path, root, report)
if root_relative_path is None:
continue
root_relative_path = "/" + root_relative_path root_relative_path = "/" + root_relative_path

View File

@ -259,14 +259,7 @@ def normalize_path_maybe_ignore(
try: try:
abspath = path if path.is_absolute() else Path.cwd() / path abspath = path if path.is_absolute() else Path.cwd() / path
normalized_path = abspath.resolve() normalized_path = abspath.resolve()
try: root_relative_path = get_root_relative_path(normalized_path, root, report)
root_relative_path = normalized_path.relative_to(root).as_posix()
except ValueError:
if report:
report.path_ignored(
path, f"is a symbolic link that points outside {root}"
)
return None
except OSError as e: except OSError as e:
if report: if report:
@ -276,6 +269,21 @@ def normalize_path_maybe_ignore(
return root_relative_path return root_relative_path
def get_root_relative_path(
path: Path,
root: Path,
report: Optional[Report] = None,
) -> Optional[str]:
"""Returns the file path relative to the 'root' directory"""
try:
root_relative_path = path.absolute().relative_to(root).as_posix()
except ValueError:
if report:
report.path_ignored(path, f"is a symbolic link that points outside {root}")
return None
return root_relative_path
def _path_is_ignored( def _path_is_ignored(
root_relative_path: str, root_relative_path: str,
root: Path, root: Path,

View File

@ -2592,6 +2592,20 @@ def test_symlinks(self) -> None:
outside_root_symlink.resolve.assert_called_once() outside_root_symlink.resolve.assert_called_once()
ignored_symlink.resolve.assert_not_called() ignored_symlink.resolve.assert_not_called()
def test_get_sources_with_stdin_symlink_outside_root(
self,
) -> None:
path = THIS_DIR / "data" / "include_exclude_tests"
stdin_filename = str(path / "b/exclude/a.py")
outside_root_symlink = Path("/target_directory/a.py")
with patch("pathlib.Path.resolve", return_value=outside_root_symlink):
assert_collected_sources(
root=Path("target_directory/"),
src=["-"],
expected=[],
stdin_filename=stdin_filename,
)
@patch("black.find_project_root", lambda *args: (THIS_DIR.resolve(), None)) @patch("black.find_project_root", lambda *args: (THIS_DIR.resolve(), None))
def test_get_sources_with_stdin(self) -> None: def test_get_sources_with_stdin(self) -> None:
src = ["-"] src = ["-"]