Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package ansible-core-2.19 for openSUSE:Factory checked in at 2026-09-18 22:03:43 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/ansible-core-2.19 (Old) and /work/SRC/openSUSE:Factory/.ansible-core-2.19.new.383539 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "ansible-core-2.19" Fri Sep 18 22:03:43 2026 rev:10 rq:1378503 version:2.19.13 Changes: -------- --- /work/SRC/openSUSE:Factory/ansible-core-2.19/ansible-core-2.19.changes 2026-08-11 17:18:19.641075795 +0200 +++ /work/SRC/openSUSE:Factory/.ansible-core-2.19.new.383539/ansible-core-2.19.changes 2026-09-18 22:04:26.529321891 +0200 @@ -1,0 +2,27 @@ +Wed Sep 16 05:26:00 UTC 2026 - Johannes Kastl <[email protected]> + +- update to 2.19.13: + https://github.com/ansible/ansible/blob/v2.19.13/changelogs/CHANGELOG-v2.19.rst + * Minor Changes + - mask_url function in module_utils to allow for masking of + auth data embedded in urls. + * Bugfixes + - apt_key module now masks authentication information in all + displays and returns of uri information. + - get_url module now masks authentication information in all + displays and returns of uri information. + - module_utils - mask_url now masks the password in URLs that + contain a password but no username, such as + redis://:password@host, instead of returning them unmasked. + - module_utils.urls now all errors mask in line url + authentication information. + - rpm_key module now masks authentication information in all + displays and returns of uri information. + - tempfile - reject prefix and suffix values that contain path + components to prevent path traversal. + - uri module now masks authentication information in all + displays and returns of uri information. + - url lookup now masks authentication information in all + displays and returns of uri information. + +------------------------------------------------------------------- Old: ---- ansible_core-2.19.12.tar.gz ansible_core-2.19.12.tar.gz.sha256 New: ---- ansible_core-2.19.13.tar.gz ansible_core-2.19.13.tar.gz.sha256 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ ansible-core-2.19.spec ++++++ --- /var/tmp/diff_new_pack.B4r0C6/_old 2026-09-18 22:04:27.864377843 +0200 +++ /var/tmp/diff_new_pack.B4r0C6/_new 2026-09-18 22:04:27.867377969 +0200 @@ -43,7 +43,7 @@ %endif Name: ansible-core-2.19 -Version: 2.19.12 +Version: 2.19.13 Release: 0 Summary: Radically simple IT automation License: GPL-3.0-or-later ++++++ ansible_core-2.19.12.tar.gz -> ansible_core-2.19.13.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.12/PKG-INFO new/ansible_core-2.19.13/PKG-INFO --- old/ansible_core-2.19.12/PKG-INFO 2026-08-10 17:48:28.000000000 +0200 +++ new/ansible_core-2.19.13/PKG-INFO 2026-09-08 18:38:52.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: ansible-core -Version: 2.19.12 +Version: 2.19.13 Summary: Radically simple IT automation Author: Ansible Project Project-URL: Homepage, https://ansible.com/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.12/ansible_core.egg-info/PKG-INFO new/ansible_core-2.19.13/ansible_core.egg-info/PKG-INFO --- old/ansible_core-2.19.12/ansible_core.egg-info/PKG-INFO 2026-08-10 17:48:28.000000000 +0200 +++ new/ansible_core-2.19.13/ansible_core.egg-info/PKG-INFO 2026-09-08 18:38:52.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: ansible-core -Version: 2.19.12 +Version: 2.19.13 Summary: Radically simple IT automation Author: Ansible Project Project-URL: Homepage, https://ansible.com/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.12/ansible_core.egg-info/SOURCES.txt new/ansible_core-2.19.13/ansible_core.egg-info/SOURCES.txt --- old/ansible_core-2.19.12/ansible_core.egg-info/SOURCES.txt 2026-08-10 17:48:28.000000000 +0200 +++ new/ansible_core-2.19.13/ansible_core.egg-info/SOURCES.txt 2026-09-08 18:38:52.000000000 +0200 @@ -4047,6 +4047,7 @@ test/integration/targets/throttle/undefined_throttle.yml test/integration/targets/throttle/group_vars/all.yml test/integration/targets/unarchive/aliases +test/integration/targets/unarchive/auth_mask.yml test/integration/targets/unarchive/runme.sh test/integration/targets/unarchive/runme.yml test/integration/targets/unarchive/test_relative_tmp_dir.yml @@ -5261,6 +5262,7 @@ test/units/module_utils/urls/test_fetch_url.py test/units/module_utils/urls/test_generic_urlparse.py test/units/module_utils/urls/test_gzip.py +test/units/module_utils/urls/test_mask_url.py test/units/module_utils/urls/test_prepare_multipart.py test/units/module_utils/urls/test_split.py test/units/module_utils/urls/test_urls.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.12/changelogs/CHANGELOG-v2.19.rst new/ansible_core-2.19.13/changelogs/CHANGELOG-v2.19.rst --- old/ansible_core-2.19.12/changelogs/CHANGELOG-v2.19.rst 2026-08-10 17:48:28.000000000 +0200 +++ new/ansible_core-2.19.13/changelogs/CHANGELOG-v2.19.rst 2026-09-08 18:38:52.000000000 +0200 @@ -4,6 +4,32 @@ .. contents:: Topics +v2.19.13 +======== + +Release Summary +--------------- + +| Release Date: 2026-09-08 +| `Porting Guide <https://docs.ansible.com/ansible-core/2.19/porting_guides/porting_guide_core_2.19.html>`__ + +Minor Changes +------------- + +- mask_url function in module_utils to allow for masking of auth data embedded in urls. + +Bugfixes +-------- + +- apt_key module now masks authentication information in all displays and returns of uri information. +- get_url module now masks authentication information in all displays and returns of uri information. +- module_utils - ``mask_url`` now masks the password in URLs that contain a password but no username, such as ``redis://:password@host``, instead of returning them unmasked. +- module_utils.urls now all errors mask in line url authentication information. +- rpm_key module now masks authentication information in all displays and returns of uri information. +- tempfile - reject prefix and suffix values that contain path components to prevent path traversal. +- uri module now masks authentication information in all displays and returns of uri information. +- url lookup now masks authentication information in all displays and returns of uri information. + v2.19.12 ======== diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.12/changelogs/changelog.yaml new/ansible_core-2.19.13/changelogs/changelog.yaml --- old/ansible_core-2.19.12/changelogs/changelog.yaml 2026-08-10 17:48:28.000000000 +0200 +++ new/ansible_core-2.19.13/changelogs/changelog.yaml 2026-09-08 18:38:52.000000000 +0200 @@ -1312,6 +1312,52 @@ - ansible-test-target-filtering.yml - fix-short-timeout-async-wrapper.yml release_date: '2026-08-03' + 2.19.13: + changes: + release_summary: '| Release Date: 2026-09-08 + + | `Porting Guide <https://docs.ansible.com/ansible-core/2.19/porting_guides/porting_guide_core_2.19.html>`__ + + ' + codename: What Is and What Should Never Be + fragments: + - 2.19.13_summary.yaml + release_date: '2026-09-08' + 2.19.13rc1: + changes: + bugfixes: + - apt_key module now masks authentication information in all displays and returns + of uri information. + - get_url module now masks authentication information in all displays and returns + of uri information. + - module_utils - ``mask_url`` now masks the password in URLs that contain a + password but no username, such as ``redis://:password@host``, instead of returning + them unmasked. + - module_utils.urls now all errors mask in line url authentication information. + - rpm_key module now masks authentication information in all displays and returns + of uri information. + - tempfile - reject prefix and suffix values that contain path components to + prevent path traversal. + - uri module now masks authentication information in all displays and returns + of uri information. + - url lookup now masks authentication information in all displays and returns + of uri information. + minor_changes: + - mask_url function in module_utils to allow for masking of auth data embedded + in urls. + release_summary: '| Release Date: 2026-08-31 + + | `Porting Guide <https://docs.ansible.com/ansible-core/2.19/porting_guides/porting_guide_core_2.19.html>`__ + + ' + codename: What Is and What Should Never Be + fragments: + - 2.19.13rc1_summary.yaml + - mask_inurl_auth.yml + - mask_url-password-auth.yml + - mask_url.yml + - tempfile-path-traversal.yml + release_date: '2026-08-31' 2.19.1rc1: changes: bugfixes: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.12/lib/ansible/module_utils/ansible_release.py new/ansible_core-2.19.13/lib/ansible/module_utils/ansible_release.py --- old/ansible_core-2.19.12/lib/ansible/module_utils/ansible_release.py 2026-08-10 17:48:28.000000000 +0200 +++ new/ansible_core-2.19.13/lib/ansible/module_utils/ansible_release.py 2026-09-08 18:38:52.000000000 +0200 @@ -17,6 +17,6 @@ from __future__ import annotations -__version__ = '2.19.12' +__version__ = '2.19.13' __author__ = 'Ansible, Inc.' __codename__ = "What Is and What Should Never Be" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.12/lib/ansible/module_utils/urls.py new/ansible_core-2.19.13/lib/ansible/module_utils/urls.py --- old/ansible_core-2.19.12/lib/ansible/module_utils/urls.py 2026-08-10 17:48:28.000000000 +0200 +++ new/ansible_core-2.19.13/lib/ansible/module_utils/urls.py 2026-09-08 18:38:52.000000000 +0200 @@ -49,6 +49,7 @@ import types # pylint: disable=unused-import import urllib.error import urllib.request + from contextlib import contextmanager from http import cookiejar from urllib.parse import unquote, urlparse, urlunparse @@ -316,6 +317,24 @@ return [self.get(k, None) for k in ('scheme', 'netloc', 'path', 'params', 'query', 'fragment')] +def mask_url(url: str) -> str: + """ + Safely display a url by masking confidential data + from a string or the result from urlparse/split + """ + if (parsed_url := urlparse(url)) and not parsed_url.username and not parsed_url.password: + return url + + netloc: str + mask = '****' + if parsed_url.password: + netloc = parsed_url.netloc.replace(f'{parsed_url.username}:{parsed_url.password}@', f'{mask}:{mask}@') + else: + netloc = parsed_url.netloc.replace(f'{parsed_url.username}@', f'{mask}@') + + return urlunparse(parsed_url._replace(netloc=netloc)) + + def generic_urlparse(parts): """ Returns a dictionary of url parts as parsed by urlparse, @@ -1235,7 +1254,7 @@ cookies = cookiejar.CookieJar() r = None - info = dict(url=url, status=-1) + info = dict(url=mask_url(url), status=-1) try: r = open_url(url, data=data, headers=headers, method=method, use_proxy=use_proxy, force=force, last_mod_time=last_mod_time, timeout=timeout, @@ -1272,7 +1291,7 @@ info['cookies'] = cookie_dict # finally update the result with a message about the fetch - info.update(dict(msg="OK (%s bytes)" % r.headers.get('Content-Length', 'unknown'), url=r.geturl(), status=r.code)) + info.update(dict(msg="OK (%s bytes)" % r.headers.get('Content-Length', 'unknown'), url=mask_url(r.geturl()), status=r.code)) except (ConnectionError, ValueError) as e: module.fail_json(msg=to_native(e), **info) except MissingModuleError as e: @@ -1392,14 +1411,14 @@ unredirected_headers=unredirected_headers, decompress=decompress, ciphers=ciphers, ca_path=ca_path, cookies=cookies) if not rsp or (rsp.code and rsp.code >= 400): - module.fail_json(msg="Failure downloading %s, %s" % (url, info['msg'])) + module.fail_json(msg="Failure downloading %s, %s" % (mask_url(url), info['msg'])) data = rsp.read(bufsize) while data: fetch_temp_file.write(data) data = rsp.read(bufsize) fetch_temp_file.close() except Exception as e: - module.fail_json(msg="Failure downloading %s, %s" % (url, to_native(e))) + module.fail_json(msg="Failure downloading %s, %s" % (mask_url(url), to_native(e))) return fetch_temp_file.name diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.12/lib/ansible/modules/apt_key.py new/ansible_core-2.19.13/lib/ansible/modules/apt_key.py --- old/ansible_core-2.19.12/lib/ansible/modules/apt_key.py 2026-08-10 17:48:28.000000000 +0200 +++ new/ansible_core-2.19.13/lib/ansible/modules/apt_key.py 2026-09-08 18:38:52.000000000 +0200 @@ -175,7 +175,7 @@ from ansible.module_utils.common.text.converters import to_native from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.common.locale import get_best_parsable_locale -from ansible.module_utils.urls import fetch_url +from ansible.module_utils.urls import fetch_url, mask_url apt_key_bin = None @@ -313,11 +313,11 @@ # note: validate_certs and other args are pulled from module directly rsp, info = fetch_url(module, url, use_proxy=True) if info['status'] != 200: - module.fail_json(msg="Failed to download key at %s: %s" % (url, info['msg'])) + module.fail_json(msg="Failed to download key at %s: %s" % (mask_url(url), info['msg'])) return rsp.read() except Exception: - module.fail_json(msg=f"Error getting key id from url: {url}") + module.fail_json(msg=f"Error getting key id from url: {mask_url(url)}") def get_key_id_from_file(module, filename, data=None): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.12/lib/ansible/modules/get_url.py new/ansible_core-2.19.13/lib/ansible/modules/get_url.py --- old/ansible_core-2.19.12/lib/ansible/modules/get_url.py 2026-08-10 17:48:28.000000000 +0200 +++ new/ansible_core-2.19.13/lib/ansible/modules/get_url.py 2026-09-08 18:38:52.000000000 +0200 @@ -378,7 +378,7 @@ from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.six.moves.urllib.parse import urlsplit from ansible.module_utils.common.text.converters import to_native -from ansible.module_utils.urls import fetch_url, url_argument_spec +from ansible.module_utils.urls import fetch_url, url_argument_spec, mask_url # ============================================================== # url handling @@ -405,14 +405,14 @@ elapsed = (datetime.now(timezone.utc) - start).seconds if info['status'] == 304: - module.exit_json(url=url, dest=dest, changed=False, msg=info.get('msg', ''), status_code=info['status'], elapsed=elapsed) + module.exit_json(url=mask_url(url), dest=dest, changed=False, msg=info.get('msg', ''), status_code=info['status'], elapsed=elapsed) # Exceptions in fetch_url may result in a status -1, the ensures a proper error to the user in all cases if info['status'] == -1: - module.fail_json(msg=info['msg'], url=url, dest=dest, elapsed=elapsed) + module.fail_json(msg=info['msg'], url=mask_url(url), dest=dest, elapsed=elapsed) if info['status'] != 200 and not url.startswith('file:/') and not (url.startswith('ftp:/') and info.get('msg', '').startswith('OK')): - module.fail_json(msg="Request failed", status_code=info['status'], response=info['msg'], url=url, dest=dest, elapsed=elapsed) + module.fail_json(msg="Request failed", status_code=info['status'], response=info['msg'], url=mask_url(url), dest=dest, elapsed=elapsed) # create a temporary file and copy content to do checksum-based replacement if tmp_dest: @@ -555,7 +555,7 @@ checksum_src=None, dest=dest, elapsed=0, - url=url, + url=mask_url(url), ) dest_is_dir = os.path.isdir(dest) @@ -587,7 +587,7 @@ checksum = None if checksum is None: - module.fail_json(msg="Unable to find a checksum for file '%s' in '%s'" % (filename, checksum_url)) + module.fail_json(msg="Unable to find a checksum for file '%s' in '%s'" % (filename, mask_url(checksum_url))) # Remove any non-alphanumeric characters, including the infamous # Unicode zero-width space checksum = re.sub(r'\W+', '', checksum).lower() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.12/lib/ansible/modules/rpm_key.py new/ansible_core-2.19.13/lib/ansible/modules/rpm_key.py --- old/ansible_core-2.19.12/lib/ansible/modules/rpm_key.py 2026-08-10 17:48:28.000000000 +0200 +++ new/ansible_core-2.19.13/lib/ansible/modules/rpm_key.py 2026-09-08 18:38:52.000000000 +0200 @@ -91,7 +91,7 @@ # import module snippets from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.urls import fetch_url +from ansible.module_utils.urls import fetch_url, mask_url from ansible.module_utils.common.text.converters import to_native @@ -165,11 +165,11 @@ """Downloads a key from url, returns a valid path to a gpg key""" rsp, info = fetch_url(self.module, url) if info['status'] != 200: - self.module.fail_json(msg="failed to fetch key at %s , error was: %s" % (url, info['msg'])) + self.module.fail_json(msg="failed to fetch key at %s , error was: %s" % (mask_url(url), info['msg'])) key = rsp.read() if not is_pubkey(key): - self.module.fail_json(msg="Not a public key: %s" % url) + self.module.fail_json(msg="Not a public key: %s" % mask_url(url)) tmpfd, tmpname = tempfile.mkstemp() self.module.add_cleanup_file(tmpname) with os.fdopen(tmpfd, "w+b") as tmpfile: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.12/lib/ansible/modules/tempfile.py new/ansible_core-2.19.13/lib/ansible/modules/tempfile.py --- old/ansible_core-2.19.12/lib/ansible/modules/tempfile.py 2026-08-10 17:48:28.000000000 +0200 +++ new/ansible_core-2.19.13/lib/ansible/modules/tempfile.py 2026-09-08 18:38:52.000000000 +0200 @@ -53,6 +53,8 @@ - module: ansible.windows.win_tempfile author: - Krzysztof Magosa (@krzysztof-magosa) +notes: + - O(prefix) and O(suffix) must be file name components and must not contain path separators. """ EXAMPLES = """ @@ -88,7 +90,7 @@ sample: "/tmp/ansible.bMlvdk" """ -from os import close +import os from tempfile import mkstemp, mkdtemp from ansible.module_utils.basic import AnsibleModule @@ -105,18 +107,26 @@ ), ) + prefix = module.params['prefix'] + suffix = module.params['suffix'] + + if prefix and prefix != os.path.basename(prefix): + module.fail_json(msg=f"prefix must be a valid file name component, got {prefix!r}") + if suffix and suffix != os.path.basename(suffix): + module.fail_json(msg=f"suffix must be a valid file name component, got {suffix!r}") + try: if module.params['state'] == 'file': handle, path = mkstemp( - prefix=module.params['prefix'], - suffix=module.params['suffix'], + prefix=prefix, + suffix=suffix, dir=module.params['path'], ) - close(handle) + os.close(handle) else: path = mkdtemp( - prefix=module.params['prefix'], - suffix=module.params['suffix'], + prefix=prefix, + suffix=suffix, dir=module.params['path'], ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.12/lib/ansible/modules/uri.py new/ansible_core-2.19.13/lib/ansible/modules/uri.py --- old/ansible_core-2.19.12/lib/ansible/modules/uri.py 2026-08-10 17:48:28.000000000 +0200 +++ new/ansible_core-2.19.13/lib/ansible/modules/uri.py 2026-09-08 18:38:52.000000000 +0200 @@ -448,6 +448,7 @@ from ansible.module_utils.urls import ( fetch_url, get_response_filename, + mask_url, parse_content_type, prepare_multipart, url_argument_spec, @@ -636,6 +637,9 @@ ciphers = module.params['ciphers'] use_netrc = module.params['use_netrc'] + # for errors and to compare to fetched responses + masked_url = mask_url(url) + if not re.match('^[A-Z]+$', method): module.fail_json(msg="Parameter 'method' needs to be a single word in uppercase, like GET or POST.") @@ -717,14 +721,14 @@ # may have been stored in the info as 'body' content = info.pop('body', b'') except http.client.HTTPException as http_err: - module.fail_json(msg=f"HTTP Error while fetching {url}: {to_native(http_err)}") + module.fail_json(msg=f"HTTP Error while fetching {masked_url}: {to_native(http_err)}") elif r: content = r else: content = None resp = {} - resp['redirected'] = info['url'] != url + resp['redirected'] = info['url'] != masked_url resp.update(info) resp['elapsed'] = elapsed @@ -752,7 +756,7 @@ uresp[ukey] = value if 'location' in uresp: - uresp['location'] = urljoin(url, uresp['location']) + uresp['location'] = urljoin(masked_url, uresp['location']) # Default content_encoding to try if isinstance(content, binary_type): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.12/lib/ansible/plugins/action/unarchive.py new/ansible_core-2.19.13/lib/ansible/plugins/action/unarchive.py --- old/ansible_core-2.19.12/lib/ansible/plugins/action/unarchive.py 2026-08-10 17:48:28.000000000 +0200 +++ new/ansible_core-2.19.13/lib/ansible/plugins/action/unarchive.py 2026-09-08 18:38:52.000000000 +0200 @@ -21,6 +21,7 @@ from ansible.errors import AnsibleActionFail, AnsibleActionSkip from ansible.module_utils.parsing.convert_bool import boolean +from ansible.module_utils.urls import mask_url from ansible.plugins.action import ActionBase @@ -64,10 +65,15 @@ raise AnsibleActionSkip("skipped, since %s exists" % creates) dest = self._remote_expand_user(dest) # CCTODO: Fix path for Windows hosts. - source = os.path.expanduser(source) + # if not remote, we need to get source at controller if not remote_src: - source = self._loader.get_real_file(self._find_needle('files', source), decrypt=decrypt) + if '://' in source: + # TODO: implement using open_url? + raise AnsibleActionFail(f"Unsupported option, an URI src ({mask_url(source)}) is only supported when remote_src is True") + else: + source = os.path.expanduser(source) + source = self._loader.get_real_file(self._find_needle('files', source), decrypt=decrypt) remote_stat = self._execute_remote_stat(dest, all_vars=task_vars, follow=True) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.12/lib/ansible/plugins/lookup/url.py new/ansible_core-2.19.13/lib/ansible/plugins/lookup/url.py --- old/ansible_core-2.19.12/lib/ansible/plugins/lookup/url.py 2026-08-10 17:48:28.000000000 +0200 +++ new/ansible_core-2.19.13/lib/ansible/plugins/lookup/url.py 2026-09-08 18:38:52.000000000 +0200 @@ -211,8 +211,8 @@ from urllib.error import HTTPError, URLError from ansible.errors import AnsibleError -from ansible.module_utils.common.text.converters import to_text, to_native -from ansible.module_utils.urls import open_url, ConnectionError, SSLValidationError +from ansible.module_utils.common.text.converters import to_text +from ansible.module_utils.urls import open_url, mask_url, ConnectionError, SSLValidationError from ansible.plugins.lookup import LookupBase from ansible.utils.display import Display @@ -227,7 +227,7 @@ ret = [] for term in terms: - display.vvvv("url lookup connecting to %s" % term) + display.vvvv("url lookup connecting to %s" % mask_url(term)) if self.get_option('follow_redirects') in ('yes', 'no'): display.deprecated( msg="Using 'yes' or 'no' for 'follow_redirects' parameter is deprecated.", @@ -253,13 +253,13 @@ use_netrc=self.get_option('use_netrc') ) except HTTPError as e: - raise AnsibleError("Received HTTP error for %s : %s" % (term, to_native(e))) + raise AnsibleError(f"Received HTTP error for {mask_url(term)}") from e except URLError as e: - raise AnsibleError("Failed lookup url for %s : %s" % (term, to_native(e))) + raise AnsibleError(f"Failed lookup url for {mask_url(term)}") from e except SSLValidationError as e: - raise AnsibleError("Error validating the server's certificate for %s: %s" % (term, to_native(e))) + raise AnsibleError(f"Error validating the server's certificate for {mask_url(term)}") from e except ConnectionError as e: - raise AnsibleError("Error connecting to %s: %s" % (term, to_native(e))) + raise AnsibleError(f"Error connecting to {mask_url(term)}") from e if self.get_option('split_lines'): for line in response.read().splitlines(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.12/lib/ansible/release.py new/ansible_core-2.19.13/lib/ansible/release.py --- old/ansible_core-2.19.12/lib/ansible/release.py 2026-08-10 17:48:28.000000000 +0200 +++ new/ansible_core-2.19.13/lib/ansible/release.py 2026-09-08 18:38:52.000000000 +0200 @@ -17,6 +17,6 @@ from __future__ import annotations -__version__ = '2.19.12' +__version__ = '2.19.13' __author__ = 'Ansible, Inc.' __codename__ = "What Is and What Should Never Be" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.12/test/integration/targets/get_url/tasks/main.yml new/ansible_core-2.19.13/test/integration/targets/get_url/tasks/main.yml --- old/ansible_core-2.19.12/test/integration/targets/get_url/tasks/main.yml 2026-08-10 17:48:28.000000000 +0200 +++ new/ansible_core-2.19.13/test/integration/targets/get_url/tasks/main.yml 2026-09-08 18:38:52.000000000 +0200 @@ -835,3 +835,15 @@ - assert: that: - get_dir_filename.dest == remote_tmp_dir ~ "/filename.json" + +- name: Test auth fail downloading to dir + get_url: + url: 'https://secretuser:secretpassword@{{ httpbin_host }}/nonexistant' + dest: "{{ remote_tmp_dir }}" + ignore_errors: true + register: auth_error + +- name: Ensure secret is not visible + assert: + that: + - "'secret' not in auth_error|to_json" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.12/test/integration/targets/tempfile/tasks/main.yml new/ansible_core-2.19.13/test/integration/targets/tempfile/tasks/main.yml --- old/ansible_core-2.19.12/test/integration/targets/tempfile/tasks/main.yml 2026-08-10 17:48:28.000000000 +0200 +++ new/ansible_core-2.19.13/test/integration/targets/tempfile/tasks/main.yml 2026-09-08 18:38:52.000000000 +0200 @@ -35,6 +35,36 @@ register: temp_dir_non_existent_path ignore_errors: yes +- name: Reject path components in prefix for file + tempfile: + path: "{{ remote_tmp_dir }}" + prefix: "../../nested" + register: temp_file_invalid_prefix + ignore_errors: yes + +- name: Reject path components in suffix for file + tempfile: + path: "{{ remote_tmp_dir }}" + suffix: "../world" + register: temp_file_invalid_suffix + ignore_errors: yes + +- name: Reject path components in prefix for directory + tempfile: + state: directory + path: "{{ remote_tmp_dir }}" + prefix: "../../nested" + register: temp_dir_invalid_prefix + ignore_errors: yes + +- name: Reject path components in suffix for directory + tempfile: + state: directory + path: "{{ remote_tmp_dir }}" + suffix: "../world" + register: temp_dir_invalid_suffix + ignore_errors: yes + - name: Check results assert: that: @@ -61,3 +91,12 @@ - temp_file_non_existent_path is failed - temp_dir_non_existent_path is failed + + - temp_file_invalid_prefix is failed + - '"prefix must be a valid file name component" in temp_file_invalid_prefix.msg' + - temp_file_invalid_suffix is failed + - '"suffix must be a valid file name component" in temp_file_invalid_suffix.msg' + - temp_dir_invalid_prefix is failed + - '"prefix must be a valid file name component" in temp_dir_invalid_prefix.msg' + - temp_dir_invalid_suffix is failed + - '"suffix must be a valid file name component" in temp_dir_invalid_suffix.msg' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.12/test/integration/targets/unarchive/auth_mask.yml new/ansible_core-2.19.13/test/integration/targets/unarchive/auth_mask.yml --- old/ansible_core-2.19.12/test/integration/targets/unarchive/auth_mask.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.19.13/test/integration/targets/unarchive/auth_mask.yml 2026-09-08 18:38:52.000000000 +0200 @@ -0,0 +1,18 @@ +- hosts: testhost + gather_facts: false + tasks: + - name: unarchive auth fail a tar from an URL download from remote + unarchive: src="http://secretuser:secretpassword@localhost/nothere" dest="/tmp/" remote_src=no + ignore_errors: true + register: auth1 + + - name: unarchive auth fail a tar from an URL download from controller + unarchive: src="http://secretuser:secretpassword@localhost/nothere" dest="/tmp/" remote_src=yes + ignore_errors: true + register: auth2 + + - name: ensure not disclosure + assert: + that: + - "'secret' not in auth1|to_json" + - "'secret' not in auth2|to_json" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.12/test/integration/targets/unarchive/runme.sh new/ansible_core-2.19.13/test/integration/targets/unarchive/runme.sh --- old/ansible_core-2.19.12/test/integration/targets/unarchive/runme.sh 2026-08-10 17:48:28.000000000 +0200 +++ new/ansible_core-2.19.13/test/integration/targets/unarchive/runme.sh 2026-09-08 18:38:52.000000000 +0200 @@ -6,3 +6,8 @@ # https://github.com/ansible/ansible/issues/80710 ANSIBLE_REMOTE_TMP=./ansible ansible-playbook -i ../../inventory test_relative_tmp_dir.yml -v "$@" + +# ensure uri inline secrets are masked +ULOG="${OUTPUT_DIR}/$$-unarchive.log" +ansible-playbook auth_mask.yml -i ../../inventory "$@" 2>&1 | tee "${ULOG}" +[ "$(grep -Ec 'secret' "${ULOG}")" = "0" ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.12/test/integration/targets/uri/tasks/main.yml new/ansible_core-2.19.13/test/integration/targets/uri/tasks/main.yml --- old/ansible_core-2.19.12/test/integration/targets/uri/tasks/main.yml 2026-08-10 17:48:28.000000000 +0200 +++ new/ansible_core-2.19.13/test/integration/targets/uri/tasks/main.yml 2026-09-08 18:38:52.000000000 +0200 @@ -755,3 +755,15 @@ assert: that: - uri_check.msg == "This action (uri) does not support check mode." + +- name: Test auth fail downloading to dir + uri: + url: 'https://secretuser:secretpassword@{{ httpbin_host }}/nonexistant' + dest: "{{ remote_tmp_dir }}" + ignore_errors: true + register: auth_error + +- name: Ensure secret is not visible + assert: + that: + - "'secret' not in auth_error|to_json" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ansible_core-2.19.12/test/units/module_utils/urls/test_mask_url.py new/ansible_core-2.19.13/test/units/module_utils/urls/test_mask_url.py --- old/ansible_core-2.19.12/test/units/module_utils/urls/test_mask_url.py 1970-01-01 01:00:00.000000000 +0100 +++ new/ansible_core-2.19.13/test/units/module_utils/urls/test_mask_url.py 2026-09-08 18:38:52.000000000 +0200 @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# (c) 2026 The Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import annotations + +import pytest + +from ansible.module_utils.urls import mask_url + + +# for test data use 'secret' as part of any parameter that requires masking, avoid elsewhere [email protected]( + 'url, wanted', + ( + ('http://nothingtoseehere.com', ('nothingtoseehere.com', 'http')), + ('http://nothingtoseehere.com:80/stuff.asp?he=no', ('http://nothingtoseehere.com:80/stuff.asp?he=no',)), + ('http://nothingtoseehere.com:80?password=intheclear&user=wrongbutweignore', ('wrongbut', 'intheclear', 'password')), + ('https://[email protected]/index.html', ('hideme.com', 'index.html', '*')), + ('https://[email protected]/index.html?token=nothidden&user=alsonothidden', ('token', 'nothidden', 'alsonothidden', 'user')), + ('https://secretuser:[email protected]/randomfile.html', ('randomfile.html')), + ('https://secretuser:[email protected]:443/protected.html', ('protected.html', '443')), + ('ftp://secretuser:[email protected]/subdir/intheclear.txt', ('subdir', 'intheclear.txt', 'ftp', 'files.insecure')), + ('sftp://secretuser:[email protected]/subdir2/encrypted', ('encrypted', 'sftp')), + ('ftps://secretuser:[email protected]/yolo.asc', ('yolo.asc', 'file.secure')), + ('ftps://file.server/yolo.asc', ('yolo.asc')), + ('ftps://secretuser:[email protected]/yolo.asc', ('file.server/yolo.asc')), + ('redis://:[email protected]:6379/0', ('cache.internal', '6379', 'redis')), + ('amqp://:[email protected]:5672/vhost', ('rabbit.internal', '5672', 'vhost')), + ) +) +def test_mask_url(url, wanted): + + masked = mask_url(url) + assert 'secret' not in masked + + for notmasked in wanted: + assert notmasked in masked ++++++ ansible_core-2.19.12.tar.gz.sha256 -> ansible_core-2.19.13.tar.gz.sha256 ++++++ --- /work/SRC/openSUSE:Factory/ansible-core-2.19/ansible_core-2.19.12.tar.gz.sha256 2026-08-11 17:18:21.805167501 +0200 +++ /work/SRC/openSUSE:Factory/.ansible-core-2.19.new.383539/ansible_core-2.19.13.tar.gz.sha256 2026-09-18 22:04:26.698328974 +0200 @@ -1 +1 @@ -cfb63b021558d5daddb1d0cd35886a5e1b18db2fc254d1f342e1527c62dac058 ansible_core-2.19.12.tar.gz +3711fc5db7a265f93b34cf3f0db8b9708fb0eec12211a6be32ed6edd6c68044b ansible_core-2.19.13.tar.gz
