Skip to content

Commit b483e95

Browse files
icanhasmathclaude
andcommitted
Backport CVE-2026-1703: use os.path.commonpath in is_within_directory
`os.path.commonprefix` operates character-by-character, so it treated `/foo/parent/child` and `/foo/parent/childfoo` as sharing the prefix `/foo/parent/child` and reported them as "within" the same directory. A crafted wheel could exploit this to write files into a sibling directory whose name is a prefix of the install path. Switch to `os.path.commonpath`, which operates on path components, and add a regression test for the prefix-vs-component substring case. Equivalent of upstream commit 4c651b7 (Seth Michael Larson, "Use os.path.commonpath() instead of commonprefix()"). `os.path.commonpath` is available since CPython 3.5, so this is safe on Python 3.7. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 0087814 commit b483e95

2 files changed

Lines changed: 3 additions & 1 deletion

File tree

src/pip/_internal/utils/unpacking.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ def is_within_directory(directory: str, target: str) -> bool:
8181
abs_directory = os.path.abspath(directory)
8282
abs_target = os.path.abspath(target)
8383

84-
prefix = os.path.commonprefix([abs_directory, abs_target])
84+
prefix = os.path.commonpath([abs_directory, abs_target])
8585
return prefix == abs_directory
8686

8787

tests/unit/test_utils_unpacking.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,8 @@ def test_unpack_tar_unicode(tmpdir: Path) -> None:
386386
(("parent/", "parent/sub"), True),
387387
# Test target outside parent
388388
(("parent/", "parent/../sub"), False),
389+
# Test target sub-string of parent
390+
(("parent/child", "parent/childfoo"), False),
389391
],
390392
)
391393
def test_is_within_directory(args: Tuple[str, str], expected: bool) -> None:

0 commit comments

Comments
 (0)