Skip to content

Commit 2e4aaa2

Browse files
committed
Ensure selfcheck file inherits directory permissions
1 parent 8752c91 commit 2e4aaa2

File tree

5 files changed

+35
-14
lines changed

5 files changed

+35
-14
lines changed

news/13528.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
selfcheck file in cache directory has same permissions as the rest of the cache.

src/pip/_internal/network/cache.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@
1313
from pip._vendor.cachecontrol.caches import SeparateBodyFileCache
1414
from pip._vendor.requests.models import Response
1515

16-
from pip._internal.utils.filesystem import adjacent_tmp_file, replace
16+
from pip._internal.utils.filesystem import (
17+
adjacent_tmp_file,
18+
copy_directory_permissions,
19+
replace,
20+
)
1721
from pip._internal.utils.misc import ensure_dir
1822

1923

@@ -82,16 +86,7 @@ def _write_to_file(self, path: str, writer_func: Callable[[BinaryIO], Any]) -> N
8286
writer_func(f)
8387
# Inherit the read/write permissions of the cache directory
8488
# to enable multi-user cache use-cases.
85-
mode = (
86-
os.stat(self.directory).st_mode
87-
& 0o666 # select read/write permissions of cache directory
88-
| 0o600 # set owner read/write permissions
89-
)
90-
# Change permissions only if there is no risk of following a symlink.
91-
if os.chmod in os.supports_fd:
92-
os.chmod(f.fileno(), mode)
93-
elif os.chmod in os.supports_follow_symlinks:
94-
os.chmod(f.name, mode, follow_symlinks=False)
89+
copy_directory_permissions(self.directory, f)
9590

9691
replace(f.name, path)
9792

src/pip/_internal/self_outdated_check.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,12 @@
2727
get_best_invocation_for_this_pip,
2828
get_best_invocation_for_this_python,
2929
)
30-
from pip._internal.utils.filesystem import adjacent_tmp_file, check_path_owner, replace
30+
from pip._internal.utils.filesystem import (
31+
adjacent_tmp_file,
32+
check_path_owner,
33+
copy_directory_permissions,
34+
replace,
35+
)
3136
from pip._internal.utils.misc import (
3237
ExternallyManagedEnvironment,
3338
check_externally_managed,
@@ -100,13 +105,15 @@ def set(self, pypi_version: str, current_time: datetime.datetime) -> None:
100105
if not self._statefile_path:
101106
return
102107

108+
statefile_directory = os.path.dirname(self._statefile_path)
109+
103110
# Check to make sure that we own the directory
104-
if not check_path_owner(os.path.dirname(self._statefile_path)):
111+
if not check_path_owner(statefile_directory):
105112
return
106113

107114
# Now that we've ensured the directory is owned by this user, we'll go
108115
# ahead and make sure that all our directories are created.
109-
ensure_dir(os.path.dirname(self._statefile_path))
116+
ensure_dir(statefile_directory)
110117

111118
state = {
112119
# Include the key so it's easy to tell which pip wrote the
@@ -120,6 +127,7 @@ def set(self, pypi_version: str, current_time: datetime.datetime) -> None:
120127

121128
with adjacent_tmp_file(self._statefile_path) as f:
122129
f.write(text.encode())
130+
copy_directory_permissions(statefile_directory, f)
123131

124132
try:
125133
# Since we have a prefix-specific state file, we can just

src/pip/_internal/utils/filesystem.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,3 +150,16 @@ def directory_size(path: str) -> int | float:
150150

151151
def format_directory_size(path: str) -> str:
152152
return format_size(directory_size(path))
153+
154+
155+
def copy_directory_permissions(directory: str, target_file: BinaryIO) -> None:
156+
mode = (
157+
os.stat(directory).st_mode
158+
& 0o666 # select read/write permissions of cache directory
159+
| 0o600 # set owner read/write permissions
160+
)
161+
# Change permissions only if there is no risk of following a symlink.
162+
if os.chmod in os.supports_fd:
163+
os.chmod(target_file.fileno(), mode)
164+
elif os.chmod in os.supports_follow_symlinks:
165+
os.chmod(target_file.name, mode, follow_symlinks=False)

tests/unit/test_self_check_outdated.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,10 @@ def test_writes_expected_statefile(self, tmpdir: Path) -> None:
190190
"pypi_version": "1.0.0",
191191
}
192192

193+
statefile_permissions = os.stat(expected_path).st_mode & 0o666
194+
selfcheckdir_permissions = os.stat(cache_dir / "selfcheck").st_mode & 0o666
195+
assert statefile_permissions == selfcheckdir_permissions
196+
193197

194198
@patch("pip._internal.self_outdated_check._self_version_check_logic")
195199
def test_suppressed_by_externally_managed(mocked_function: Mock, tmpdir: Path) -> None:

0 commit comments

Comments
 (0)