diff --git a/CVE-2022-3697.patch b/CVE-2022-3697.patch new file mode 100644 index 0000000000000000000000000000000000000000..27875b7a266d7b7536ef6e1f4ae3e5d4bbbb1767 --- /dev/null +++ b/CVE-2022-3697.patch @@ -0,0 +1,366 @@ +From: =?utf-8?q?Bastien_Roucari=C3=A8s?= +Date: Sun, 24 Dec 2023 11:59:07 +0000 +Subject: CVE-2022-3697: ec2_instance - validate options on tower_callback + +A flaw was found in Ansible in the amazon.aws collection when using the +tower_callback parameter from the amazon.aws.ec2_instance module. +This flaw allows an attacker to take advantage of this issue as +the module is handling the parameter insecurely, leading to the password leaking in the logs + +https://salsa.debian.org/python-team/packages/ansible/-/blob/debian/2.7.7+dfsg-1+deb10u2/debian/patches/0029-CVE-2022-3697-ec2_instance-validate-options-on-tower.patch?ref_type=tags +origin: https://patch-diff.githubusercontent.com/raw/ansible-collections/amazon.aws/pull/1207.patch +bug: https://github.com/ansible-collections/amazon.aws/pull/1199 +bug-redhat: https://bugzilla.redhat.com/show_bug.cgi?id=2137664 + +--- + .../20221021-ec2_instance-tower_callback.yml | 5 + + lib/ansible/module_utils/aws/tower.py | 83 +++++++++++ + .../modules/cloud/amazon/ec2_instance.py | 137 ++++++++---------- + test/units/module_utils/aws/test_tower.py | 40 +++++ + 4 files changed, 187 insertions(+), 78 deletions(-) + create mode 100644 changelogs/fragments/20221021-ec2_instance-tower_callback.yml + create mode 100644 lib/ansible/module_utils/aws/tower.py + create mode 100644 test/units/module_utils/aws/test_tower.py + +diff --git a/changelogs/fragments/20221021-ec2_instance-tower_callback.yml b/changelogs/fragments/20221021-ec2_instance-tower_callback.yml +new file mode 100644 +index 00000000..ae961144 +--- /dev/null ++++ b/changelogs/fragments/20221021-ec2_instance-tower_callback.yml +@@ -0,0 +1,5 @@ ++minor_changes: ++- ec2_instance - refacter ``tower_callback`` code to handle parameter validation as part of the argument specification (https://github.com/ansible-collections/amazon.aws/pull/1199). ++- ec2_instance - the ``tower_callback`` parameter has been renamed to ``aap_callback``, ``tower_callback`` remains as an alias. This change should have no observable effect for users outside the module documentation (https://github.com/ansible-collections/amazon.aws/pull/1199). ++security_fixes: ++- ec2_instance - fixes leak of password into logs when using ``tower_callback.windows=True`` and ``tower_callback.set_password`` (https://github.com/ansible-collections/amazon.aws/pull/1199). +diff --git a/lib/ansible/module_utils/aws/tower.py b/lib/ansible/module_utils/aws/tower.py +new file mode 100644 +index 00000000..dd7d9738 +--- /dev/null ++++ b/lib/ansible/module_utils/aws/tower.py +@@ -0,0 +1,83 @@ ++# Copyright: Ansible Project ++# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ++ ++from __future__ import absolute_import, division, print_function ++__metaclass__ = type ++ ++import string ++import textwrap ++ ++from ansible.module_utils._text import to_native ++from ansible.module_utils.six.moves.urllib import parse as urlparse ++ ++ ++def _windows_callback_script(passwd=None): ++ script_url = 'https://raw.githubusercontent.com/ansible/ansible/devel/examples/scripts/ConfigureRemotingForAnsible.ps1' ++ if passwd is not None: ++ passwd = passwd.replace("'", "''") ++ script_tpl = """\ ++ ++ $admin = [adsi]('WinNT://./administrator, user') ++ $admin.PSBase.Invoke('SetPassword', '${PASS}') ++ Invoke-Expression ((New-Object System.Net.Webclient).DownloadString('${SCRIPT}')) ++ ++ """ ++ else: ++ script_tpl = """\ ++ ++ $admin = [adsi]('WinNT://./administrator, user') ++ Invoke-Expression ((New-Object System.Net.Webclient).DownloadString('${SCRIPT}')) ++ ++ """ ++ ++ tpl = string.Template(textwrap.dedent(script_tpl)) ++ return tpl.safe_substitute(PASS=passwd, SCRIPT=script_url) ++ ++ ++def _linux_callback_script(tower_address, template_id, host_config_key): ++ template_id = urlparse.quote(template_id) ++ tower_address = urlparse.quote(tower_address) ++ host_config_key = host_config_key.replace("'", "'\"'\"'") ++ ++ script_tpl = """\ ++ #!/bin/bash ++ set -x ++ ++ retry_attempts=10 ++ attempt=0 ++ while [[ $attempt -lt $retry_attempts ]] ++ do ++ status_code=$(curl --max-time 10 -v -k -s -i \ ++ --data 'host_config_key=${host_config_key}' \ ++ 'https://${tower_address}/api/v2/job_templates/${template_id}/callback/' \ ++ | head -n 1 \ ++ | awk '{print $2}') ++ if [[ $status_code == 404 ]] ++ then ++ status_code=$(curl --max-time 10 -v -k -s -i \ ++ --data 'host_config_key=${host_config_key}' \ ++ 'https://${tower_address}/api/v1/job_templates/${template_id}/callback/' \ ++ | head -n 1 \ ++ | awk '{print $2}') ++ # fall back to using V1 API for Tower 3.1 and below, since v2 API will always 404 ++ fi ++ if [[ $status_code == 201 ]] ++ then ++ exit 0 ++ fi ++ attempt=$(( attempt + 1 )) ++ echo "$${status_code} received... retrying in 1 minute. (Attempt $${attempt})" ++ sleep 60 ++ done ++ exit 1 ++ """ ++ tpl = string.Template(textwrap.dedent(script_tpl)) ++ return tpl.safe_substitute(tower_address=tower_address, ++ template_id=template_id, ++ host_config_key=host_config_key) ++ ++ ++def tower_callback_script(tower_address, job_template_id, host_config_key, windows, passwd): ++ if windows: ++ return to_native(_windows_callback_script(passwd=passwd)) ++ return _linux_callback_script(tower_address, job_template_id, host_config_key) +diff --git a/lib/ansible/modules/cloud/amazon/ec2_instance.py b/lib/ansible/modules/cloud/amazon/ec2_instance.py +index fdde1eef..c74d8820 100644 +--- a/lib/ansible/modules/cloud/amazon/ec2_instance.py ++++ b/lib/ansible/modules/cloud/amazon/ec2_instance.py +@@ -46,19 +46,40 @@ options: + user_data: + description: + - Opaque blob of data which is made available to the ec2 instance +- tower_callback: ++ aap_callback: + description: +- - Preconfigured user-data to enable an instance to perform a Tower callback (Linux only). ++ - Preconfigured user-data to enable an instance to perform an Ansible Automation Platform ++ callback (Linux only). ++ - For Windows instances, to enable remote access via Ansible set I(windows) to C(true), and ++ optionally set an admin password. ++ - If using I(windows) and I(set_password), callback ton Ansible Automation Platform will not ++ be performed but the instance will be ready to receive winrm connections from Ansible. + - Mutually exclusive with I(user_data). +- - For Windows instances, to enable remote access via Ansible set I(tower_callback.windows) to true, and optionally set an admin password. +- - If using 'windows' and 'set_password', callback to Tower will not be performed but the instance will be ready to receive winrm connections from Ansible. ++ type: dict ++ aliases: ['tower_callback'] + suboptions: ++ windows: ++ description: ++ - Set I(windows=True) to use powershell instead of bash for the callback script. ++ type: bool ++ default: False ++ set_password: ++ description: ++ - Optional admin password to use if I(windows=True). ++ type: str + tower_address: + description: +- - IP address or DNS name of Tower server. Must be accessible via this address from the VPC that this instance will be launched in. ++ - IP address or DNS name of Tower server. Must be accessible via this address from the ++ VPC that this instance will be launched in. ++ - Required if I(windows=False). ++ type: str + job_template_id: + description: +- - Either the integer ID of the Tower Job Template, or the name (name supported only for Tower 3.2+). ++ - Either the integer ID of the Tower Job Template, or the name. ++ Using a name for the job template is not supported by Ansible Tower prior to version ++ 3.2. ++ - Required if I(windows=False). ++ type: str + host_config_key: + description: + - Host configuration secret key generated by the Tower job template. +@@ -729,71 +750,10 @@ from ansible.module_utils.ec2 import (boto3_conn, + camel_dict_to_snake_dict) + + from ansible.module_utils.aws.core import AnsibleAWSModule ++from ansible.module_utils.aws.tower import tower_callback_script + + module = None + +- +-def tower_callback_script(tower_conf, windows=False, passwd=None): +- script_url = 'https://raw.githubusercontent.com/ansible/ansible/devel/examples/scripts/ConfigureRemotingForAnsible.ps1' +- if windows and passwd is not None: +- script_tpl = """ +- $admin = [adsi]("WinNT://./administrator, user") +- $admin.PSBase.Invoke("SetPassword", "{PASS}") +- Invoke-Expression ((New-Object System.Net.Webclient).DownloadString('{SCRIPT}')) +- +- """ +- return to_native(textwrap.dedent(script_tpl).format(PASS=passwd, SCRIPT=script_url)) +- elif windows and passwd is None: +- script_tpl = """ +- $admin = [adsi]("WinNT://./administrator, user") +- Invoke-Expression ((New-Object System.Net.Webclient).DownloadString('{SCRIPT}')) +- +- """ +- return to_native(textwrap.dedent(script_tpl).format(PASS=passwd, SCRIPT=script_url)) +- elif not windows: +- for p in ['tower_address', 'job_template_id', 'host_config_key']: +- if p not in tower_conf: +- module.fail_json(msg="Incomplete tower_callback configuration. tower_callback.{0} not set.".format(p)) +- +- if isinstance(tower_conf['job_template_id'], string_types): +- tower_conf['job_template_id'] = urlparse.quote(tower_conf['job_template_id']) +- tpl = string.Template(textwrap.dedent("""#!/bin/bash +- set -x +- +- retry_attempts=10 +- attempt=0 +- while [[ $attempt -lt $retry_attempts ]] +- do +- status_code=`curl --max-time 10 -v -k -s -i \ +- --data "host_config_key=${host_config_key}" \ +- 'https://${tower_address}/api/v2/job_templates/${template_id}/callback/' \ +- | head -n 1 \ +- | awk '{print $2}'` +- if [[ $status_code == 404 ]] +- then +- status_code=`curl --max-time 10 -v -k -s -i \ +- --data "host_config_key=${host_config_key}" \ +- 'https://${tower_address}/api/v1/job_templates/${template_id}/callback/' \ +- | head -n 1 \ +- | awk '{print $2}'` +- # fall back to using V1 API for Tower 3.1 and below, since v2 API will always 404 +- fi +- if [[ $status_code == 201 ]] +- then +- exit 0 +- fi +- attempt=$(( attempt + 1 )) +- echo "$${status_code} received... retrying in 1 minute. (Attempt $${attempt})" +- sleep 60 +- done +- exit 1 +- """)) +- return tpl.safe_substitute(tower_address=tower_conf['tower_address'], +- template_id=tower_conf['job_template_id'], +- host_config_key=tower_conf['host_config_key']) +- raise NotImplementedError("Only windows with remote-prep or non-windows with tower job callback supported so far.") +- +- + @AWSRetry.jittered_backoff() + def manage_tags(match, new_tags, purge_tags, ec2): + changed = False +@@ -1084,6 +1044,21 @@ def discover_security_groups(group, groups, parent_vpc_id=None, subnet_id=None, + return list(dict((g['GroupId'], g) for g in found_groups).values()) + + ++def build_userdata(params): ++ if params.get('user_data') is not None: ++ return {'UserData': to_native(params.get('user_data'))} ++ if params.get('aap_callback'): ++ userdata = tower_callback_script( ++ tower_address=params.get('aap_callback').get('tower_address'), ++ job_template_id=params.get('aap_callback').get('job_template_id'), ++ host_config_key=params.get('aap_callback').get('host_config_key'), ++ windows=params.get('aap_callback').get('windows'), ++ passwd=params.get('aap_callback').get('set_passwd'), ++ ) ++ return {'UserData': userdata} ++ return {} ++ ++ + def build_top_level_options(params): + spec = {} + if params.get('image_id'): +@@ -1100,14 +1075,8 @@ def build_top_level_options(params): + + if params.get('key_name') is not None: + spec['KeyName'] = params.get('key_name') +- if params.get('user_data') is not None: +- spec['UserData'] = to_native(params.get('user_data')) +- elif params.get('tower_callback') is not None: +- spec['UserData'] = tower_callback_script( +- tower_conf=params.get('tower_callback'), +- windows=params.get('tower_callback').get('windows', False), +- passwd=params.get('tower_callback').get('set_password'), +- ) ++ ++ spec.update(build_userdata(params)) + + if params.get('launch_template') is not None: + spec['LaunchTemplate'] = {} +@@ -1586,7 +1555,19 @@ def main(): + image_id=dict(type='str'), + instance_type=dict(default='t2.micro', type='str'), + user_data=dict(type='str'), +- tower_callback=dict(type='dict'), ++ aap_callback=dict( ++ type='dict', aliases=['tower_callback'], ++ required_if=[ ++ ('windows', False, ('tower_address', 'job_template_id', 'host_config_key',), False), ++ ], ++ options=dict( ++ windows=dict(type='bool', default=False), ++ set_password=dict(type='str', no_log=True), ++ tower_address=dict(type='str'), ++ job_template_id=dict(type='str'), ++ host_config_key=dict(type='str', no_log=True), ++ ), ++ ), + ebs_optimized=dict(type='bool'), + vpc_subnet_id=dict(type='str', aliases=['subnet_id']), + availability_zone=dict(type='str'), +@@ -1620,7 +1601,7 @@ def main(): + mutually_exclusive=[ + ['security_groups', 'security_group'], + ['availability_zone', 'vpc_subnet_id'], +- ['tower_callback', 'user_data'], ++ ['aap_callback', 'user_data'], + ['image_id', 'image'], + ], + supports_check_mode=True +diff --git a/test/units/module_utils/aws/test_tower.py b/test/units/module_utils/aws/test_tower.py +new file mode 100644 +index 00000000..527423b1 +--- /dev/null ++++ b/test/units/module_utils/aws/test_tower.py +@@ -0,0 +1,40 @@ ++# (c) 2022 Red Hat Inc. ++# ++# This file is part of Ansible ++# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ++ ++from __future__ import (absolute_import, division, print_function) ++__metaclass__ = type ++ ++# import pytest ++ ++import ansible.module_utils.aws.tower as utils_tower ++ ++WINDOWS_DOWNLOAD = "Invoke-Expression ((New-Object System.Net.Webclient).DownloadString(" \ ++ "'https://raw.githubusercontent.com/ansible/ansible/devel/examples/scripts/ConfigureRemotingForAnsible.ps1'))" ++EXAMPLE_PASSWORD = 'MY_EXAMPLE_PASSWORD' ++WINDOWS_INVOKE = "$admin.PSBase.Invoke('SetPassword', 'MY_EXAMPLE_PASSWORD'" ++ ++EXAMPLE_TOWER = "tower.example.com" ++EXAMPLE_TEMPLATE = 'My Template' ++EXAMPLE_KEY = '123EXAMPLE123' ++LINUX_TRIGGER_V1 = 'https://tower.example.com/api/v1/job_templates/My%20Template/callback/' ++LINUX_TRIGGER_V2 = 'https://tower.example.com/api/v2/job_templates/My%20Template/callback/' ++ ++ ++def test_windows_callback_no_password(): ++ user_data = utils_tower._windows_callback_script() ++ assert WINDOWS_DOWNLOAD in user_data ++ assert 'SetPassword' not in user_data ++ ++ ++def test_windows_callback_password(): ++ user_data = utils_tower._windows_callback_script(EXAMPLE_PASSWORD) ++ assert WINDOWS_DOWNLOAD in user_data ++ assert WINDOWS_INVOKE in user_data ++ ++ ++def test_linux_callback_with_name(): ++ user_data = utils_tower._linux_callback_script(EXAMPLE_TOWER, EXAMPLE_TEMPLATE, EXAMPLE_KEY) ++ assert LINUX_TRIGGER_V1 in user_data ++ assert LINUX_TRIGGER_V2 in user_data +-- +2.33.0 + diff --git a/CVE-2023-5115.patch b/CVE-2023-5115.patch new file mode 100644 index 0000000000000000000000000000000000000000..d1b99b0daf0f5c5050dfeb67566a58cf7035fc39 --- /dev/null +++ b/CVE-2023-5115.patch @@ -0,0 +1,177 @@ +Description: Fix for CVE-2023-5115, based on upstream patch +Author: Lee Garrett +Origin: https://github.com/ansible/ansible/pull/81787 +https://salsa.debian.org/python-team/packages/ansible/-/blob/debian/2.10.7+merged+base+2.10.17+dfsg-0+deb11u2/debian/patches/0010-fix-CVE-2023-5115.patch?ref_type=tags +Forwarded: not-needed +Applied-Upstream: https://github.com/ansible/ansible/pull/81787 +Reviewed-by: Lee Garrett +Last-Update: 2024-07-04 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- /dev/null ++++ b/test/integration/targets/ansible-galaxy-role/files/create-role-archive.py +@@ -0,0 +1,45 @@ ++#!/usr/bin/env python ++"""Create a role archive which overwrites an arbitrary file.""" ++ ++import argparse ++import pathlib ++import tarfile ++import tempfile ++ ++ ++def main() -> None: ++ parser = argparse.ArgumentParser(description=__doc__) ++ parser.add_argument('archive', type=pathlib.Path, help='archive to create') ++ parser.add_argument('content', type=pathlib.Path, help='content to write') ++ parser.add_argument('target', type=pathlib.Path, help='file to overwrite') ++ ++ args = parser.parse_args() ++ ++ create_archive(args.archive, args.content, args.target) ++ ++ ++def create_archive(archive_path: pathlib.Path, content_path: pathlib.Path, target_path: pathlib.Path) -> None: ++ with ( ++ tarfile.open(name=archive_path, mode='w') as role_archive, ++ tempfile.TemporaryDirectory() as temp_dir_name, ++ ): ++ temp_dir_path = pathlib.Path(temp_dir_name) ++ ++ meta_main_path = temp_dir_path / 'meta' / 'main.yml' ++ meta_main_path.parent.mkdir() ++ meta_main_path.write_text('') ++ ++ symlink_path = temp_dir_path / 'symlink' ++ symlink_path.symlink_to(target_path) ++ ++ role_archive.add(meta_main_path) ++ role_archive.add(symlink_path) ++ ++ content_tarinfo = role_archive.gettarinfo(content_path, str(symlink_path)) ++ ++ with content_path.open('rb') as content_file: ++ role_archive.addfile(content_tarinfo, content_file) ++ ++ ++if __name__ == '__main__': ++ main() +--- /dev/null ++++ b/test/integration/targets/ansible-galaxy-role/tasks/dir-traversal.yml +@@ -0,0 +1,44 @@ ++- name: create test directories ++ file: ++ path: '{{ remote_tmp_dir }}/dir-traversal/{{ item }}' ++ state: directory ++ loop: ++ - source ++ - target ++ - roles ++ ++- name: create test content ++ copy: ++ dest: '{{ remote_tmp_dir }}/dir-traversal/source/content.txt' ++ content: | ++ some content to write ++ ++- name: build dangerous dir traversal role ++ script: ++ chdir: '{{ remote_tmp_dir }}/dir-traversal/source' ++ cmd: create-role-archive.py dangerous.tar content.txt {{ remote_tmp_dir }}/dir-traversal/target/target-file-to-overwrite.txt ++ executable: '{{ ansible_playbook_python }}' ++ ++- name: install dangerous role ++ command: ++ cmd: ansible-galaxy role install --roles-path '{{ remote_tmp_dir }}/dir-traversal/roles' dangerous.tar ++ chdir: '{{ remote_tmp_dir }}/dir-traversal/source' ++ ignore_errors: true ++ register: galaxy_install_dangerous ++ ++- name: check for overwritten file ++ stat: ++ path: '{{ remote_tmp_dir }}/dir-traversal/target/target-file-to-overwrite.txt' ++ register: dangerous_overwrite_stat ++ ++- name: get overwritten content ++ slurp: ++ path: '{{ remote_tmp_dir }}/dir-traversal/target/target-file-to-overwrite.txt' ++ register: dangerous_overwrite_content ++ when: dangerous_overwrite_stat.stat.exists ++ ++- assert: ++ that: ++ - dangerous_overwrite_content.content|default('')|b64decode == '' ++ - not dangerous_overwrite_stat.stat.exists ++ - galaxy_install_dangerous is failed +--- /dev/null ++++ b/test/integration/targets/ansible-galaxy-role/tasks/main.yml +@@ -0,0 +1 @@ ++- import_tasks: dir-traversal.yml +--- /dev/null ++++ b/changelogs/fragments/cve-2023-5115.yml +@@ -0,0 +1,3 @@ ++security_fixes: ++- ansible-galaxy - Prevent roles from using symlinks to overwrite ++ files outside of the installation directory (CVE-2023-5115) +--- a/lib/ansible/galaxy/role.py ++++ b/lib/ansible/galaxy/role.py +@@ -329,14 +329,36 @@ + # bits that might be in the file for security purposes + # and drop any containing directory, as mentioned above + if member.isreg() or member.issym(): +- n_member_name = to_native(member.name) +- n_archive_parent_dir = to_native(archive_parent_dir) +- n_parts = n_member_name.replace(n_archive_parent_dir, "", 1).split(os.sep) +- n_final_parts = [] +- for n_part in n_parts: +- if n_part != '..' and '~' not in n_part and '$' not in n_part: ++ for attr in ('name', 'linkname'): ++ attr_value = getattr(member, attr, None) ++ if not attr_value: ++ continue ++ n_attr_value = to_native(attr_value) ++ n_archive_parent_dir = to_native(archive_parent_dir) ++ n_parts = n_attr_value.replace(n_archive_parent_dir, "", 1).split(os.sep) ++ n_final_parts = [] ++ for n_part in n_parts: ++ # TODO if the condition triggers it produces a broken installation. ++ # It will create the parent directory as an empty file and will ++ # explode if the directory contains valid files. ++ # Leaving this as is since the whole module needs a rewrite. ++ # ++ # Check if we have any files with illegal names, ++ # and display a warning if so. This could help users ++ # to debug a broken installation. ++ if not n_part: ++ continue ++ if n_part == '..': ++ display.warning(f"Illegal filename '{n_part}': '..' is not allowed") ++ continue ++ if n_part.startswith('~'): ++ display.warning(f"Illegal filename '{n_part}': names cannot start with '~'") ++ continue ++ if '$' in n_part: ++ display.warning(f"Illegal filename '{n_part}': names cannot contain '$'") ++ continue + n_final_parts.append(n_part) +- member.name = os.path.join(*n_final_parts) ++ setattr(member, attr, os.path.join(*n_final_parts)) + role_tar_file.extract(member, to_native(self.path)) + + # write out the install info file for later use +--- /dev/null ++++ b/test/integration/targets/ansible-galaxy-role/meta/main.yml +@@ -0,0 +1 @@ ++dependencies: [setup_remote_tmp_dir] +--- /dev/null ++++ b/test/integration/targets/setup_remote_tmp_dir/defaults/main.yml +@@ -0,0 +1,2 @@ ++setup_remote_tmp_dir_skip_cleanup: no ++setup_remote_tmp_dir_cache_path: no +--- a/test/integration/targets/setup_remote_tmp_dir/tasks/default.yml ++++ b/test/integration/targets/setup_remote_tmp_dir/tasks/default.yml +@@ -9,3 +9,4 @@ + - name: record temporary directory + set_fact: + remote_tmp_dir: "{{ remote_tmp_dir.path }}" ++ cacheable: "{{ setup_remote_tmp_dir_cache_path | bool }}" diff --git a/ansible.spec b/ansible.spec index 518cfdee163d602389dc00942b0ebfb9828b6f23..0602852cdf5a32262eb7ca64ced9d17ec924a302 100644 --- a/ansible.spec +++ b/ansible.spec @@ -2,7 +2,7 @@ Name: ansible Summary: SSH-based configuration management, deployment, and task execution system Version: 2.9.27 -Release: 6 +Release: 7 License: GPLv3+ Source0: https://releases.ansible.com/ansible/%{name}-%{version}.tar.gz @@ -21,6 +21,8 @@ Patch6: Fix-build-error-for-sphinx-7.0.patch Patch7: CVE-2024-0690.patch Patch8: CVE-2024-8775.patch Patch9: CVE-2024-9902.patch +Patch10:CVE-2022-3697.patch +Patch11:CVE-2023-5115.patch Provides: ansible-python3 = %{version}-%{release} Obsoletes: ansible-python3 < %{version}-%{release} @@ -218,6 +220,9 @@ make PYTHON=/usr/bin/python3 tests-py3 %{python3_sitelib}/ansible_test %changelog +* Sat Feb 08 2025 wangkai <13474090681@163.com> - 2.9.27-7 +- Fix CVE-2022-3697 CVE-2023-5115 + * Mon Dec 02 2024 yaoxin - 2.9.27-6 - Fix CVE-2024-8775 and CVE-2024-9902