From 48c08f410cd368b129fed61f9a58a0cc2b1df458 Mon Sep 17 00:00:00 2001 From: Brian Coca Date: Tue, 10 Nov 2020 10:48:20 -0500 Subject: [PATCH] allow any type of unsafe data (#72547) * allow any type of unsafe data dont limit to strings --- changelogs/fragments/unsafe_for_all.yml | 2 ++ lib/ansible/parsing/yaml/constructor.py | 16 ++++++--- .../targets/yaml_parsing/tasks/main.yml | 4 +++ .../targets/yaml_parsing/tasks/unsafe.yml | 36 +++++++++++++++++++ .../_data/sanity/yamllint/yamllinter.py | 14 +++++++- 5 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 changelogs/fragments/unsafe_for_all.yml create mode 100644 test/integration/targets/yaml_parsing/tasks/unsafe.yml diff --git a/changelogs/fragments/unsafe_for_all.yml b/changelogs/fragments/unsafe_for_all.yml new file mode 100644 index 00000000000..805000ac122 --- /dev/null +++ b/changelogs/fragments/unsafe_for_all.yml @@ -0,0 +1,2 @@ +minor_changes: + - now !unsafe works on all types of data, not just strings, even recursively for mappings and sequences. diff --git a/lib/ansible/parsing/yaml/constructor.py b/lib/ansible/parsing/yaml/constructor.py index 208286e49c7..12e271093a2 100644 --- a/lib/ansible/parsing/yaml/constructor.py +++ b/lib/ansible/parsing/yaml/constructor.py @@ -24,11 +24,10 @@ from yaml.nodes import MappingNode from ansible import constants as C from ansible.module_utils._text import to_bytes, to_native -from ansible.parsing.yaml.objects import AnsibleMapping, AnsibleSequence, AnsibleUnicode -from ansible.parsing.yaml.objects import AnsibleVaultEncryptedUnicode -from ansible.utils.unsafe_proxy import wrap_var +from ansible.parsing.yaml.objects import AnsibleMapping, AnsibleSequence, AnsibleUnicode, AnsibleVaultEncryptedUnicode from ansible.parsing.vault import VaultLib from ansible.utils.display import Display +from ansible.utils.unsafe_proxy import wrap_var display = Display() @@ -121,7 +120,16 @@ class AnsibleConstructor(SafeConstructor): data.ansible_pos = self._node_position_info(node) def construct_yaml_unsafe(self, node): - return wrap_var(self.construct_yaml_str(node)) + try: + constructor = getattr(node, 'id', 'object') + if constructor is not None: + constructor = getattr(self, 'construct_%s' % constructor) + except AttributeError: + constructor = self.construct_object + + value = constructor(node) + + return wrap_var(value) def _node_position_info(self, node): # the line number where the previous token has ended (plus empty lines) diff --git a/test/integration/targets/yaml_parsing/tasks/main.yml b/test/integration/targets/yaml_parsing/tasks/main.yml index 9446e0d9066..7d9c4aa63dc 100644 --- a/test/integration/targets/yaml_parsing/tasks/main.yml +++ b/test/integration/targets/yaml_parsing/tasks/main.yml @@ -31,3 +31,7 @@ that: - '"found a duplicate dict key (foo)" not in duplicate_ignore.stderr' - duplicate_ignore.rc == 0 + + +- name: test unsafe YAMLism + import_tasks: unsafe.yml diff --git a/test/integration/targets/yaml_parsing/tasks/unsafe.yml b/test/integration/targets/yaml_parsing/tasks/unsafe.yml new file mode 100644 index 00000000000..8d9d627b726 --- /dev/null +++ b/test/integration/targets/yaml_parsing/tasks/unsafe.yml @@ -0,0 +1,36 @@ +- name: ensure no templating unsafe + block: + - name: check unsafe string + assert: + that: + - regstr != resolved + - "'Fail' not in regstr" + - "'{' in regstr" + - "'}' in regstr" + vars: + regstr: !unsafe b{{nottemplate}} + + - name: check unsafe string in list + assert: + that: + - ulist[0] != resolved + - "'Fail' not in ulist[0]" + - "'{' in ulist[0]" + - "'}' in ulist[0]" + vars: + ulist: !unsafe [ 'b{{nottemplate}}', 'c', 'd'] + + - name: check unsafe string in dict + assert: + that: + - udict['a'] != resolved + - "'Fail' not in udict['a']" + - "'{' in udict['a']" + - "'}' in udict['a']" + vars: + udict: !unsafe + a: b{{nottemplate}} + c: d + vars: + nottemplate: FAIL + resolved: 'b{{nottemplate}}' diff --git a/test/lib/ansible_test/_data/sanity/yamllint/yamllinter.py b/test/lib/ansible_test/_data/sanity/yamllint/yamllinter.py index ca21cfcbd82..04533d4c69a 100644 --- a/test/lib/ansible_test/_data/sanity/yamllint/yamllinter.py +++ b/test/lib/ansible_test/_data/sanity/yamllint/yamllinter.py @@ -31,10 +31,22 @@ def main(): class TestConstructor(SafeConstructor): """Yaml Safe Constructor that knows about Ansible tags""" + def construct_yaml_unsafe(self, node): + try: + constructor = getattr(node, 'id', 'object') + if constructor is not None: + constructor = getattr(self, 'construct_%s' % constructor) + except AttributeError: + constructor = self.construct_object + + value = constructor(node) + + return value + TestConstructor.add_constructor( u'!unsafe', - TestConstructor.construct_yaml_str) + TestConstructor.construct_yaml_unsafe) TestConstructor.add_constructor(