From 6d0aeac1e166842f2833f4fb64c727cc7f818118 Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Tue, 4 Oct 2022 15:44:00 +0200 Subject: [PATCH] Do not crash templating when filter/test name is not a valid Ansible plugin name (#78913) * Do not crash templating when filter/test name is not a valid Ansible plugin name. * Store and re-raise KeyError if there was one. Co-authored-by: s-hertel <19572925+s-hertel@users.noreply.github.com> --- .../78913-template-missing-filter-test.yml | 2 ++ lib/ansible/template/__init__.py | 10 +++++++--- .../targets/templating/tasks/main.yml | 17 +++++++++++++++++ .../templating/templates/invalid_test_name.j2 | 1 + 4 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 changelogs/fragments/78913-template-missing-filter-test.yml create mode 100644 test/integration/targets/templating/templates/invalid_test_name.j2 diff --git a/changelogs/fragments/78913-template-missing-filter-test.yml b/changelogs/fragments/78913-template-missing-filter-test.yml new file mode 100644 index 00000000000..a4957fe6aeb --- /dev/null +++ b/changelogs/fragments/78913-template-missing-filter-test.yml @@ -0,0 +1,2 @@ +bugfixes: + - "Do not crash when templating an expression with a test or filter that is not a valid Ansible filter name (https://github.com/ansible/ansible/issues/78912, https://github.com/ansible/ansible/pull/78913)." diff --git a/lib/ansible/template/__init__.py b/lib/ansible/template/__init__.py index 32e68547358..1498d3f8134 100644 --- a/lib/ansible/template/__init__.py +++ b/lib/ansible/template/__init__.py @@ -423,12 +423,13 @@ class JinjaPluginIntercept(MutableMapping): if not isinstance(key, string_types): raise ValueError('key must be a string, got %s instead' % type(key)) + original_exc = None if key not in self._loaded_builtins: plugin = None try: plugin = self._pluginloader.get(key) except (AnsibleError, KeyError) as e: - raise TemplateSyntaxError('Could not load "%s": %s' % (key, to_native(e)), 0) + original_exc = e except Exception as e: display.vvvv('Unexpected plugin load (%s) exception: %s' % (key, to_native(e))) raise e @@ -439,8 +440,11 @@ class JinjaPluginIntercept(MutableMapping): self._delegatee[key] = plugin.j2_function self._loaded_builtins.add(key) - # let it trigger keyerror if we could not find ours or jinja2 one - func = self._delegatee[key] + # raise template syntax error if we could not find ours or jinja2 one + try: + func = self._delegatee[key] + except KeyError as e: + raise TemplateSyntaxError('Could not load "%s": %s' % (key, to_native(original_exc or e)), 0) # if i do have func and it is a filter, it nees wrapping if self._pluginloader.type == 'filter': diff --git a/test/integration/targets/templating/tasks/main.yml b/test/integration/targets/templating/tasks/main.yml index f0d3e93ccce..312e171de37 100644 --- a/test/integration/targets/templating/tasks/main.yml +++ b/test/integration/targets/templating/tasks/main.yml @@ -16,3 +16,20 @@ vars: # Kind of hack to just send a JSON string through jinja, by templating out nothing foo: '{{ "" }}{"null": null, "true": true, "false": false}' + +- name: Make sure that test with name that isn't a valid Ansible plugin name does not result in a crash (1/2) + set_fact: + foo: '{{ [{"failed": false}] | selectattr("failed", "==", true) }}' + +- name: Make sure that test with name that isn't a valid Ansible plugin name does not result in a crash (2/2) + template: + src: invalid_test_name.j2 + dest: /tmp/foo + ignore_errors: true + register: result + +- assert: + that: + - result is failed + - >- + "TemplateSyntaxError: Could not load \"asdf \": 'invalid plugin name: ansible.builtin.asdf '" in result.msg diff --git a/test/integration/targets/templating/templates/invalid_test_name.j2 b/test/integration/targets/templating/templates/invalid_test_name.j2 new file mode 100644 index 00000000000..98b836f14fc --- /dev/null +++ b/test/integration/targets/templating/templates/invalid_test_name.j2 @@ -0,0 +1 @@ +{{ [{"failed": false}] | selectattr("failed", "asdf ", true) }}