gem: fix soundness issue when uninstalling default gems on Ubuntu (#10689)
Some checks failed
EOL CI / EOL Sanity (Ⓐ2.16) (push) Has been cancelled
EOL CI / EOL Units (Ⓐ2.16+py2.7) (push) Has been cancelled
EOL CI / EOL Units (Ⓐ2.16+py3.11) (push) Has been cancelled
EOL CI / EOL Units (Ⓐ2.16+py3.6) (push) Has been cancelled
EOL CI / EOL I (Ⓐ2.16+alpine3+py:azp/posix/1/) (push) Has been cancelled
EOL CI / EOL I (Ⓐ2.16+alpine3+py:azp/posix/2/) (push) Has been cancelled
EOL CI / EOL I (Ⓐ2.16+alpine3+py:azp/posix/3/) (push) Has been cancelled
EOL CI / EOL I (Ⓐ2.16+fedora38+py:azp/posix/1/) (push) Has been cancelled
EOL CI / EOL I (Ⓐ2.16+fedora38+py:azp/posix/2/) (push) Has been cancelled
EOL CI / EOL I (Ⓐ2.16+fedora38+py:azp/posix/3/) (push) Has been cancelled
EOL CI / EOL I (Ⓐ2.16+opensuse15+py:azp/posix/1/) (push) Has been cancelled
EOL CI / EOL I (Ⓐ2.16+opensuse15+py:azp/posix/2/) (push) Has been cancelled
EOL CI / EOL I (Ⓐ2.16+opensuse15+py:azp/posix/3/) (push) Has been cancelled
nox / Run extra sanity tests (push) Has been cancelled

* Attempt to fix gem soundness issue

* Return command execution

* Fix value error

* Attempt to fix failling tests

* Fix minor issues

* Update changelog

* Update tests/integration/targets/gem/tasks/main.yml

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>

* Update changelogs/fragments/10689-gem-prevent-soundness-issue.yml

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>

* Remove state and name from gem error message

* Improve gem uninstall check

* Make unit tests pass

* Fix linting issues

* gem: Remove length chenck and adapt unit tests

* Adapt gem unit tests

* gem: improve error msg

* Fix sanity error

* Fix linting issue

---------

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
This commit is contained in:
Giorgos Drosos 2025-10-05 07:15:25 +02:00 committed by GitHub
commit cc41d9da60
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 38 additions and 8 deletions

View file

@ -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)."

View file

@ -243,7 +243,7 @@ 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)
return module.run_command(cmd, environ_update=environ, check_rc=True)
def install(module):
@ -334,9 +334,21 @@ def main():
changed = True
elif module.params['state'] == 'absent':
if exists(module):
uninstall(module)
command_output = uninstall(module)
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'],
rc=rc,
stdout=out,
stderr=err
)
else:
changed = True
result = {}
result['name'] = module.params['name']
result['state'] = module.params['state']

View file

@ -212,3 +212,18 @@
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_result.msg.startswith("Failed to uninstall gem 'json'")

View file

@ -52,7 +52,9 @@ class TestGem(ModuleTestCase):
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({
@ -107,12 +109,11 @@ class TestGem(ModuleTestCase):
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)