From 77217fdd24c399d02b01d0b504d587c27077bb1b Mon Sep 17 00:00:00 2001 From: Matt Martz Date: Tue, 12 Mar 2019 12:39:02 -0500 Subject: [PATCH] Fix checksum file parsing in get_url (#53685) * Fix checksum file parsing. Fixes #48790 * guard invalid int conversion Co-Authored-By: sivel * Remove extra newline --- changelogs/fragments/get_url-checksum.yaml | 4 ++ .../modules/net_tools/basics/get_url.py | 58 +++++++++++-------- 2 files changed, 37 insertions(+), 25 deletions(-) create mode 100644 changelogs/fragments/get_url-checksum.yaml diff --git a/changelogs/fragments/get_url-checksum.yaml b/changelogs/fragments/get_url-checksum.yaml new file mode 100644 index 00000000000..7bd836bf3fe --- /dev/null +++ b/changelogs/fragments/get_url-checksum.yaml @@ -0,0 +1,4 @@ +bugfixes: +- get_url - Fix issue with checksum validation when using a file to ensure we skip lines in the file that + do not contain exactly 2 parts. Also restrict exception handling to the minimum number of + necessary lines (https://github.com/ansible/ansible/issues/48790) diff --git a/lib/ansible/modules/net_tools/basics/get_url.py b/lib/ansible/modules/net_tools/basics/get_url.py index 2c8ce8375a2..fc26066cf93 100644 --- a/lib/ansible/modules/net_tools/basics/get_url.py +++ b/lib/ansible/modules/net_tools/basics/get_url.py @@ -487,34 +487,42 @@ def main(): if checksum: try: algorithm, checksum = checksum.split(':', 1) - if checksum.startswith('http://') or checksum.startswith('https://') or checksum.startswith('ftp://'): - checksum_url = checksum - # download checksum file to checksum_tmpsrc - checksum_tmpsrc, checksum_info = url_get(module, checksum_url, dest, use_proxy, last_mod_time, force, timeout, headers, tmp_dest) - with open(checksum_tmpsrc) as f: - lines = [line.rstrip('\n') for line in f] - os.remove(checksum_tmpsrc) - lines = dict(s.split(None, 1) for s in lines) - filename = url_filename(url) - - # Look through each line in the checksum file for a hash corresponding to - # the filename in the url, returning the first hash that is found. - for cksum in (s for (s, f) in lines.items() if f.strip('./') == filename): - checksum = cksum - break - else: - checksum = None - - if checksum is None: - module.fail_json(msg="Unable to find a checksum for file '%s' in '%s'" % (filename, checksum_url)) - # Remove any non-alphanumeric characters, including the infamous - # Unicode zero-width space - checksum = re.sub(r'\W+', '', checksum).lower() - # Ensure the checksum portion is a hexdigest - int(checksum, 16) except ValueError: module.fail_json(msg="The checksum parameter has to be in format :", **result) + if checksum.startswith('http://') or checksum.startswith('https://') or checksum.startswith('ftp://'): + checksum_url = checksum + # download checksum file to checksum_tmpsrc + checksum_tmpsrc, checksum_info = url_get(module, checksum_url, dest, use_proxy, last_mod_time, force, timeout, headers, tmp_dest) + with open(checksum_tmpsrc) as f: + lines = [line.rstrip('\n') for line in f] + os.remove(checksum_tmpsrc) + checksum_map = {} + for line in lines: + parts = line.split(None, 1) + if len(parts) == 2: + checksum_map[parts[0]] = parts[1] + filename = url_filename(url) + + # Look through each line in the checksum file for a hash corresponding to + # the filename in the url, returning the first hash that is found. + for cksum in (s for (s, f) in checksum_map.items() if f.strip('./') == filename): + checksum = cksum + break + else: + checksum = None + + if checksum is None: + module.fail_json(msg="Unable to find a checksum for file '%s' in '%s'" % (filename, checksum_url)) + # Remove any non-alphanumeric characters, including the infamous + # Unicode zero-width space + checksum = re.sub(r'\W+', '', checksum).lower() + # Ensure the checksum portion is a hexdigest + try: + int(checksum, 16) + except ValueError: + module.fail_json(msg='The checksum format is invalid', **result) + if not dest_is_dir and os.path.exists(dest): checksum_mismatch = False