From d2d1f01f9d067e82b6af0da156a24627d04b396c Mon Sep 17 00:00:00 2001 From: Brian Coca Date: Wed, 19 May 2021 16:14:52 -0400 Subject: [PATCH] avoid literal quoting in config lists (#74740) * added tests --- changelogs/fragments/config_lists_unquote.yml | 2 + lib/ansible/config/manager.py | 2 +- .../targets/config/lookup_plugins/types.py | 66 +++++++++++++++++++ test/integration/targets/config/runme.sh | 3 + .../targets/config/type_munging.cfg | 8 +++ test/integration/targets/config/types.yml | 25 +++++++ 6 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 changelogs/fragments/config_lists_unquote.yml create mode 100644 test/integration/targets/config/lookup_plugins/types.py create mode 100644 test/integration/targets/config/type_munging.cfg create mode 100644 test/integration/targets/config/types.yml diff --git a/changelogs/fragments/config_lists_unquote.yml b/changelogs/fragments/config_lists_unquote.yml new file mode 100644 index 00000000000..960539d4ffc --- /dev/null +++ b/changelogs/fragments/config_lists_unquote.yml @@ -0,0 +1,2 @@ +bugfixes: + - config, ensure 'quoted' lists from ini or env do not take the quotes literally as part of the list item. diff --git a/lib/ansible/config/manager.py b/lib/ansible/config/manager.py index d1503218de6..5adad38b21a 100644 --- a/lib/ansible/config/manager.py +++ b/lib/ansible/config/manager.py @@ -92,7 +92,7 @@ def ensure_type(value, value_type, origin=None): elif value_type == 'list': if isinstance(value, string_types): - value = [x.strip() for x in value.split(',')] + value = [unquote(x.strip()) for x in value.split(',')] elif not isinstance(value, Sequence): errmsg = 'list' diff --git a/test/integration/targets/config/lookup_plugins/types.py b/test/integration/targets/config/lookup_plugins/types.py new file mode 100644 index 00000000000..a398f1b4480 --- /dev/null +++ b/test/integration/targets/config/lookup_plugins/types.py @@ -0,0 +1,66 @@ +# (c) 2021 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + +DOCUMENTATION = """ + name: types + author: Ansible Core Team + version_added: histerical + short_description: returns what you gave it + description: + - this is mostly a noop + options: + _terms: + description: stuff to pass through + valid: + description: does nothihng, just for testing values + type: list + ini: + - section: list_values + key: valid + mustunquote: + description: does nothihng, just for testing values + type: list + ini: + - section: list_values + key: mustunquote + notvalid: + description: does nothihng, just for testing values + type: list + ini: + - section: list_values + key: notvalid + totallynotvalid: + description: does nothihng, just for testing values + type: list + ini: + - section: list_values + key: totallynotvalid +""" + +EXAMPLES = """ +- name: like some other plugins, this is mostly useless + debug: msg={{ q('types', [1,2,3])}} +""" + +RETURN = """ + _list: + description: basically the same as you fed in + type: list + elements: raw +""" + +from ansible.plugins.lookup import LookupBase + + +class LookupModule(LookupBase): + + def run(self, terms, variables=None, **kwargs): + + self.set_options(var_options=variables, direct=kwargs) + + return terms diff --git a/test/integration/targets/config/runme.sh b/test/integration/targets/config/runme.sh index bbff6acc3ee..9162aa51032 100755 --- a/test/integration/targets/config/runme.sh +++ b/test/integration/targets/config/runme.sh @@ -21,3 +21,6 @@ ANSIBLE_CONFIG=inline_comment_ansible.cfg ansible-config dump --only-changed | g # test the config option validation ansible-playbook validation.yml "$@" + +# test types from config (just lists for now) +ANSIBLE_CONFIG=type_munging.cfg ansible-playbook types.yml "$@" diff --git a/test/integration/targets/config/type_munging.cfg b/test/integration/targets/config/type_munging.cfg new file mode 100644 index 00000000000..d6aeaab6fb0 --- /dev/null +++ b/test/integration/targets/config/type_munging.cfg @@ -0,0 +1,8 @@ +[defaults] +nothing = here + +[list_values] +valid = 1, 2, 3 +mustunquote = '1', '2', '3' +notvalid = [1, 2, 3] +totallynotvalid = ['1', '2', '3'] diff --git a/test/integration/targets/config/types.yml b/test/integration/targets/config/types.yml new file mode 100644 index 00000000000..650a96f6b1d --- /dev/null +++ b/test/integration/targets/config/types.yml @@ -0,0 +1,25 @@ +- hosts: localhost + gather_facts: false + tasks: + - name: ensures we got the list we expected + block: + - name: initialize plugin + debug: msg={{ lookup('types', 'starting test') }} + + - set_fact: + valid: '{{ lookup("config", "valid", plugin_type="lookup", plugin_name="types") }}' + mustunquote: '{{ lookup("config", "mustunquote", plugin_type="lookup", plugin_name="types") }}' + notvalid: '{{ lookup("config", "notvalid", plugin_type="lookup", plugin_name="types") }}' + totallynotvalid: '{{ lookup("config", "totallynotvalid", plugin_type="lookup", plugin_name="types") }}' + + - assert: + that: + - 'valid|type_debug == "list"' + - 'mustunquote|type_debug == "list"' + - 'notvalid|type_debug == "list"' + - 'totallynotvalid|type_debug == "list"' + - valid[0]|int == 1 + - mustunquote[0]|int == 1 + - "notvalid[0] == '[1'" + # using 'and true' to avoid quote hell + - totallynotvalid[0] == "['1'" and True