From 866922f80326671815c9187eed0ba6c4127b8721 Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Wed, 11 May 2022 10:23:59 +0200 Subject: properly handle percent-encoded characters in .deb file names --- src/hydrilla/builder/local_apt.py | 25 ++++++++++++------------- tests/test_local_apt.py | 39 +++++++++++++++++++++++---------------- 2 files changed, 35 insertions(+), 29 deletions(-) diff --git a/src/hydrilla/builder/local_apt.py b/src/hydrilla/builder/local_apt.py index 4c93a4d..eee028d 100644 --- a/src/hydrilla/builder/local_apt.py +++ b/src/hydrilla/builder/local_apt.py @@ -35,6 +35,7 @@ CP = subprocess.CompletedProcess from pathlib import Path, PurePosixPath from tempfile import TemporaryDirectory, NamedTemporaryFile from hashlib import sha256 +from urllib.parse import unquote from contextlib import contextmanager from typing import Optional, Iterable @@ -296,25 +297,20 @@ def download_apt_packages(list: SourcesList, keys: [str], packages: [str], Return value is a list of names of all downloaded files. """ + install_line_regex = re.compile(r'^Inst (?P\S+) \((?P\S+) ') + with local_apt(list, keys) as apt: if with_deps: cp = apt.get('install', '--yes', '--just-print', *packages) - deps_listing = re.match( - r''' - .* - The\sfollowing\sNEW\spackages\swill\sbe\sinstalled: - (.*) - 0\supgraded, - ''', - cp.stdout, - re.MULTILINE | re.DOTALL | re.VERBOSE) + lines = cp.stdout.split('\n') + matches = [install_line_regex.match(l) for l in lines] + packages = [f'{m.group("name")}={m.group("version")}' + for m in matches if m] - if deps_listing is None: + if not packages: raise AptError(_('apt_install_output_not_understood'), cp) - packages = deps_listing.group(1).split() - # Download .debs to indirectly to destination_dir by first placing them # in a temporary subdirectory. with TemporaryDirectory(dir=destination_dir) as td: @@ -343,7 +339,10 @@ def download_apt_packages(list: SourcesList, keys: [str], packages: [str], .format(deb_file.name) raise AptError(msg, cp) - names_vers.append((match.group('name'), match.group('ver'))) + names_vers.append(( + unquote(match.group('name')), + unquote(match.group('ver')) + )) downloaded.append(deb_file.name) apt.get('source', '--download-only', diff --git a/tests/test_local_apt.py b/tests/test_local_apt.py index 1ff52cc..6ba2a06 100644 --- a/tests/test_local_apt.py +++ b/tests/test_local_apt.py @@ -128,10 +128,10 @@ Suggested packages: The following NEW packages will be installed: fonts-mathjax libjs-mathjax 0 upgraded, 2 newly installed, 0 to remove and 0 not upgraded. -Inst fonts-mathjax (2.7.9+dfsg-1 Devuan:4.0/stable, Devuan:1.0.0/unstable [all]) -Inst libjs-mathjax (2.7.9+dfsg-1 Devuan:4.0/stable, Devuan:1.0.0/unstable [all]) -Conf fonts-mathjax (2.7.9+dfsg-1 Devuan:4.0/stable, Devuan:1.0.0/unstable [all]) -Conf libjs-mathjax (2.7.9+dfsg-1 Devuan:4.0/stable, Devuan:1.0.0/unstable [all]) +Inst fonts-mathjax (0:2.7.9+dfsg-1 Devuan:4.0/stable, Devuan:1.0.0/unstable [all]) +Inst libjs-mathjax (0:2.7.9+dfsg-1 Devuan:4.0/stable, Devuan:1.0.0/unstable [all]) +Conf fonts-mathjax (0:2.7.9+dfsg-1 Devuan:4.0/stable, Devuan:1.0.0/unstable [all]) +Conf libjs-mathjax (0:2.7.9+dfsg-1 Devuan:4.0/stable, Devuan:1.0.0/unstable [all]) ''' def run_apt_get_install(command, returncode=0, **kwargs): @@ -153,17 +153,24 @@ def run_apt_get_download(command, returncode=0, **kwargs): Instead of running an 'apt-get download' command just write some dummy .deb to the appropriate directory. """ - expected = ['apt-get', '-c', '', 'download', 'libjs-mathjax'] - if 'fonts-mathjax' in command: - expected.insert(-1, 'fonts-mathjax') + expected = ['apt-get', '-c', '', 'download'] + if 'libjs-mathjax' in command: + expected.append('libjs-mathjax') + else: + expected.append('fonts-mathjax=0:2.7.9+dfsg-1') + expected.append('libjs-mathjax=0:2.7.9+dfsg-1') conf_path = Path(process_run_args(command, kwargs, expected)['conf_path']) destination = Path(kwargs.get('cwd') or Path.cwd()) + package_name_regex = re.compile(r'^[^=]+-mathjax') + for word in expected: - if word.endswith('-mathjax'): - deb_path = destination / f'{word}_2.7.9+dfsg-1_all.deb' + match = package_name_regex.match(word) + if match: + filename = f'{match.group(0)}_0%3a2.7.9+dfsg-1_all.deb' + deb_path = destination / filename deb_path.write_text(f'dummy {deb_path.name}') return MockedCompletedProcess(command, returncode, @@ -175,12 +182,12 @@ def run_apt_get_source(command, returncode=0, **kwargs): "tarballs" to the appropriate directory. """ expected = ['apt-get', '-c', '', 'source', - '--download-only', 'libjs-mathjax=2.7.9+dfsg-1'] - if 'fonts-mathjax=2.7.9+dfsg-1' in command: - if command[-1] == 'fonts-mathjax=2.7.9+dfsg-1': - expected.append('fonts-mathjax=2.7.9+dfsg-1') + '--download-only', 'libjs-mathjax=0:2.7.9+dfsg-1'] + if 'fonts-mathjax=0:2.7.9+dfsg-1' in command: + if command[-1] == 'fonts-mathjax=0:2.7.9+dfsg-1': + expected.append('fonts-mathjax=0:2.7.9+dfsg-1') else: - expected.insert(-1, 'fonts-mathjax=2.7.9+dfsg-1') + expected.insert(-1, 'fonts-mathjax=0:2.7.9+dfsg-1') destination = Path(kwargs.get('cwd') or Path.cwd()) for filename in [ @@ -335,8 +342,8 @@ def test_local_apt_download(mock_cache_dir): local_apt.download_apt_packages(sources_list, local_apt.default_keys, ['libjs-mathjax'], destination) - libjs_mathjax_path = destination / 'libjs-mathjax_2.7.9+dfsg-1_all.deb' - fonts_mathjax_path = destination / 'fonts-mathjax_2.7.9+dfsg-1_all.deb' + libjs_mathjax_path = destination / 'libjs-mathjax_0%3a2.7.9+dfsg-1_all.deb' + fonts_mathjax_path = destination / 'fonts-mathjax_0%3a2.7.9+dfsg-1_all.deb' source_paths = [ destination / 'mathjax_2.7.9+dfsg-1.debian.tar.xz', -- cgit v1.2.3