Skip to content

Commit d1106f1

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

File tree

4 files changed

+25
-14
lines changed

4 files changed

+25
-14
lines changed

src/pip/_internal/network/cache.py

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
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 adjacent_tmp_file, replace, copy_directory_permissions
1717
from pip._internal.utils.misc import ensure_dir
1818

1919

@@ -82,16 +82,7 @@ def _write_to_file(self, path: str, writer_func: Callable[[BinaryIO], Any]) -> N
8282
writer_func(f)
8383
# Inherit the read/write permissions of the cache directory
8484
# 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)
85+
copy_directory_permissions(os.path.dirname(path), f)
9586

9687
replace(f.name, path)
9788

src/pip/_internal/self_outdated_check.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
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 adjacent_tmp_file, check_path_owner, replace, copy_directory_permissions
3131
from pip._internal.utils.misc import (
3232
ExternallyManagedEnvironment,
3333
check_externally_managed,
@@ -100,13 +100,15 @@ def set(self, pypi_version: str, current_time: datetime.datetime) -> None:
100100
if not self._statefile_path:
101101
return
102102

103+
statefile_directory = os.path.dirname(self._statefile_path)
104+
103105
# Check to make sure that we own the directory
104-
if not check_path_owner(os.path.dirname(self._statefile_path)):
106+
if not check_path_owner(statefile_directory):
105107
return
106108

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

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

121123
with adjacent_tmp_file(self._statefile_path) as f:
122124
f.write(text.encode())
125+
copy_directory_permissions(statefile_directory, f)
123126

124127
try:
125128
# 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)