Skip to content
Open
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
1 change: 1 addition & 0 deletions news/14232.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Prevent wheel cache origin mismatch warnings from exposing URL credentials.
8 changes: 5 additions & 3 deletions src/pip/_internal/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ def get_cache_entry(
@staticmethod
def record_download_origin(cache_dir: str, download_info: DirectUrl) -> None:
origin_path = Path(cache_dir) / ORIGIN_JSON_NAME
download_url = download_info.to_dict_compat()["url"]
if origin_path.exists():
try:
origin = DirectUrl.from_json(origin_path.read_text(encoding="utf-8"))
Expand All @@ -282,13 +283,14 @@ def record_download_origin(cache_dir: str, download_info: DirectUrl) -> None:
else:
# TODO: use DirectUrl.equivalent when
# https://github.com/pypa/pip/pull/10564 is merged.
if origin.url != download_info.url:
origin_url = origin.to_dict_compat()["url"]
if origin_url != download_url:
logger.warning(
"Origin URL %s in cache entry %s does not match download URL "
"%s. This is likely a pip bug or a cache corruption issue. "
"Will overwrite it with the new value.",
origin.url,
origin_url,
cache_dir,
download_info.url,
download_url,
)
origin_path.write_text(download_info.to_json(), encoding="utf-8")
38 changes: 38 additions & 0 deletions tests/unit/test_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from pip._vendor.packaging.tags import Tag, interpreter_name, interpreter_version

from pip._internal.cache import SimpleWheelCache, WheelCache, _hash_dict
from pip._internal.models.direct_url import ArchiveInfo, DirectUrl
from pip._internal.models.link import Link
from pip._internal.utils.misc import ensure_dir
from pip._internal.utils.urls import path_to_url
Expand Down Expand Up @@ -152,3 +153,40 @@ def test_wheel_cache_entry_none_for_existing_directory(tmpdir: Path) -> None:

assert wc.get_cache_entry(link, "example", supported_tags) is None
assert wc.get(link, "example", supported_tags) is link


def test_record_download_origin_ignores_stripped_auth_difference(
tmp_path: Path, caplog: pytest.LogCaptureFixture
) -> None:
download_info = DirectUrl(
url="https://user:secret@example.com/pkg.tar.gz",
archive_info=ArchiveInfo(),
)

WheelCache.record_download_origin(str(tmp_path), download_info)
WheelCache.record_download_origin(str(tmp_path), download_info)

assert caplog.records == []


def test_record_download_origin_redacts_auth_from_warning(
tmp_path: Path, caplog: pytest.LogCaptureFixture
) -> None:
old_info = DirectUrl(
url="https://old-user:old-secret@old.example/pkg.tar.gz",
archive_info=ArchiveInfo(),
)
new_info = DirectUrl(
url="https://new-user:new-secret@new.example/pkg.tar.gz",
archive_info=ArchiveInfo(),
)
WheelCache.record_download_origin(str(tmp_path), old_info)

WheelCache.record_download_origin(str(tmp_path), new_info)

assert len(caplog.records) == 1
message = caplog.records[0].getMessage()
assert "old-secret" not in message
assert "new-secret" not in message
assert "https://old.example/pkg.tar.gz" in message
assert "https://new.example/pkg.tar.gz" in message
Loading