From 1c765a6afc94cc04eaaee116c2afebf7a55572ef Mon Sep 17 00:00:00 2001 From: Matt Davis <6775756+nitzmahone@users.noreply.github.com> Date: Wed, 9 Aug 2023 09:25:20 -0700 Subject: [PATCH] restore conditional lookup nerfing (#81460) * a recent optimization lost the unsafe lookup disable behavior when templating conditionals with inline templates that referred to untrusted values * added regression test to catch this case --- lib/ansible/playbook/conditional.py | 9 +++++-- .../integration/targets/conditionals/play.yml | 26 +++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/lib/ansible/playbook/conditional.py b/lib/ansible/playbook/conditional.py index bf2419e4b0f..4bb8ef22afa 100644 --- a/lib/ansible/playbook/conditional.py +++ b/lib/ansible/playbook/conditional.py @@ -101,10 +101,15 @@ class Conditional: elif conditional == "": return False + # If the result of the first-pass template render (to resolve inline templates) is marked unsafe, + # explicitly disable lookups on the final pass to prevent evaluation of untrusted content in the + # constructed template. + disable_lookups = hasattr(conditional, '__UNSAFE__') + # NOTE The spaces around True and False are intentional to short-circuit literal_eval for # jinja2_native=False and avoid its expensive calls. return templar.template( - "{%% if %s %%} True {%% else %%} False {%% endif %%}" % conditional - ).strip() == "True" + "{%% if %s %%} True {%% else %%} False {%% endif %%}" % conditional, + disable_lookups=disable_lookups).strip() == "True" except AnsibleUndefinedVariable as e: raise AnsibleUndefinedVariable("error while evaluating conditional (%s): %s" % (original, e)) diff --git a/test/integration/targets/conditionals/play.yml b/test/integration/targets/conditionals/play.yml index 455818c97c0..56ec843854f 100644 --- a/test/integration/targets/conditionals/play.yml +++ b/test/integration/targets/conditionals/play.yml @@ -665,3 +665,29 @@ - item loop: - 1 == 1 + + - set_fact: + sentinel_file: '{{ lookup("env", "OUTPUT_DIR")}}/LOOKUP_SIDE_EFFECT.txt' + + - name: ensure sentinel file is absent + file: + path: '{{ sentinel_file }}' + state: absent + - name: get an untrusted var that's a valid Jinja expression with a side-effect + shell: | + echo "lookup('pipe', 'echo bang > \"$SENTINEL_FILE\" && cat \"$SENTINEL_FILE\"')" + environment: + SENTINEL_FILE: '{{ sentinel_file }}' + register: untrusted_expr + - name: use a conditional with an inline template that refers to the untrusted expression + debug: + msg: look at some seemingly innocuous stuff + when: '"foo" in {{ untrusted_expr.stdout }}' + ignore_errors: true + - name: ensure the untrusted expression side-effect has not executed + stat: + path: '{{ sentinel_file }}' + register: sentinel_stat + - assert: + that: + - not sentinel_stat.stat.exists