Skip to content

apk fails with unhandled ValueError on package names containing unmatched quotes #10673

@gdrosos

Description

@gdrosos

Summary

When the community.general.apk module is invoked with a package name containing an unmatched quotation mark, the module passes the name parameter directly to Python’s shlex.split() without validation.
This call raises a ValueError: No closing quotation, which is not caught by the module. Because the error is uncaptured, it propagates to Ansible and produces a raw Python traceback in the output.

Instead, we would expect the module to either:

  1. Validate the name parameter before calling shlex.split() and fail early with a clear error message, or
  2. Catch the ValueError and report it via fail_json() to provide a user-friendly failure.

Issue Type

Bug Report

Component Name

apk

Ansible Version

$ ansible --version
ansible [core 2.18.6]
  python version = 3.12.3 
  jinja version = 3.1.6
  libyaml = True

Community.general Version

$ ansible-galaxy collection list community.general
Collection        Version
----------------- -------
community.general 11.1.0 

Configuration

$ ansible-config dump --only-changed

OS / Environment

Alpine 3.20.6

Steps to Reproduce

  - name: Reproduce unmatched quote bug in apk module
     hosts: localhost
     gather_facts: false
     tasks:
       - name: Install package with unmatched quote
         community.general.apk:
           name: 'invalid"name'
           state: present

Expected Results

We would expect the module to either:

  1. Validate the name parameter before calling shlex.split() and fail early with a clear error message, or
  2. Catch the ValueError and report it via fail_json() to provide a user-friendly failure.

Actual Results

PLAY [Reproduce unmatched quote bug in apk module] ************************************************************************************

TASK [Install package with unmatched quote] *******************************************************************************************
task path: /foo.yml:5
<127.0.0.1> ESTABLISH LOCAL CONNECTION FOR USER: root
<127.0.0.1> EXEC /bin/sh -c 'echo ~root && sleep 0'
<127.0.0.1> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /root/.ansible/tmp `"&& mkdir "` echo /root/.ansible/tmp/ansible-tmp-1755282926.8335867-52-96736124164692 `" && echo ansible-tmp-1755282926.8335867-52-96736124164692="` echo /root/.ansible/tmp/ansible-tmp-1755282926.8335867-52-96736124164692 `" ) && sleep 0'
Using module file /usr/lib/python3.12/site-packages/ansible_collections/community/general/plugins/modules/apk.py
<127.0.0.1> PUT /root/.ansible/tmp/ansible-local-49vqigmv9t/tmpni2l3hog TO /root/.ansible/tmp/ansible-tmp-1755282926.8335867-52-96736124164692/AnsiballZ_apk.py
<127.0.0.1> EXEC /bin/sh -c 'chmod u+rwx /root/.ansible/tmp/ansible-tmp-1755282926.8335867-52-96736124164692/ /root/.ansible/tmp/ansible-tmp-1755282926.8335867-52-96736124164692/AnsiballZ_apk.py && sleep 0'
<127.0.0.1> EXEC /bin/sh -c '/usr/bin/python3 /root/.ansible/tmp/ansible-tmp-1755282926.8335867-52-96736124164692/AnsiballZ_apk.py && sleep 0'
<127.0.0.1> EXEC /bin/sh -c 'rm -f -r /root/.ansible/tmp/ansible-tmp-1755282926.8335867-52-96736124164692/ > /dev/null 2>&1 && sleep 0'
The full traceback is:
Traceback (most recent call last):
  File "/root/.ansible/tmp/ansible-tmp-1755282926.8335867-52-96736124164692/AnsiballZ_apk.py", line 107, in <module>
    _ansiballz_main()
  File "/root/.ansible/tmp/ansible-tmp-1755282926.8335867-52-96736124164692/AnsiballZ_apk.py", line 99, in _ansiballz_main
    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)
  File "/root/.ansible/tmp/ansible-tmp-1755282926.8335867-52-96736124164692/AnsiballZ_apk.py", line 47, in invoke_module
    runpy.run_module(mod_name='ansible_collections.community.general.plugins.modules.apk', init_globals=dict(_module_fqn='ansible_collections.community.general.plugins.modules.apk', _modlib_path=modlib_path),
  File "<frozen runpy>", line 226, in run_module
  File "<frozen runpy>", line 98, in _run_module_code
  File "<frozen runpy>", line 88, in _run_code
  File "/tmp/ansible_community.general.apk_payload_yf5tz74p/ansible_community.general.apk_payload.zip/ansible_collections/community/general/plugins/modules/apk.py", line 384, in <module>
  File "/tmp/ansible_community.general.apk_payload_yf5tz74p/ansible_community.general.apk_payload.zip/ansible_collections/community/general/plugins/modules/apk.py", line 378, in main
  File "/tmp/ansible_community.general.apk_payload_yf5tz74p/ansible_community.general.apk_payload.zip/ansible_collections/community/general/plugins/modules/apk.py", line 269, in install_packages
  File "/tmp/ansible_community.general.apk_payload_yf5tz74p/ansible_community.general.apk_payload.zip/ansible_collections/community/general/plugins/modules/apk.py", line 230, in query_virtual
  File "/tmp/ansible_community.general.apk_payload_yf5tz74p/ansible_community.general.apk_payload.zip/ansible/module_utils/basic.py", line 1856, in run_command
  File "/usr/lib/python3.12/shlex.py", line 313, in split
    return list(lex)
           ^^^^^^^^^
  File "/usr/lib/python3.12/shlex.py", line 300, in __next__
    token = self.get_token()
            ^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/shlex.py", line 109, in get_token
    raw = self.read_token()
          ^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/shlex.py", line 191, in read_token
    raise ValueError("No closing quotation")
ValueError: No closing quotation
fatal: [localhost]: FAILED! => {
    "changed": false,
    "module_stderr": "Traceback (most recent call last):\n  File \"/root/.ansible/tmp/ansible-tmp-1755282926.8335867-52-96736124164692/AnsiballZ_apk.py\", line 107, in <module>\n    _ansiballz_main()\n  File \"/root/.ansible/tmp/ansible-tmp-1755282926.8335867-52-96736124164692/AnsiballZ_apk.py\", line 99, in _ansiballz_main\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n  File \"/root/.ansible/tmp/ansible-tmp-1755282926.8335867-52-96736124164692/AnsiballZ_apk.py\", line 47, in invoke_module\n    runpy.run_module(mod_name='ansible_collections.community.general.plugins.modules.apk', init_globals=dict(_module_fqn='ansible_collections.community.general.plugins.modules.apk', _modlib_path=modlib_path),\n  File \"<frozen runpy>\", line 226, in run_module\n  File \"<frozen runpy>\", line 98, in _run_module_code\n  File \"<frozen runpy>\", line 88, in _run_code\n  File \"/tmp/ansible_community.general.apk_payload_yf5tz74p/ansible_community.general.apk_payload.zip/ansible_collections/community/general/plugins/modules/apk.py\", line 384, in <module>\n  File \"/tmp/ansible_community.general.apk_payload_yf5tz74p/ansible_community.general.apk_payload.zip/ansible_collections/community/general/plugins/modules/apk.py\", line 378, in main\n  File \"/tmp/ansible_community.general.apk_payload_yf5tz74p/ansible_community.general.apk_payload.zip/ansible_collections/community/general/plugins/modules/apk.py\", line 269, in install_packages\n  File \"/tmp/ansible_community.general.apk_payload_yf5tz74p/ansible_community.general.apk_payload.zip/ansible_collections/community/general/plugins/modules/apk.py\", line 230, in query_virtual\n  File \"/tmp/ansible_community.general.apk_payload_yf5tz74p/ansible_community.general.apk_payload.zip/ansible/module_utils/basic.py\", line 1856, in run_command\n  File \"/usr/lib/python3.12/shlex.py\", line 313, in split\n    return list(lex)\n           ^^^^^^^^^\n  File \"/usr/lib/python3.12/shlex.py\", line 300, in __next__\n    token = self.get_token()\n            ^^^^^^^^^^^^^^^^\n  File \"/usr/lib/python3.12/shlex.py\", line 109, in get_token\n    raw = self.read_token()\n          ^^^^^^^^^^^^^^^^^\n  File \"/usr/lib/python3.12/shlex.py\", line 191, in read_token\n    raise ValueError(\"No closing quotation\")\nValueError: No closing quotation\n",
    "module_stdout": "",
    "msg": "MODULE FAILURE: No start of json char found\nSee stdout/stderr for the exact error",
    "rc": 1
}

PLAY RECAP ****************************************************************************************************************************
localhost                  : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0  

Code of Conduct

  • I agree to follow the Ansible Code of Conduct

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugThis issue/PR relates to a bugmodulemodulepluginsplugin (any type)traceback

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions