Skip to content

Commit a201003

Browse files
authored
Simplify check for symlinks that resolve outside root (#4221)
This PR does not change any behaviour. There have been 1-2 issues about symlinks recently. Both over and under resolving can cause problems. This makes a case where we resolve more explicit and prevent a resolved path from leaking out via the return.
1 parent dab37a6 commit a201003

File tree

3 files changed

+22
-25
lines changed

3 files changed

+22
-25
lines changed

src/black/__init__.py

+4-7
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@
5050
gen_python_files,
5151
get_gitignore,
5252
get_root_relative_path,
53-
normalize_path_maybe_ignore,
5453
parse_pyproject_toml,
5554
path_is_excluded,
55+
resolves_outside_root_or_cannot_stat,
5656
wrap_stream_for_windows,
5757
)
5858
from black.handle_ipynb_magics import (
@@ -763,12 +763,9 @@ def get_sources(
763763
)
764764
continue
765765

766-
normalized_path: Optional[str] = normalize_path_maybe_ignore(
767-
path, root, report
768-
)
769-
if normalized_path is None:
766+
if resolves_outside_root_or_cannot_stat(path, root, report):
770767
if verbose:
771-
out(f'Skipping invalid source: "{normalized_path}"', fg="red")
768+
out(f'Skipping invalid source: "{path}"', fg="red")
772769
continue
773770

774771
if is_stdin:
@@ -780,7 +777,7 @@ def get_sources(
780777
continue
781778

782779
if verbose:
783-
out(f'Found input source: "{normalized_path}"', fg="blue")
780+
out(f'Found input source: "{path}"', fg="blue")
784781
sources.add(path)
785782
elif path.is_dir():
786783
path = root / (path.resolve().relative_to(root))

src/black/files.py

+11-14
Original file line numberDiff line numberDiff line change
@@ -254,26 +254,24 @@ def get_gitignore(root: Path) -> PathSpec:
254254
raise
255255

256256

257-
def normalize_path_maybe_ignore(
257+
def resolves_outside_root_or_cannot_stat(
258258
path: Path,
259259
root: Path,
260260
report: Optional[Report] = None,
261-
) -> Optional[str]:
262-
"""Normalize `path`. May return `None` if `path` was ignored.
263-
264-
`report` is where "path ignored" output goes.
261+
) -> bool:
262+
"""
263+
Returns whether the path is a symbolic link that points outside the
264+
root directory. Also returns True if we failed to resolve the path.
265265
"""
266266
try:
267-
abspath = path if path.is_absolute() else Path.cwd() / path
268-
normalized_path = abspath.resolve()
269-
root_relative_path = get_root_relative_path(normalized_path, root, report)
270-
267+
if sys.version_info < (3, 8, 6):
268+
path = path.absolute() # https://bugs.python.org/issue33660
269+
resolved_path = path.resolve()
270+
return get_root_relative_path(resolved_path, root, report) is None
271271
except OSError as e:
272272
if report:
273273
report.path_ignored(path, f"cannot be read because {e}")
274-
return None
275-
276-
return root_relative_path
274+
return True
277275

278276

279277
def get_root_relative_path(
@@ -369,8 +367,7 @@ def gen_python_files(
369367
report.path_ignored(child, "matches the --force-exclude regular expression")
370368
continue
371369

372-
normalized_path = normalize_path_maybe_ignore(child, root, report)
373-
if normalized_path is None:
370+
if resolves_outside_root_or_cannot_stat(child, root, report):
374371
continue
375372

376373
if child.is_dir():

tests/test_black.py

+7-4
Original file line numberDiff line numberDiff line change
@@ -1760,12 +1760,15 @@ def test_bpo_33660_workaround(self) -> None:
17601760
return
17611761

17621762
# https://bugs.python.org/issue33660
1763+
# Can be removed when we drop support for Python 3.8.5
17631764
root = Path("/")
17641765
with change_directory(root):
17651766
path = Path("workspace") / "project"
17661767
report = black.Report(verbose=True)
1767-
normalized_path = black.normalize_path_maybe_ignore(path, root, report)
1768-
self.assertEqual(normalized_path, "workspace/project")
1768+
resolves_outside = black.resolves_outside_root_or_cannot_stat(
1769+
path, root, report
1770+
)
1771+
self.assertIs(resolves_outside, False)
17691772

17701773
def test_normalize_path_ignore_windows_junctions_outside_of_root(self) -> None:
17711774
if system() != "Windows":
@@ -1778,13 +1781,13 @@ def test_normalize_path_ignore_windows_junctions_outside_of_root(self) -> None:
17781781
os.system(f"mklink /J {junction_dir} {junction_target_outside_of_root}")
17791782

17801783
report = black.Report(verbose=True)
1781-
normalized_path = black.normalize_path_maybe_ignore(
1784+
resolves_outside = black.resolves_outside_root_or_cannot_stat(
17821785
junction_dir, root, report
17831786
)
17841787
# Manually delete for Python < 3.8
17851788
os.system(f"rmdir {junction_dir}")
17861789

1787-
self.assertEqual(normalized_path, None)
1790+
self.assertIs(resolves_outside, True)
17881791

17891792
def test_newline_comment_interaction(self) -> None:
17901793
source = "class A:\\\r\n# type: ignore\n pass\n"

0 commit comments

Comments
 (0)