From 89c9d9cf18da96790b8568522ae7311cf626d182 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Eustace?= Date: Mon, 20 Sep 2021 23:17:25 +0200 Subject: [PATCH] Ensure all accepted hash types are checked --- poetry.lock | 14 +++--- poetry/installation/executor.py | 32 ++++++++---- pyproject.toml | 2 +- tests/installation/test_executor.py | 77 +++++++++++++++++++++++++++++ 4 files changed, 108 insertions(+), 17 deletions(-) diff --git a/poetry.lock b/poetry.lock index cf6819628b1..310d212b719 100644 --- a/poetry.lock +++ b/poetry.lock @@ -343,7 +343,7 @@ six = "*" [[package]] name = "identify" -version = "2.2.14" +version = "2.2.15" description = "File identification library for Python" category = "dev" optional = false @@ -624,7 +624,7 @@ dev = ["pre-commit", "tox"] [[package]] name = "poetry-core" -version = "1.0.5" +version = "1.0.6" description = "Poetry PEP 517 Build Backend" category = "main" optional = false @@ -1061,7 +1061,7 @@ testing = ["pathlib2", "unittest2", "jaraco.itertools", "func-timeout"] [metadata] lock-version = "1.1" python-versions = "~2.7 || ^3.5" -content-hash = "24a401f74f301836bdb4c066edcb766d1c0702a96151add3145e7b389928a94f" +content-hash = "130da628eda47ac4a8006a6129598eb8a3b98f28193d4f969f2eee5012b6508f" [metadata.files] atomicwrites = [ @@ -1320,8 +1320,8 @@ httpretty = [ {file = "httpretty-0.9.7.tar.gz", hash = "sha256:66216f26b9d2c52e81808f3e674a6fb65d4bf719721394a1a9be926177e55fbe"}, ] identify = [ - {file = "identify-2.2.14-py2.py3-none-any.whl", hash = "sha256:113a76a6ba614d2a3dd408b3504446bcfac0370da5995aa6a17fd7c6dffde02d"}, - {file = "identify-2.2.14.tar.gz", hash = "sha256:32f465f3c48083f345ad29a9df8419a4ce0674bf4a8c3245191d65c83634bdbf"}, + {file = "identify-2.2.15-py2.py3-none-any.whl", hash = "sha256:de83a84d774921669774a2000bf87ebba46b4d1c04775f4a5d37deff0cf39f73"}, + {file = "identify-2.2.15.tar.gz", hash = "sha256:528a88021749035d5a39fe2ba67c0642b8341aaf71889da0e1ed669a429b87f0"}, ] idna = [ {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, @@ -1427,8 +1427,8 @@ pluggy = [ {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, ] poetry-core = [ - {file = "poetry-core-1.0.5.tar.gz", hash = "sha256:8cad9893ea70e344b2ce1a75d3834ba3fb1bac8123f89aad758e59e2a5d67805"}, - {file = "poetry_core-1.0.5-py2.py3-none-any.whl", hash = "sha256:affb0d841d897fbc214fa00765ada432582dcb93b8d0d3a5696712dd96f67dde"}, + {file = "poetry-core-1.0.6.tar.gz", hash = "sha256:dd3c97003579242236890306836f2acc86d9741e6bea320dda6f844f16b0d845"}, + {file = "poetry_core-1.0.6-py2.py3-none-any.whl", hash = "sha256:4ef68b4a55a8a95a60e6a312317e5a2f2af7590cf3d46b6bfe648c1e5f13cc48"}, ] pre-commit = [ {file = "pre_commit-2.15.0-py2.py3-none-any.whl", hash = "sha256:a4ed01000afcb484d9eb8d504272e642c4c4099bbad3a6b27e519bd6a3e928a6"}, diff --git a/poetry/installation/executor.py b/poetry/installation/executor.py index ba117b3a7ad..79bf4e34a49 100644 --- a/poetry/installation/executor.py +++ b/poetry/installation/executor.py @@ -608,16 +608,30 @@ def _download_link(self, operation, link): archive = self._chef.prepare(archive) if package.files: - archive_hash = ( - "sha256:" - + FileDependency( - package.name, - Path(archive.path) if isinstance(archive, Link) else archive, - ).hash() - ) - if archive_hash not in {f["hash"] for f in package.files}: + hashes = {f["hash"] for f in package.files} + hash_types = {h.split(":")[0] for h in hashes} + archive_hashes = set() + for hash_type in hash_types: + archive_hashes.add( + "{}:{}".format( + hash_type, + FileDependency( + package.name, + Path(archive.path) + if isinstance(archive, Link) + else archive, + ).hash(hash_type), + ) + ) + + if archive_hashes.isdisjoint(hashes): raise RuntimeError( - "Invalid hash for {} using archive {}".format(package, archive.name) + "Invalid hashes ({}) for {} using archive {}. Expected one of {}.".format( + ", ".join(sorted(archive_hashes)), + package, + archive.name, + ", ".join(sorted(hashes)), + ) ) return archive diff --git a/pyproject.toml b/pyproject.toml index cfd0c83d983..d499110cc50 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ classifiers = [ [tool.poetry.dependencies] python = "~2.7 || ^3.5" -poetry-core = "~1.0.5" +poetry-core = "~1.0.6" cleo = "^0.8.1" clikit = "^0.6.2" crashtest = { version = "^0.3.0", python = "^3.6" } diff --git a/tests/installation/test_executor.py b/tests/installation/test_executor.py index bb659321d0f..db889312e83 100644 --- a/tests/installation/test_executor.py +++ b/tests/installation/test_executor.py @@ -11,6 +11,8 @@ from poetry.config.config import Config from poetry.core.packages.package import Package +from poetry.core.packages.utils.link import Link +from poetry.installation.chef import Chef from poetry.installation.executor import Executor from poetry.installation.operations import Install from poetry.installation.operations import Uninstall @@ -251,3 +253,78 @@ def test_executor_should_delete_incomplete_downloads( executor._download(Install(Package("tomlkit", "0.5.3"))) assert not destination_fixture.exists() + + +def test_executor_should_check_every_possible_hash_types( + config, io, pool, mocker, fixture_dir, tmp_dir +): + mocker.patch.object( + Chef, "get_cached_archive_for_link", side_effect=lambda link: link, + ) + mocker.patch.object( + Executor, + "_download_archive", + return_value=fixture_dir("distributions").joinpath( + "demo-0.1.0-py2.py3-none-any.whl" + ), + ) + + env = MockEnv(path=Path(tmp_dir)) + executor = Executor(env, pool, config, io) + + package = Package("demo", "0.1.0") + package.files = [ + { + "file": "demo-0.1.0-py2.py3-none-any.whl", + "hash": "md5:15507846fd4299596661d0197bfb4f90", + } + ] + + archive = executor._download_link( + Install(package), Link("https://example.com/demo-0.1.0-py2.py3-none-any.whl") + ) + + assert archive == fixture_dir("distributions").joinpath( + "demo-0.1.0-py2.py3-none-any.whl" + ) + + +def test_executor_should_check_every_possible_hash_types_before_failing( + config, io, pool, mocker, fixture_dir, tmp_dir +): + mocker.patch.object( + Chef, "get_cached_archive_for_link", side_effect=lambda link: link, + ) + mocker.patch.object( + Executor, + "_download_archive", + return_value=fixture_dir("distributions").joinpath( + "demo-0.1.0-py2.py3-none-any.whl" + ), + ) + + env = MockEnv(path=Path(tmp_dir)) + executor = Executor(env, pool, config, io) + + package = Package("demo", "0.1.0") + package.files = [ + {"file": "demo-0.1.0-py2.py3-none-any.whl", "hash": "md5:123456"}, + {"file": "demo-0.1.0-py2.py3-none-any.whl", "hash": "sha256:123456"}, + ] + + with pytest.raises(RuntimeError) as e: + executor._download_link( + Install(package), + Link("https://example.com/demo-0.1.0-py2.py3-none-any.whl"), + ) + + expected_message = ( + "Invalid hashes " + "(" + "md5:15507846fd4299596661d0197bfb4f90, " + "sha256:70e704135718fffbcbf61ed1fc45933cfd86951a744b681000eaaa75da31f17a" + ") " + "for demo (0.1.0) using archive demo-0.1.0-py2.py3-none-any.whl. " + "Expected one of md5:123456, sha256:123456." + ) + assert str(e.value) == expected_message