8000 fix(detectors): a rooted search path is refused on 3.13 too by k-sok · Pull Request #170 · habit-hooks/habit-hooks · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
# Changelog

## Unreleased

### Fixed

- **On Python 3.13 a plugin's `search_paths` could name a directory outside
your project.** A detector names the directories under the project its tool
is looked for in, and an entry that climbs out of it is refused — except a
Windows-rooted one like `\tools`, which stopped being refused when CPython
3.13 changed what counts as a rooted path there. On Windows that entry
resolves to `C:\tools`, so a tool could be found, and spawned, from outside
the project; on POSIX it named a directory under the project and was
harmless. `search_paths` is declared in a plugin's `config.toml`, which a
project can also carry itself under `.habit-hooks/<plugin>/`. It is refused
again, on every interpreter. (#166)

## 1.5.0

Ruby. A `Gemfile` or a `.rubocop.yml` in your project and habit-hooks now
Expand Down
13 changes: 10 additions & 3 deletions src/habit_hooks/detectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
from __future__ import annotations

import ntpath
import posixpath

from attrs import field, fields, frozen

Expand Down Expand Up @@ -126,18 +125,26 @@ def _splices_extra_directories(path: str) -> bool:


def _escapes_the_project(path: str) -> bool:
"""Whether the path names anything but a directory under the project.
r"""Whether the path names anything but a directory under the project.

Both platforms' forms, not the host's, because a config travels: a Windows
drive or rooted path reads as relative to ``os.path.isabs`` on a Mac and is
a directory this project still does not keep. Drive-relative (``C:tools``)
is refused with them — it names the current directory of another drive —
and so is a ``..`` component, which climbs out the same way.

The leading separator is read off the components rather than asked of
``isabs``, which cannot answer for both platforms at once: ``ntpath.isabs``
stopped counting a single leading (back)slash as rooted in CPython 3.13,
and ``\tools`` is still ``C:\tools`` to ``project_dir /`` on Windows
(#166). Once ``splitdrive`` has returned empty, an empty first component
is what the older ``ntpath.isabs`` meant, and it subsumes
``posixpath.isabs``.
"""
if ntpath.splitdrive(path)[0]:
return True
components = path.replace("\\", "/").split("/")
return posixpath.isabs(path) or ntpath.isabs(path) or ".." in components
return components[0] == "" or ".." in components


def _reject_invalid_detector(entry: object, where: str) -> None:
Expand Down
Loading
0