|
| 1 | +import hashlib |
1 | 2 | import os
|
2 | 3 | import re
|
3 | 4 | import ssl
|
|
13 | 14 | from pip._internal.cli.status_codes import ERROR, SUCCESS
|
14 | 15 | from pip._internal.models.index import PyPI, TestPyPI
|
15 | 16 | from pip._internal.utils.misc import rmtree
|
| 17 | +from pip._internal.utils.urls import path_to_url |
16 | 18 | from tests.conftest import CertFactory
|
17 | 19 | from tests.lib import (
|
18 | 20 | PipTestEnvironment,
|
@@ -616,6 +618,117 @@ def test_hashed_install_failure(script: PipTestEnvironment, tmpdir: Path) -> Non
|
616 | 618 | assert len(result.files_created) == 0
|
617 | 619 |
|
618 | 620 |
|
| 621 | +def test_link_hash_pass_require_hashes( |
| 622 | + script: PipTestEnvironment, shared_data: TestData |
| 623 | +) -> None: |
| 624 | + """Test that a good hash in user provided direct URL is |
| 625 | + considered valid for --require-hashes.""" |
| 626 | + url = path_to_url(str(shared_data.packages.joinpath("simple-1.0.tar.gz"))) |
| 627 | + url = ( |
| 628 | + f"{url}#sha256=" |
| 629 | + "393043e672415891885c9a2a0929b1af95fb866d6ca016b42d2e6ce53619b653" |
| 630 | + ) |
| 631 | + script.pip_install_local("--no-deps", "--require-hashes", url) |
| 632 | + |
| 633 | + |
| 634 | +def test_bad_link_hash_install_failure( |
| 635 | + script: PipTestEnvironment, shared_data: TestData |
| 636 | +) -> None: |
| 637 | + """Test that wrong hash in direct URL stops installation.""" |
| 638 | + url = path_to_url(str(shared_data.packages.joinpath("simple-1.0.tar.gz"))) |
| 639 | + url = f"{url}#sha256=invalidhash" |
| 640 | + result = script.pip_install_local("--no-deps", url, expect_error=True) |
| 641 | + assert "THESE PACKAGES DO NOT MATCH THE HASHES" in result.stderr |
| 642 | + |
| 643 | + |
| 644 | +def test_bad_link_hash_good_user_hash_install_success( |
| 645 | + script: PipTestEnvironment, shared_data: TestData, tmp_path: Path |
| 646 | +) -> None: |
| 647 | + """Test that wrong hash in direct URL ignored when good --hash provided. |
| 648 | +
|
| 649 | + This behaviour may be accidental? |
| 650 | + """ |
| 651 | + url = path_to_url(str(shared_data.packages.joinpath("simple-1.0.tar.gz"))) |
| 652 | + url = f"{url}#sha256=invalidhash" |
| 653 | + digest = "393043e672415891885c9a2a0929b1af95fb866d6ca016b42d2e6ce53619b653" |
| 654 | + with requirements_file( |
| 655 | + f"simple @ {url} --hash sha256:{digest}", tmp_path |
| 656 | + ) as reqs_file: |
| 657 | + script.pip_install_local("--no-deps", "--require-hashes", "-r", reqs_file) |
| 658 | + |
| 659 | + |
| 660 | +def test_link_hash_in_dep_fails_require_hashes( |
| 661 | + script: PipTestEnvironment, tmp_path: Path, shared_data: TestData |
| 662 | +) -> None: |
| 663 | + """Test that a good hash in direct URL dependency is not considered |
| 664 | + for --require-hashes.""" |
| 665 | + # Create a project named pkga that depends on the simple-1.0.tar.gz with a direct |
| 666 | + # URL including a hash. |
| 667 | + simple_url = path_to_url(str(shared_data.packages.joinpath("simple-1.0.tar.gz"))) |
| 668 | + simple_url_with_hash = ( |
| 669 | + f"{simple_url}#sha256=" |
| 670 | + "393043e672415891885c9a2a0929b1af95fb866d6ca016b42d2e6ce53619b653" |
| 671 | + ) |
| 672 | + project_path = tmp_path / "pkga" |
| 673 | + project_path.mkdir() |
| 674 | + project_path.joinpath("pyproject.toml").write_text( |
| 675 | + textwrap.dedent( |
| 676 | + f"""\ |
| 677 | + [project] |
| 678 | + name = "pkga" |
| 679 | + version = "1.0" |
| 680 | + dependencies = ["simple @ {simple_url_with_hash}"] |
| 681 | + """ |
| 682 | + ) |
| 683 | + ) |
| 684 | + # Build a wheel for pkga and compute its hash. |
| 685 | + wheelhouse = tmp_path / "wheehouse" |
| 686 | + wheelhouse.mkdir() |
| 687 | + script.pip("wheel", "--no-deps", "-w", wheelhouse, project_path) |
| 688 | + digest = hashlib.sha256( |
| 689 | + wheelhouse.joinpath("pkga-1.0-py3-none-any.whl").read_bytes() |
| 690 | + ).hexdigest() |
| 691 | + # Install pkga from a requirements file with hash, using --require-hashes. |
| 692 | + # This should fail because we have not provided a hash for the 'simple' dependency. |
| 693 | + with requirements_file(f"pkga==1.0 --hash sha256:{digest}", tmp_path) as reqs_file: |
| 694 | + result = script.pip( |
| 695 | + "install", |
| 696 | + "--no-build-isolation", |
| 697 | + "--require-hashes", |
| 698 | + "--no-index", |
| 699 | + "-f", |
| 700 | + wheelhouse, |
| 701 | + "-r", |
| 702 | + reqs_file, |
| 703 | + expect_error=True, |
| 704 | + ) |
| 705 | + assert "Hashes are required in --require-hashes mode" in result.stderr |
| 706 | + |
| 707 | + |
| 708 | +def test_bad_link_hash_in_dep_install_failure( |
| 709 | + script: PipTestEnvironment, tmp_path: Path, shared_data: TestData |
| 710 | +) -> None: |
| 711 | + """Test that wrong hash in direct URL dependency stops installation.""" |
| 712 | + url = path_to_url(str(shared_data.packages.joinpath("simple-1.0.tar.gz"))) |
| 713 | + url = f"{url}#sha256=invalidhash" |
| 714 | + project_path = tmp_path / "pkga" |
| 715 | + project_path.mkdir() |
| 716 | + project_path.joinpath("pyproject.toml").write_text( |
| 717 | + textwrap.dedent( |
| 718 | + f"""\ |
| 719 | + [project] |
| 720 | + name = "pkga" |
| 721 | + version = "1.0" |
| 722 | + dependencies = ["simple @ {url}"] |
| 723 | + """ |
| 724 | + ) |
| 725 | + ) |
| 726 | + result = script.pip_install_local( |
| 727 | + "--no-build-isolation", project_path, expect_error=True |
| 728 | + ) |
| 729 | + assert "THESE PACKAGES DO NOT MATCH THE HASHES" in result.stderr, result.stderr |
| 730 | + |
| 731 | + |
619 | 732 | def assert_re_match(pattern: str, text: str) -> None:
|
620 | 733 | assert re.search(pattern, text), f"Could not find {pattern!r} in {text!r}"
|
621 | 734 |
|
|
0 commit comments