Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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"]
Comment thread
carrerasdarren-cell marked this conversation as resolved.
Outdated
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"]
Comment thread
carrerasdarren-cell marked this conversation as resolved.
Outdated
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