Compare commits

...

12 Commits

Author SHA1 Message Date
Stéphane Bidoul 9dcbde7cb1
Merge 8003490440 into a15dd75d98 2023-11-29 16:11:03 +02:00
Tzu-ping Chung a15dd75d98
Merge pull request #12417 from xqm32/fix-outdated-pip-install 2023-11-28 16:08:29 +09:00
Tzu-ping Chung d8ab6dc6c1 Clarify news fragment 2023-11-28 15:06:25 +08:00
Qiming Xu fe10d368f6
Add end line 2023-11-28 14:25:56 +08:00
Qiming Xu 28250baffb
Fix line wrap length and add news entry 2023-11-28 14:17:51 +08:00
Qiming Xu 88ac529219
Fix outdated pip install argument description 2023-11-28 13:15:31 +08:00
Stéphane Bidoul 8003490440
Update src/pip/_internal/exceptions.py
Co-authored-by: Ed Morley <501702+edmorley@users.noreply.github.com>
2023-10-01 16:51:00 +02:00
Stéphane Bidoul c59af71f40
Update docs/html/topics/secure-installs.md
Co-authored-by: Ed Morley <501702+edmorley@users.noreply.github.com>
2023-10-01 16:50:51 +02:00
Stéphane Bidoul 7aab44cc06
Update src/pip/_internal/operations/prepare.py
Co-authored-by: Ed Morley <501702+edmorley@users.noreply.github.com>
2023-10-01 16:50:43 +02:00
Stéphane Bidoul a1cd61b4fc Support immutable VCS references in hash-checking mode. 2023-04-16 17:34:55 +02:00
Stéphane Bidoul 88ac144288 Don't use deprecated git protocol in test 2023-04-16 17:34:55 +02:00
Stéphane Bidoul 061d3e9c5d Don't set a default value for ireq.hashes() function
The default is potentially dangerous.
2023-04-16 13:27:20 +02:00
9 changed files with 96 additions and 26 deletions

View File

@ -45,8 +45,8 @@ When looking at the items to be installed, pip checks what type of item
each is, in the following order:
1. Project or archive URL.
2. Local directory (which must contain a ``setup.py``, or pip will report
an error).
2. Local directory (which must contain a ``pyproject.toml`` or ``setup.py``,
otherwise pip will report an error).
3. Local file (a sdist or wheel format archive, following the naming
conventions for those formats).
4. A requirement, as specified in :pep:`440`.

View File

@ -43,6 +43,12 @@ FooProject == 1.2 \
This prevents a surprising hash mismatch upon the release of a new version that matches the requirement specifier.
```{versionadded} 23.2
VCS URLs that reference a commit hash are now supported in hash checking mode,
since pip now trusts that the VCS provides the required source immutability guarantees.
This is currently only supported with `git` URLs.
```
### Forcing Hash-checking mode
It is possible to force the hash checking mode to be enabled, by passing `--require-hashes` command-line option.

1
news/12417.doc.rst Normal file
View File

@ -0,0 +1 @@
Fix outdated pip install argument description in documentation.

1
news/6469.feature.rst Normal file
View File

@ -0,0 +1 @@
Validate VCS urls in hash-checking mode using their commit hashes.

View File

@ -498,6 +498,32 @@ class VcsHashUnsupported(HashError):
)
class CacheEntryTypeHashNotSupported(HashError):
"""A wheel cache entry was built from a URL that does not support hash checking."""
order = 0
head = (
"Can't verify hashes for these cached requirements because they are "
"from a URL that does not support hash checking:"
)
class DependencyVcsHashNotSupported(HashError):
order = 0
head = (
"Can't verify hashes for these VCS requirements because they are "
"not user supplied, so we don't assume their VCS ref is trusted:"
)
class MutableVcsRefHashNotSupported(HashError):
order = 0
head = (
"Can't verify hashes for these VCS requirements because their ref "
"is not immutable:"
)
class DirectoryUrlHashUnsupported(HashError):
"""A hash was provided for a version-control-system-based requirement, but
we don't have a method for hashing those."""

View File

@ -15,17 +15,20 @@ from pip._vendor.packaging.utils import canonicalize_name
from pip._internal.distributions import make_distribution_for_install_requirement
from pip._internal.distributions.installed import InstalledDistribution
from pip._internal.exceptions import (
CacheEntryTypeHashNotSupported,
DependencyVcsHashNotSupported,
DirectoryUrlHashUnsupported,
HashMismatch,
HashUnpinned,
InstallationError,
MetadataInconsistent,
MutableVcsRefHashNotSupported,
NetworkConnectionError,
VcsHashUnsupported,
)
from pip._internal.index.package_finder import PackageFinder
from pip._internal.metadata import BaseDistribution, get_metadata_distribution
from pip._internal.models.direct_url import ArchiveInfo
from pip._internal.models.direct_url import ArchiveInfo, VcsInfo
from pip._internal.models.link import Link
from pip._internal.models.wheel import Wheel
from pip._internal.network.download import BatchDownloader, Downloader
@ -41,7 +44,7 @@ from pip._internal.utils.direct_url_helpers import (
direct_url_for_editable,
direct_url_from_link,
)
from pip._internal.utils.hashes import Hashes, MissingHashes
from pip._internal.utils.hashes import Hashes, MissingHashes, VcsHashes
from pip._internal.utils.logging import indent_log
from pip._internal.utils.misc import (
display_path,
@ -74,10 +77,14 @@ def _get_prepared_distribution(
return abstract_dist.get_metadata_distribution()
def unpack_vcs_link(link: Link, location: str, verbosity: int) -> None:
def unpack_vcs_link(
link: Link, location: str, verbosity: int, hashes: Optional[Hashes] = None
) -> None:
vcs_backend = vcs.get_backend_for_scheme(link.scheme)
assert vcs_backend is not None
vcs_backend.unpack(location, url=hide_url(link.url), verbosity=verbosity)
if hashes and not vcs_backend.is_immutable_rev_checkout(link.url, location):
raise MutableVcsRefHashNotSupported()
class File:
@ -154,7 +161,7 @@ def unpack_url(
"""
# non-editable vcs urls
if link.is_vcs:
unpack_vcs_link(link, location, verbosity=verbosity)
unpack_vcs_link(link, location, verbosity=verbosity, hashes=hashes)
return None
assert not link.is_existing_dir()
@ -327,6 +334,14 @@ class RequirementPreparer:
# and raise some more informative errors than otherwise.
# (For example, we can raise VcsHashUnsupported for a VCS URL
# rather than HashMissing.)
# Check that --hash is not used with VCS and local directory direct URLs.
if req.original_link:
if req.original_link.is_vcs and req.hashes(trust_internet=False):
raise VcsHashUnsupported()
if req.original_link.is_existing_dir() and req.hashes(trust_internet=False):
raise DirectoryUrlHashUnsupported()
if not self.require_hashes:
return req.hashes(trust_internet=True)
@ -335,7 +350,9 @@ class RequirementPreparer:
# report less-useful error messages for unhashable
# requirements, complaining that there's no hash provided.
if req.link.is_vcs:
raise VcsHashUnsupported()
if not req.user_supplied:
raise DependencyVcsHashNotSupported()
return VcsHashes()
if req.link.is_existing_dir():
raise DirectoryUrlHashUnsupported()
@ -568,24 +585,33 @@ class RequirementPreparer:
assert link.is_file
# We need to verify hashes, and we have found the requirement in the cache
# of locally built wheels.
if (
isinstance(req.download_info.info, ArchiveInfo)
and req.download_info.info.hashes
and hashes.has_one_of(req.download_info.info.hashes)
):
# At this point we know the requirement was built from a hashable source
# artifact, and we verified that the cache entry's hash of the original
# artifact matches one of the hashes we expect. We don't verify hashes
# against the cached wheel, because the wheel is not the original.
if isinstance(req.download_info.info, ArchiveInfo):
if req.download_info.info.hashes and hashes.has_one_of(
req.download_info.info.hashes
):
# At this point we know the requirement was built from a hashable
# source artifact, and we verified that the cache entry's hash of
# the original artifact matches one of the hashes we expect. We
# don't verify hashes against the cached wheel, because the wheel is
# not the original.
hashes = None
else:
logger.warning(
"The hashes of the source archive found in cache entry "
"don't match, ignoring cached built wheel "
"and re-downloading source."
)
req.link = req.cached_wheel_source_link
link = req.link
elif isinstance(req.download_info.info, VcsInfo):
if not req.user_supplied:
raise DependencyVcsHashNotSupported()
# Don't verify hashes against the cached wheel: if it is in cache,
# it means it was built from a URL referencing an immutable commit
# hash.
hashes = None
else:
logger.warning(
"The hashes of the source archive found in cache entry "
"don't match, ignoring cached built wheel "
"and re-downloading source."
)
req.link = req.cached_wheel_source_link
link = req.link
raise CacheEntryTypeHashNotSupported()
self._ensure_link_req_src_dir(req, parallel_builds)

View File

@ -293,7 +293,7 @@ class InstallRequirement:
"""
return bool(self.hash_options)
def hashes(self, trust_internet: bool = True) -> Hashes:
def hashes(self, *, trust_internet: bool) -> Hashes:
"""Return a hash-comparer that considers my option- and URL-based
hashes to be known-good.

View File

@ -149,3 +149,13 @@ class MissingHashes(Hashes):
def _raise(self, gots: Dict[str, "_Hash"]) -> "NoReturn":
raise HashMissing(gots[FAVORITE_HASH].hexdigest())
class VcsHashes(MissingHashes):
"""A workalike for Hashes used for VCS references
It never matches, and is used as a sentinel to indicate that we should
check the VCS reference is an immutable commit reference.
"""
pass

View File

@ -208,7 +208,7 @@ class TestRequirementSet:
reqset = RequirementSet()
reqset.add_unnamed_requirement(
get_processed_req_from_line(
"git+git://github.com/pypa/pip-test-package --hash=sha256:123",
"git+https://github.com/pypa/pip-test-package --hash=sha256:123",
lineno=1,
)
)
@ -231,7 +231,7 @@ class TestRequirementSet:
match=(
r"Can't verify hashes for these requirements because we don't "
r"have a way to hash version control repositories:\n"
r" git\+git://github\.com/pypa/pip-test-package \(from -r "
r" git\+https://github\.com/pypa/pip-test-package \(from -r "
r"file \(line 1\)\)\n"
r"Can't verify hashes for these file:// requirements because "
r"they point to directories:\n"