Skip to content

Commit f8a55c5

Browse files
committed
Ensure all accepted hash types are checked
1 parent 5bea4d9 commit f8a55c5

File tree

4 files changed

+120
-147
lines changed

4 files changed

+120
-147
lines changed

poetry.lock

+19-137
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

poetry/installation/executor.py

+23-9
Original file line numberDiff line numberDiff line change
@@ -608,16 +608,30 @@ def _download_link(self, operation, link):
608608
archive = self._chef.prepare(archive)
609609

610610
if package.files:
611-
archive_hash = (
612-
"sha256:"
613-
+ FileDependency(
614-
package.name,
615-
Path(archive.path) if isinstance(archive, Link) else archive,
616-
).hash()
617-
)
618-
if archive_hash not in {f["hash"] for f in package.files}:
611+
hashes = {f["hash"] for f in package.files}
612+
hash_types = {h.split(":")[0] for h in hashes}
613+
archive_hashes = set()
614+
for hash_type in hash_types:
615+
archive_hashes.add(
616+
"{}:{}".format(
617+
hash_type,
618+
FileDependency(
619+
package.name,
620+
Path(archive.path)
621+
if isinstance(archive, Link)
622+
else archive,
623+
).hash(hash_type),
624+
)
625+
)
626+
627+
if archive_hashes.isdisjoint(hashes):
619628
raise RuntimeError(
620-
"Invalid hash for {} using archive {}".format(package, archive.name)
629+
"Invalid hashes ({}) for {} using archive {}. Expected one of {}.".format(
630+
", ".join(sorted(archive_hashes)),
631+
package,
632+
archive.name,
633+
", ".join(sorted(hashes)),
634+
)
621635
)
622636

623637
return archive

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ classifiers = [
2424
[tool.poetry.dependencies]
2525
python = "~2.7 || ^3.5"
2626

27-
poetry-core = "~1.0.5"
27+
poetry-core = { git = "https://github.com/awilkins/poetry-core.git", branch = "fix/support-guaranteed-hashes" }
2828
cleo = "^0.8.1"
2929
clikit = "^0.6.2"
3030
crashtest = { version = "^0.3.0", python = "^3.6" }

tests/installation/test_executor.py

+77
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
from poetry.config.config import Config
1313
from poetry.core.packages.package import Package
14+
from poetry.core.packages.utils.link import Link
15+
from poetry.installation.chef import Chef
1416
from poetry.installation.executor import Executor
1517
from poetry.installation.operations import Install
1618
from poetry.installation.operations import Uninstall
@@ -251,3 +253,78 @@ def test_executor_should_delete_incomplete_downloads(
251253
executor._download(Install(Package("tomlkit", "0.5.3")))
252254

253255
assert not destination_fixture.exists()
256+
257+
258+
def test_executor_should_check_every_possible_hash_types(
259+
config, io, pool, mocker, fixture_dir, tmp_dir
260+
):
261+
mocker.patch.object(
262+
Chef, "get_cached_archive_for_link", side_effect=lambda link: link,
263+
)
264+
mocker.patch.object(
265+
Executor,
266+
"_download_archive",
267+
return_value=fixture_dir("distributions").joinpath(
268+
"demo-0.1.0-py2.py3-none-any.whl"
269+
),
270+
)
271+
272+
env = MockEnv(path=Path(tmp_dir))
273+
executor = Executor(env, pool, config, io)
274+
275+
package = Package("demo", "0.1.0")
276+
package.files = [
277+
{
278+
"file": "demo-0.1.0-py2.py3-none-any.whl",
279+
"hash": "md5:15507846fd4299596661d0197bfb4f90",
280+
}
281+
]
282+
283+
archive = executor._download_link(
284+
Install(package), Link("https://example.com/demo-0.1.0-py2.py3-none-any.whl")
285+
)
286+
287+
assert archive == fixture_dir("distributions").joinpath(
288+
"demo-0.1.0-py2.py3-none-any.whl"
289+
)
290+
291+
292+
def test_executor_should_check_every_possible_hash_types_before_failing(
293+
config, io, pool, mocker, fixture_dir, tmp_dir
294+
):
295+
mocker.patch.object(
296+
Chef, "get_cached_archive_for_link", side_effect=lambda link: link,
297+
)
298+
mocker.patch.object(
299+
Executor,
300+
"_download_archive",
301+
return_value=fixture_dir("distributions").joinpath(
302+
"demo-0.1.0-py2.py3-none-any.whl"
303+
),
304+
)
305+
306+
env = MockEnv(path=Path(tmp_dir))
307+
executor = Executor(env, pool, config, io)
308+
309+
package = Package("demo", "0.1.0")
310+
package.files = [
311+
{"file": "demo-0.1.0-py2.py3-none-any.whl", "hash": "md5:123456"},
312+
{"file": "demo-0.1.0-py2.py3-none-any.whl", "hash": "sha256:123456"},
313+
]
314+
315+
with pytest.raises(RuntimeError) as e:
316+
executor._download_link(
317+
Install(package),
318+
Link("https://example.com/demo-0.1.0-py2.py3-none-any.whl"),
319+
)
320+
321+
expected_message = (
322+
"Invalid hashes "
323+
"("
324+
"md5:15507846fd4299596661d0197bfb4f90, "
325+
"sha256:70e704135718fffbcbf61ed1fc45933cfd86951a744b681000eaaa75da31f17a"
326+
") "
327+
"for demo (0.1.0) using archive demo-0.1.0-py2.py3-none-any.whl. "
328+
"Expected one of md5:123456, sha256:123456."
329+
)
330+
assert str(e.value) == expected_message

0 commit comments

Comments
 (0)