From 41e336e4c170da1f62425d1a6ddbe7304f812825 Mon Sep 17 00:00:00 2001 From: Adrian Wilkins Date: Tue, 21 Sep 2021 10:12:50 +0100 Subject: [PATCH 1/5] Minor type hint improvement --- poetry/inspection/info.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/poetry/inspection/info.py b/poetry/inspection/info.py index dcd30a1e55d..8a416404dec 100644 --- a/poetry/inspection/info.py +++ b/poetry/inspection/info.py @@ -63,7 +63,7 @@ def __init__( platform=None, # type: Optional[str] requires_dist=None, # type: Optional[List[str]] requires_python=None, # type: Optional[str] - files=None, # type: Optional[List[str]] + files=None, # type: Optional[List[Dict[str, str]]] cache_version=None, # type: Optional[str] ): self.name = name From 51d01e3e3839b0f95291229837ccb9d9a00dbb88 Mon Sep 17 00:00:00 2001 From: Adrian Wilkins Date: Tue, 21 Sep 2021 10:15:32 +0100 Subject: [PATCH 2/5] Test that executor checks all kinds of hashes --- tests/installation/test_executor.py | 101 ++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/tests/installation/test_executor.py b/tests/installation/test_executor.py index bb659321d0f..db855cf39f3 100644 --- a/tests/installation/test_executor.py +++ b/tests/installation/test_executor.py @@ -11,6 +11,7 @@ from poetry.config.config import Config from poetry.core.packages.package import Package +from poetry.core.packages.utils.link import Link from poetry.installation.executor import Executor from poetry.installation.operations import Install from poetry.installation.operations import Uninstall @@ -56,6 +57,106 @@ def callback(request, uri, headers): ) +try: + from hashlib import algorithms_guaranteed as ALGORITHMS_GUARANTEED +except ImportError: + ALGORITHMS_GUARANTEED = "md5,sha1,sha224,sha256,sha384,sha512".split(",") + + +@pytest.mark.parametrize( + "hash_name,expected", + [ + (hash_name, value) + for hash_name, value in [ + ("sha224", "1c6ac3b371ed8af9c8db514f237c4c4696eca78c4766eb7a392c6205"), + ( + "sha3_512", + "6da91528a686799b0a8d3a582b5da50d0ae75eb45adf1f8f4b96b84279a777dc8fdfa4309972f859dbb343d6e69af6f367377a6fac75e1941dfc897fae6a9ff9", + ), + ( + "blake2s", + "4508e2ff4a93b3ca4e0cd4b61348ddd2066a740c0a7c44692ad30eed3128ce12", + ), + ( + "sha3_384", + "2a0e744f2d15938e35bf1d0350edf8f03f9dc507bee74dc9823626d72210c4d0df4099fc9ba70133f7f72d6e7d2ea598", + ), + ( + "blake2b", + "211843c91d73a96e584bddf3e1b9e6915903a98daf36b181643b20590af1fe3a8cc62856e42126c34f89f293cf836c41e27af29a97678744ef9f1a56f10f89ad", + ), + ( + "sha256", + "70e704135718fffbcbf61ed1fc45933cfd86951a744b681000eaaa75da31f17a", + ), + ( + "sha512", + "44cfa6b8696e5008c1c4dfe32c52a8b6511c261d8f3a25882366c0718eb42a17da5e4b9196623597661871be65c48e613d8f5c50a5c5c827e0efbf8b612fe5be", + ), + ( + "sha384", + "55daa98dcf37a699df770a9c94c4ee50dd155137152c018fcbaa607d879e24b50abb6fd19d7a4f8b5ef0ae7ecf0359dc", + ), + ("sha3_224", "757f3dfe2159e25e3994e8935604448d25d11bf99b9730663ca85cc9"), + ("sha1", "24ad37bd0efbb1e66ed888a3429ad13a717b2ec4"), + ( + "sha3_256", + "a5acd9ee8f486b3d6b4a7c4b9f65ec0915028c91997ff0a55cd5fcc4322a5da8", + ), + ] + if hash_name in ALGORITHMS_GUARANTEED + ], +) +def test_download_link_checks_hash( + config, pool, io, tmp_dir, mock_file_downloads, hash_name, expected +): + config = Config() + config.merge({"cache-dir": tmp_dir}) + + env = MockEnv(path=Path(tmp_dir)) + + executor = Executor(env, pool, config, io) + + sha_package = Package(name="demo", version="0.1.0",) + + sha_package.files.append( + { + "file": "demo-0.1.0-py2.py3-none-any.whl", + "hash": "{}:{}".format(hash_name, expected), + } + ) + + install_operation = Install(sha_package) + link = Link("https://files.pythonhosted.org/simple/demo-0.1.0-py2.py3-none-any.whl") + + # Test that this works without exception + executor._download_link(install_operation, link) + + # Then spoil the hash and check that it fails + def ruin_hash(hash): + return hash[1:] + hash[0] + + sha_package = Package(name="demo", version="0.1.0",) + + sha_package.files.append( + { + "file": "demo-0.1.0-py2.py3-none-any.whl", + "hash": "{}:{}".format(hash_name, ruin_hash(expected)), + } + ) + + install_operation = Install(sha_package) + link = Link("https://files.pythonhosted.org/simple/demo-0.1.0-py2.py3-none-any.whl") + + with pytest.raises( + RuntimeError, + match="Invalid {} hash for demo \\(0.1.0\\) using file demo-0.1.0-py2.py3-none-any.whl".format( + hash_name + ), + ): + executor._download_link(install_operation, link) + + def test_execute_executes_a_batch_of_operations( config, pool, io, tmp_dir, mock_file_downloads ): From d80d910f0d3d42c239934e661ad1203f1a0df7b9 Mon Sep 17 00:00:00 2001 From: Adrian Wilkins Date: Tue, 21 Sep 2021 10:21:41 +0100 Subject: [PATCH 3/5] Check hash regardless of type --- poetry/installation/executor.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/poetry/installation/executor.py b/poetry/installation/executor.py index ba117b3a7ad..76b68a42110 100644 --- a/poetry/installation/executor.py +++ b/poetry/installation/executor.py @@ -608,17 +608,20 @@ 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() + dep = FileDependency( + package.name, + Path(archive.path) if isinstance(archive, Link) else archive, ) - if archive_hash not in {f["hash"] for f in package.files}: - raise RuntimeError( - "Invalid hash for {} using archive {}".format(package, archive.name) - ) + for file in package.files: + hash_name, expected_digest = file["hash"].split(":") + hsh = dep.hash(hash_name) + if hsh != expected_digest: + print(hsh) + raise RuntimeError( + "Invalid {} hash for {} using file {}\n{}".format( + hash_name, package, link.filename, hsh + ) + ) return archive From 83b5366cd69c1e27e3ffb3c36653b93658dde24f Mon Sep 17 00:00:00 2001 From: Adrian Wilkins Date: Tue, 21 Sep 2021 10:23:29 +0100 Subject: [PATCH 4/5] This change requires a higher version of core NB : lockfile not updated, because it's not published yet.. --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index cfd0c83d983..ef2cb52d260 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "poetry" -version = "1.1.9" +version = "1.1.10-alpha.0" description = "Python dependency management and packaging made easy." authors = [ "Sébastien Eustace " @@ -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" } From 8fd52e4cb475aa0fefa90616b49c5d08dc2f7189 Mon Sep 17 00:00:00 2001 From: Adrian Wilkins Date: Tue, 21 Sep 2021 10:32:48 +0100 Subject: [PATCH 5/5] Link to git instead for the time being --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index ef2cb52d260..698c3a4da83 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ classifiers = [ [tool.poetry.dependencies] python = "~2.7 || ^3.5" -poetry-core = "~1.0.6" +poetry-core = { git = "https://github.com/python-poetry/poetry-core.git", branch = "1.0" } cleo = "^0.8.1" clikit = "^0.6.2" crashtest = { version = "^0.3.0", python = "^3.6" }