From d96d41235c10dd6362af49cff0df765a90e407f5 Mon Sep 17 00:00:00 2001 From: Giorgos Drosos Date: Sun, 17 Aug 2025 23:28:10 +0300 Subject: [PATCH 01/17] Attempt to fix gem soundness issue --- plugins/modules/gem.py | 18 ++++++++++++++---- tests/integration/targets/gem/tasks/main.yml | 17 +++++++++++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/plugins/modules/gem.py b/plugins/modules/gem.py index 1ea9c68a948..63902c75fdc 100644 --- a/plugins/modules/gem.py +++ b/plugins/modules/gem.py @@ -243,7 +243,8 @@ def uninstall(module): if module.params['force']: cmd.append('--force') cmd.append(module.params['name']) - module.run_command(cmd, environ_update=environ, check_rc=True) + rc, out, err = module.run_command(cmd, environ_update=environ, check_rc=True) + return rc, out, err def install(module): @@ -334,9 +335,18 @@ def main(): changed = True elif module.params['state'] == 'absent': if exists(module): - uninstall(module) - changed = True - + rc, out, err = uninstall(module) + if exists(module): + module.fail_json( + msg="Gem could not be removed.", + rc=rc, + stdout=out, + stderr=err, + name=module.params['name'], + state='absent' + ) + else: + changed = True result = {} result['name'] = module.params['name'] result['state'] = module.params['state'] diff --git a/tests/integration/targets/gem/tasks/main.yml b/tests/integration/targets/gem/tasks/main.yml index 0c85e564891..17a95fbdc11 100644 --- a/tests/integration/targets/gem/tasks/main.yml +++ b/tests/integration/targets/gem/tasks/main.yml @@ -212,3 +212,20 @@ that: - install_gem_result is changed - not gem_bindir_stat.stat.exists + + - name: Attempt to uninstall default gem 'json' + community.general.gem: + name: json + state: absent + when: ansible_distribution == "Ubuntu" + register: gem_result + ignore_errors: true + + - name: Assert gem uninstall failed as expected + when: ansible_distribution == "Ubuntu" + assert: + that: + - gem_result is failed + - "'Gem could not be removed' == gem_result.msg" + - gem_result.state == "absent" + - gem_result.name == "json" \ No newline at end of file From ce52960c1c1e20c1910fd5bc02c4a86c2d150a87 Mon Sep 17 00:00:00 2001 From: Giorgos Drosos Date: Sun, 17 Aug 2025 23:44:37 +0300 Subject: [PATCH 02/17] Return command execution --- plugins/modules/gem.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/modules/gem.py b/plugins/modules/gem.py index 63902c75fdc..f4725028a91 100644 --- a/plugins/modules/gem.py +++ b/plugins/modules/gem.py @@ -243,8 +243,7 @@ def uninstall(module): if module.params['force']: cmd.append('--force') cmd.append(module.params['name']) - rc, out, err = module.run_command(cmd, environ_update=environ, check_rc=True) - return rc, out, err + return module.run_command(cmd, environ_update=environ, check_rc=True) def install(module): From 4a76c09bae5a7d76c478cffe403e21d00d7e5967 Mon Sep 17 00:00:00 2001 From: Giorgos Drosos Date: Sun, 17 Aug 2025 23:49:26 +0300 Subject: [PATCH 03/17] Fix value error --- plugins/modules/gem.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/modules/gem.py b/plugins/modules/gem.py index f4725028a91..81eed50c07f 100644 --- a/plugins/modules/gem.py +++ b/plugins/modules/gem.py @@ -334,8 +334,9 @@ def main(): changed = True elif module.params['state'] == 'absent': if exists(module): - rc, out, err = uninstall(module) - if exists(module): + command_output = uninstall(module) + if command_output and exists(module): + rc, out, err = command_output module.fail_json( msg="Gem could not be removed.", rc=rc, From 599922c77ce070ed8dc99b42e5f5152a08a399f2 Mon Sep 17 00:00:00 2001 From: Giorgos Drosos Date: Sun, 17 Aug 2025 23:55:23 +0300 Subject: [PATCH 04/17] Attempt to fix failling tests --- plugins/modules/gem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/gem.py b/plugins/modules/gem.py index 81eed50c07f..1ecb53ddcfd 100644 --- a/plugins/modules/gem.py +++ b/plugins/modules/gem.py @@ -335,7 +335,7 @@ def main(): elif module.params['state'] == 'absent': if exists(module): command_output = uninstall(module) - if command_output and exists(module): + if isinstance(command_output, (list, tuple)) and len(command_output) == 3 and exists(module): rc, out, err = command_output module.fail_json( msg="Gem could not be removed.", From 3f81912ec3960d7accd5ce0e3456765a5f42984e Mon Sep 17 00:00:00 2001 From: Giorgos Drosos Date: Mon, 18 Aug 2025 00:02:24 +0300 Subject: [PATCH 05/17] Fix minor issues --- plugins/modules/gem.py | 2 +- tests/integration/targets/gem/tasks/main.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/modules/gem.py b/plugins/modules/gem.py index 1ecb53ddcfd..116cfe00587 100644 --- a/plugins/modules/gem.py +++ b/plugins/modules/gem.py @@ -338,7 +338,7 @@ def main(): if isinstance(command_output, (list, tuple)) and len(command_output) == 3 and exists(module): rc, out, err = command_output module.fail_json( - msg="Gem could not be removed.", + msg="Gem could not be removed", rc=rc, stdout=out, stderr=err, diff --git a/tests/integration/targets/gem/tasks/main.yml b/tests/integration/targets/gem/tasks/main.yml index 17a95fbdc11..aea976b7d90 100644 --- a/tests/integration/targets/gem/tasks/main.yml +++ b/tests/integration/targets/gem/tasks/main.yml @@ -212,7 +212,7 @@ that: - install_gem_result is changed - not gem_bindir_stat.stat.exists - + - name: Attempt to uninstall default gem 'json' community.general.gem: name: json From 54674024c5ad13f2bf6c29fbec793bf6fd532683 Mon Sep 17 00:00:00 2001 From: Giorgos Drosos Date: Mon, 18 Aug 2025 00:07:20 +0300 Subject: [PATCH 06/17] Update changelog --- changelogs/fragments/10689-gem-prevent-soundness-issue.yml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 changelogs/fragments/10689-gem-prevent-soundness-issue.yml diff --git a/changelogs/fragments/10689-gem-prevent-soundness-issue.yml b/changelogs/fragments/10689-gem-prevent-soundness-issue.yml new file mode 100644 index 00000000000..bc426df0306 --- /dev/null +++ b/changelogs/fragments/10689-gem-prevent-soundness-issue.yml @@ -0,0 +1,2 @@ +bugfixes: + - "gem - fix soundness issue when uninstalling default gems on Ubuntu (https://github.com/ansible-collections/community.general/issues/10451, https://github.com/ansible-collections/community.general/pull/10689)." \ No newline at end of file From f8972018b62d47edd795a3bbd487754aac5d73be Mon Sep 17 00:00:00 2001 From: Giorgos Drosos <56369797+gdrosos@users.noreply.github.com> Date: Mon, 18 Aug 2025 12:54:41 +0300 Subject: [PATCH 07/17] Update tests/integration/targets/gem/tasks/main.yml Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com> --- tests/integration/targets/gem/tasks/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/targets/gem/tasks/main.yml b/tests/integration/targets/gem/tasks/main.yml index aea976b7d90..a255a375a42 100644 --- a/tests/integration/targets/gem/tasks/main.yml +++ b/tests/integration/targets/gem/tasks/main.yml @@ -226,6 +226,6 @@ assert: that: - gem_result is failed - - "'Gem could not be removed' == gem_result.msg" + - gem_result.msg == "Gem could not be removed" - gem_result.state == "absent" - gem_result.name == "json" \ No newline at end of file From 9812ef24f3e28f46001318053428e59b98399c0e Mon Sep 17 00:00:00 2001 From: Giorgos Drosos <56369797+gdrosos@users.noreply.github.com> Date: Mon, 18 Aug 2025 12:56:29 +0300 Subject: [PATCH 08/17] Update changelogs/fragments/10689-gem-prevent-soundness-issue.yml Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com> --- changelogs/fragments/10689-gem-prevent-soundness-issue.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelogs/fragments/10689-gem-prevent-soundness-issue.yml b/changelogs/fragments/10689-gem-prevent-soundness-issue.yml index bc426df0306..a55dba1ea1e 100644 --- a/changelogs/fragments/10689-gem-prevent-soundness-issue.yml +++ b/changelogs/fragments/10689-gem-prevent-soundness-issue.yml @@ -1,2 +1,2 @@ bugfixes: - - "gem - fix soundness issue when uninstalling default gems on Ubuntu (https://github.com/ansible-collections/community.general/issues/10451, https://github.com/ansible-collections/community.general/pull/10689)." \ No newline at end of file + - "gem - fix soundness issue when uninstalling default gems on Ubuntu (https://github.com/ansible-collections/community.general/issues/10451, https://github.com/ansible-collections/community.general/pull/10689)." \ No newline at end of file From 8dd227becfe7240167fbb6382b514bcdc20c505d Mon Sep 17 00:00:00 2001 From: Giorgos Drosos Date: Mon, 18 Aug 2025 12:57:56 +0300 Subject: [PATCH 09/17] Remove state and name from gem error message --- plugins/modules/gem.py | 4 +--- tests/integration/targets/gem/tasks/main.yml | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/plugins/modules/gem.py b/plugins/modules/gem.py index 116cfe00587..43738e422a9 100644 --- a/plugins/modules/gem.py +++ b/plugins/modules/gem.py @@ -341,9 +341,7 @@ def main(): msg="Gem could not be removed", rc=rc, stdout=out, - stderr=err, - name=module.params['name'], - state='absent' + stderr=err ) else: changed = True diff --git a/tests/integration/targets/gem/tasks/main.yml b/tests/integration/targets/gem/tasks/main.yml index a255a375a42..8f41d945327 100644 --- a/tests/integration/targets/gem/tasks/main.yml +++ b/tests/integration/targets/gem/tasks/main.yml @@ -226,6 +226,4 @@ assert: that: - gem_result is failed - - gem_result.msg == "Gem could not be removed" - - gem_result.state == "absent" - - gem_result.name == "json" \ No newline at end of file + - gem_result.msg == "Gem could not be removed" \ No newline at end of file From d5575c5f890175194bd1100831af4a6fefed340e Mon Sep 17 00:00:00 2001 From: Giorgos Drosos Date: Mon, 18 Aug 2025 13:01:31 +0300 Subject: [PATCH 10/17] Improve gem uninstall check --- plugins/modules/gem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/gem.py b/plugins/modules/gem.py index 43738e422a9..f654f1c2227 100644 --- a/plugins/modules/gem.py +++ b/plugins/modules/gem.py @@ -335,7 +335,7 @@ def main(): elif module.params['state'] == 'absent': if exists(module): command_output = uninstall(module) - if isinstance(command_output, (list, tuple)) and len(command_output) == 3 and exists(module): + if command_output is not None and exists(module): rc, out, err = command_output module.fail_json( msg="Gem could not be removed", From e2d7a3fb52a0be989e6fa7aa99be20b856a72a62 Mon Sep 17 00:00:00 2001 From: Giorgos Drosos Date: Mon, 18 Aug 2025 13:12:47 +0300 Subject: [PATCH 11/17] Make unit tests pass --- plugins/modules/gem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/gem.py b/plugins/modules/gem.py index f654f1c2227..95854b65968 100644 --- a/plugins/modules/gem.py +++ b/plugins/modules/gem.py @@ -335,7 +335,7 @@ def main(): elif module.params['state'] == 'absent': if exists(module): command_output = uninstall(module) - if command_output is not None and exists(module): + if command_output is not None and len(command_output)==3 and exists(module): rc, out, err = command_output module.fail_json( msg="Gem could not be removed", From 5794244d547649236e030448f05c09671eb34797 Mon Sep 17 00:00:00 2001 From: Giorgos Drosos Date: Mon, 18 Aug 2025 13:15:07 +0300 Subject: [PATCH 12/17] Fix linting issues --- plugins/modules/gem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/gem.py b/plugins/modules/gem.py index 95854b65968..c6ab7c93532 100644 --- a/plugins/modules/gem.py +++ b/plugins/modules/gem.py @@ -335,7 +335,7 @@ def main(): elif module.params['state'] == 'absent': if exists(module): command_output = uninstall(module) - if command_output is not None and len(command_output)==3 and exists(module): + if command_output is not None and len(command_output) == 3 and exists(module): rc, out, err = command_output module.fail_json( msg="Gem could not be removed", From c71950aeeac2d1410def58a65c8edb78a22dc18a Mon Sep 17 00:00:00 2001 From: Giorgos Drosos Date: Wed, 1 Oct 2025 19:30:31 +0200 Subject: [PATCH 13/17] gem: Remove length chenck and adapt unit tests --- plugins/modules/gem.py | 2 +- tests/unit/plugins/modules/test_gem.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/modules/gem.py b/plugins/modules/gem.py index c6ab7c93532..f654f1c2227 100644 --- a/plugins/modules/gem.py +++ b/plugins/modules/gem.py @@ -335,7 +335,7 @@ def main(): elif module.params['state'] == 'absent': if exists(module): command_output = uninstall(module) - if command_output is not None and len(command_output) == 3 and exists(module): + if command_output is not None and exists(module): rc, out, err = command_output module.fail_json( msg="Gem could not be removed", diff --git a/tests/unit/plugins/modules/test_gem.py b/tests/unit/plugins/modules/test_gem.py index 78c73be5a6b..3e0bec3c5a1 100644 --- a/tests/unit/plugins/modules/test_gem.py +++ b/tests/unit/plugins/modules/test_gem.py @@ -52,7 +52,9 @@ def new(module): def patch_run_command(self): target = 'ansible.module_utils.basic.AnsibleModule.run_command' - return self.mocker.patch(target) + mock = self.mocker.patch(target) + mock.return_value = (0, '', '') + return mock def test_fails_when_user_install_and_install_dir_are_combined(self): with set_module_args({ From d8a4dbd908518cbbdb86e1b439170afc303226bd Mon Sep 17 00:00:00 2001 From: Giorgos Drosos Date: Wed, 1 Oct 2025 19:50:28 +0200 Subject: [PATCH 14/17] Adapt gem unit tests --- tests/unit/plugins/modules/test_gem.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/unit/plugins/modules/test_gem.py b/tests/unit/plugins/modules/test_gem.py index 3e0bec3c5a1..3e6df91185b 100644 --- a/tests/unit/plugins/modules/test_gem.py +++ b/tests/unit/plugins/modules/test_gem.py @@ -109,12 +109,11 @@ def test_passes_install_dir_and_gem_home_when_uninstall_gem(self): run_command = self.patch_run_command() - with pytest.raises(AnsibleExitJson) as exc: + with pytest.raises(AnsibleFailJson) as exc: gem.main() result = exc.value.args[0] - - assert result['changed'] + assert result['failed'] assert run_command.called assert '--install-dir /opt/dummy' in get_command(run_command) From d1c58e6f4a4e6ed2ae6f592ae37e799bbdf57dbe Mon Sep 17 00:00:00 2001 From: Giorgos Drosos Date: Wed, 1 Oct 2025 20:01:16 +0200 Subject: [PATCH 15/17] gem: improve error msg --- plugins/modules/gem.py | 6 +++++- tests/integration/targets/gem/tasks/main.yml | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/plugins/modules/gem.py b/plugins/modules/gem.py index f654f1c2227..d848bf1ef61 100644 --- a/plugins/modules/gem.py +++ b/plugins/modules/gem.py @@ -338,7 +338,11 @@ def main(): if command_output is not None and exists(module): rc, out, err = command_output module.fail_json( - msg="Gem could not be removed", + msg=( + "Failed to uninstall gem '%s': it is still present after 'gem uninstall'. " + "This usually happens with default or system gems provided by the OS, " + "which cannot be removed with the gem command." + ) % module.params['name'], rc=rc, stdout=out, stderr=err diff --git a/tests/integration/targets/gem/tasks/main.yml b/tests/integration/targets/gem/tasks/main.yml index 8f41d945327..ca8a5c2ad80 100644 --- a/tests/integration/targets/gem/tasks/main.yml +++ b/tests/integration/targets/gem/tasks/main.yml @@ -226,4 +226,5 @@ assert: that: - gem_result is failed - - gem_result.msg == "Gem could not be removed" \ No newline at end of file + - gem_result.msg.startswith("Failed to uninstall gem 'json'") + From d369720bf87ae78c6a3779f9d1f4fa93c72837fd Mon Sep 17 00:00:00 2001 From: Giorgos Drosos Date: Wed, 1 Oct 2025 20:04:03 +0200 Subject: [PATCH 16/17] Fix sanity error --- tests/integration/targets/gem/tasks/main.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/integration/targets/gem/tasks/main.yml b/tests/integration/targets/gem/tasks/main.yml index ca8a5c2ad80..73119c20da2 100644 --- a/tests/integration/targets/gem/tasks/main.yml +++ b/tests/integration/targets/gem/tasks/main.yml @@ -227,4 +227,3 @@ that: - gem_result is failed - gem_result.msg.startswith("Failed to uninstall gem 'json'") - From 0044a1e9ae4eef28c633ca5dafeeddc9fa05b101 Mon Sep 17 00:00:00 2001 From: Giorgos Drosos Date: Wed, 1 Oct 2025 20:07:21 +0200 Subject: [PATCH 17/17] Fix linting issue --- plugins/modules/gem.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/modules/gem.py b/plugins/modules/gem.py index d848bf1ef61..2a458116c73 100644 --- a/plugins/modules/gem.py +++ b/plugins/modules/gem.py @@ -338,11 +338,11 @@ def main(): if command_output is not None and exists(module): rc, out, err = command_output module.fail_json( - msg=( - "Failed to uninstall gem '%s': it is still present after 'gem uninstall'. " - "This usually happens with default or system gems provided by the OS, " - "which cannot be removed with the gem command." - ) % module.params['name'], + msg=( + "Failed to uninstall gem '%s': it is still present after 'gem uninstall'. " + "This usually happens with default or system gems provided by the OS, " + "which cannot be removed with the gem command." + ) % module.params['name'], rc=rc, stdout=out, stderr=err