Moderate severityNVD Advisory· Published Sep 11, 2020· Updated Aug 4, 2024
CVE-2020-14332
CVE-2020-14332
Description
A flaw was found in the Ansible Engine when using module_args. Tasks executed with check mode (--check-mode) do not properly neutralize sensitive data exposed in the event data. This flaw allows unauthorized users to read this data. The highest threat from this vulnerability is to confidentiality.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
ansiblePyPI | < 2.8.14 | 2.8.14 |
ansiblePyPI | >= 2.9.0a1, < 2.9.12 | 2.9.12 |
ansiblePyPI | >= 2.10.0a1, < 2.10.1rc2 | 2.10.1rc2 |
Affected products
1Patches
3714cd2ad2effcopy - redact 'content' from invocation in check mode (#71033) (#71067)
4 files changed · +98 −2
changelogs/fragments/copy-sanitize-check-mode-invocation-args.yaml+7 −0 added@@ -0,0 +1,7 @@ +security_fixes: +- > + **security issue** - copy - Redact the value of the no_log 'content' + parameter in the result's invocation.module_args in check mode. + Previously when used with check mode and with '-vvv', the module + would not censor the content if a change would be made to the + destination path. (CVE-2020-14332)
lib/ansible/plugins/action/copy.py+7 −2 modified@@ -209,6 +209,8 @@ def _ensure_invocation(self, result): # NOTE: adding invocation arguments here needs to be kept in sync with # any no_log specified in the argument_spec in the module. # This is not automatic. + # NOTE: do not add to this. This should be made a generic function for action plugins. + # This should also use the same argspec as the module instead of keeping it in sync. if 'invocation' not in result: if self._play_context.no_log: result['invocation'] = "CENSORED: no_log is set" @@ -218,8 +220,11 @@ def _ensure_invocation(self, result): result['invocation'] = self._task.args.copy() result['invocation']['module_args'] = self._task.args.copy() - if isinstance(result['invocation'], dict) and 'content' in result['invocation']: - result['invocation']['content'] = 'CENSORED: content is a no_log parameter' + if isinstance(result['invocation'], dict): + if 'content' in result['invocation']: + result['invocation']['content'] = 'CENSORED: content is a no_log parameter' + if result['invocation'].get('module_args', {}).get('content') is not None: + result['invocation']['module_args']['content'] = 'VALUE_SPECIFIED_IN_NO_LOG_PARAMETER' return result
test/integration/targets/copy/tasks/main.yml+2 −0 modified@@ -74,6 +74,8 @@ - import_tasks: acls.yml when: ansible_system == 'Linux' + - import_tasks: no_log.yml + - import_tasks: check_mode.yml # https://github.com/ansible/ansible/issues/57618
test/integration/targets/copy/tasks/no_log.yml+82 −0 added@@ -0,0 +1,82 @@ +- block: + + - set_fact: + dest: "{{ local_temp_dir }}/test_no_log" + + - name: ensure playbook and dest files don't exist yet + file: + path: "{{ item }}" + state: absent + loop: + - "{{ local_temp_dir }}/test_no_log.yml" + - "{{ dest }}" + + - name: create a playbook to run with command + copy: + dest: "{{local_temp_dir}}/test_no_log.yml" + content: !unsafe | + --- + - hosts: localhost + gather_facts: no + tasks: + - copy: + dest: "{{ dest }}" + content: "{{ secret }}" + + - name: copy the secret while using -vvv and check mode + command: "ansible-playbook {{local_temp_dir}}/test_no_log.yml -vvv -e secret=SECRET -e dest={{dest}} --check" + register: result + + - assert: + that: + - "'SECRET' not in result.stdout" + + - name: copy the secret while using -vvv + command: "ansible-playbook {{local_temp_dir}}/test_no_log.yml -vvv -e secret=SECRET -e dest={{dest}}" + register: result + + - assert: + that: + - "'SECRET' not in result.stdout" + + - name: copy the secret while using -vvv and check mode again + command: "ansible-playbook {{local_temp_dir}}/test_no_log.yml -vvv -e secret=SECRET -e dest={{dest}} --check" + register: result + + - assert: + that: + - "'SECRET' not in result.stdout" + + - name: copy the secret while using -vvv again + command: "ansible-playbook {{local_temp_dir}}/test_no_log.yml -vvv -e secret=SECRET -e dest={{dest}}" + register: result + + - assert: + that: + - "'SECRET' not in result.stdout" + + - name: copy a new secret while using -vvv and check mode + command: "ansible-playbook {{local_temp_dir}}/test_no_log.yml -vvv -e secret=NEWSECRET -e dest={{dest}} --check" + register: result + + - assert: + that: + - "'NEWSECRET' not in result.stdout" + + - name: copy a new secret while using -vvv + command: "ansible-playbook {{local_temp_dir}}/test_no_log.yml -vvv -e secret=NEWSECRET -e dest={{dest}}" + register: result + + - assert: + that: + - "'NEWSECRET' not in result.stdout" + + always: + + - name: remove temp test files + file: + path: "{{ item }}" + state: absent + loop: + - "{{ local_temp_dir }}/test_no_log.yml" + - "{{ dest }}"
291f94934c8ccopy - redact 'content' from invocation in check mode (#71033) (#71069)
4 files changed · +98 −2
changelogs/fragments/copy-sanitize-check-mode-invocation-args.yaml+7 −0 added@@ -0,0 +1,7 @@ +security_fixes: +- > + **security issue** - copy - Redact the value of the no_log 'content' + parameter in the result's invocation.module_args in check mode. + Previously when used with check mode and with '-vvv', the module + would not censor the content if a change would be made to the + destination path. (CVE-2020-14332)
lib/ansible/plugins/action/copy.py+7 −2 modified@@ -212,6 +212,8 @@ def _ensure_invocation(self, result): # NOTE: adding invocation arguments here needs to be kept in sync with # any no_log specified in the argument_spec in the module. # This is not automatic. + # NOTE: do not add to this. This should be made a generic function for action plugins. + # This should also use the same argspec as the module instead of keeping it in sync. if 'invocation' not in result: if self._play_context.no_log: result['invocation'] = "CENSORED: no_log is set" @@ -221,8 +223,11 @@ def _ensure_invocation(self, result): result['invocation'] = self._task.args.copy() result['invocation']['module_args'] = self._task.args.copy() - if isinstance(result['invocation'], dict) and 'content' in result['invocation']: - result['invocation']['content'] = 'CENSORED: content is a no_log parameter' + if isinstance(result['invocation'], dict): + if 'content' in result['invocation']: + result['invocation']['content'] = 'CENSORED: content is a no_log parameter' + if result['invocation'].get('module_args', {}).get('content') is not None: + result['invocation']['module_args']['content'] = 'VALUE_SPECIFIED_IN_NO_LOG_PARAMETER' return result
test/integration/targets/copy/tasks/main.yml+2 −0 modified@@ -61,6 +61,8 @@ - import_tasks: acls.yml when: ansible_system == 'Linux' + - import_tasks: no_log.yml + # https://github.com/ansible/ansible/issues/57618 - name: Test diff contents copy:
test/integration/targets/copy/tasks/no_log.yml+82 −0 added@@ -0,0 +1,82 @@ +- block: + + - set_fact: + dest: "{{ local_temp_dir }}/test_no_log" + + - name: ensure playbook and dest files don't exist yet + file: + path: "{{ item }}" + state: absent + loop: + - "{{ local_temp_dir }}/test_no_log.yml" + - "{{ dest }}" + + - name: create a playbook to run with command + copy: + dest: "{{local_temp_dir}}/test_no_log.yml" + content: !unsafe | + --- + - hosts: localhost + gather_facts: no + tasks: + - copy: + dest: "{{ dest }}" + content: "{{ secret }}" + + - name: copy the secret while using -vvv and check mode + command: "ansible-playbook {{local_temp_dir}}/test_no_log.yml -vvv -e secret=SECRET -e dest={{dest}} --check" + register: result + + - assert: + that: + - "'SECRET' not in result.stdout" + + - name: copy the secret while using -vvv + command: "ansible-playbook {{local_temp_dir}}/test_no_log.yml -vvv -e secret=SECRET -e dest={{dest}}" + register: result + + - assert: + that: + - "'SECRET' not in result.stdout" + + - name: copy the secret while using -vvv and check mode again + command: "ansible-playbook {{local_temp_dir}}/test_no_log.yml -vvv -e secret=SECRET -e dest={{dest}} --check" + register: result + + - assert: + that: + - "'SECRET' not in result.stdout" + + - name: copy the secret while using -vvv again + command: "ansible-playbook {{local_temp_dir}}/test_no_log.yml -vvv -e secret=SECRET -e dest={{dest}}" + register: result + + - assert: + that: + - "'SECRET' not in result.stdout" + + - name: copy a new secret while using -vvv and check mode + command: "ansible-playbook {{local_temp_dir}}/test_no_log.yml -vvv -e secret=NEWSECRET -e dest={{dest}} --check" + register: result + + - assert: + that: + - "'NEWSECRET' not in result.stdout" + + - name: copy a new secret while using -vvv + command: "ansible-playbook {{local_temp_dir}}/test_no_log.yml -vvv -e secret=NEWSECRET -e dest={{dest}}" + register: result + + - assert: + that: + - "'NEWSECRET' not in result.stdout" + + always: + + - name: remove temp test files + file: + path: "{{ item }}" + state: absent + loop: + - "{{ local_temp_dir }}/test_no_log.yml" + - "{{ dest }}"
6cae9a4b168dcopy - redact 'content' from invocation in check mode (#71033) (#71068)
4 files changed · +98 −2
changelogs/fragments/copy-sanitize-check-mode-invocation-args.yaml+7 −0 added@@ -0,0 +1,7 @@ +security_fixes: +- > + **security issue** - copy - Redact the value of the no_log 'content' + parameter in the result's invocation.module_args in check mode. + Previously when used with check mode and with '-vvv', the module + would not censor the content if a change would be made to the + destination path. (CVE-2020-14332)
lib/ansible/plugins/action/copy.py+7 −2 modified@@ -212,6 +212,8 @@ def _ensure_invocation(self, result): # NOTE: adding invocation arguments here needs to be kept in sync with # any no_log specified in the argument_spec in the module. # This is not automatic. + # NOTE: do not add to this. This should be made a generic function for action plugins. + # This should also use the same argspec as the module instead of keeping it in sync. if 'invocation' not in result: if self._play_context.no_log: result['invocation'] = "CENSORED: no_log is set" @@ -221,8 +223,11 @@ def _ensure_invocation(self, result): result['invocation'] = self._task.args.copy() result['invocation']['module_args'] = self._task.args.copy() - if isinstance(result['invocation'], dict) and 'content' in result['invocation']: - result['invocation']['content'] = 'CENSORED: content is a no_log parameter' + if isinstance(result['invocation'], dict): + if 'content' in result['invocation']: + result['invocation']['content'] = 'CENSORED: content is a no_log parameter' + if result['invocation'].get('module_args', {}).get('content') is not None: + result['invocation']['module_args']['content'] = 'VALUE_SPECIFIED_IN_NO_LOG_PARAMETER' return result
test/integration/targets/copy/tasks/main.yml+2 −0 modified@@ -74,6 +74,8 @@ - import_tasks: acls.yml when: ansible_system == 'Linux' + - import_tasks: no_log.yml + # https://github.com/ansible/ansible/issues/57618 - name: Test diff contents copy:
test/integration/targets/copy/tasks/no_log.yml+82 −0 added@@ -0,0 +1,82 @@ +- block: + + - set_fact: + dest: "{{ local_temp_dir }}/test_no_log" + + - name: ensure playbook and dest files don't exist yet + file: + path: "{{ item }}" + state: absent + loop: + - "{{ local_temp_dir }}/test_no_log.yml" + - "{{ dest }}" + + - name: create a playbook to run with command + copy: + dest: "{{local_temp_dir}}/test_no_log.yml" + content: !unsafe | + --- + - hosts: localhost + gather_facts: no + tasks: + - copy: + dest: "{{ dest }}" + content: "{{ secret }}" + + - name: copy the secret while using -vvv and check mode + command: "ansible-playbook {{local_temp_dir}}/test_no_log.yml -vvv -e secret=SECRET -e dest={{dest}} --check" + register: result + + - assert: + that: + - "'SECRET' not in result.stdout" + + - name: copy the secret while using -vvv + command: "ansible-playbook {{local_temp_dir}}/test_no_log.yml -vvv -e secret=SECRET -e dest={{dest}}" + register: result + + - assert: + that: + - "'SECRET' not in result.stdout" + + - name: copy the secret while using -vvv and check mode again + command: "ansible-playbook {{local_temp_dir}}/test_no_log.yml -vvv -e secret=SECRET -e dest={{dest}} --check" + register: result + + - assert: + that: + - "'SECRET' not in result.stdout" + + - name: copy the secret while using -vvv again + command: "ansible-playbook {{local_temp_dir}}/test_no_log.yml -vvv -e secret=SECRET -e dest={{dest}}" + register: result + + - assert: + that: + - "'SECRET' not in result.stdout" + + - name: copy a new secret while using -vvv and check mode + command: "ansible-playbook {{local_temp_dir}}/test_no_log.yml -vvv -e secret=NEWSECRET -e dest={{dest}} --check" + register: result + + - assert: + that: + - "'NEWSECRET' not in result.stdout" + + - name: copy a new secret while using -vvv + command: "ansible-playbook {{local_temp_dir}}/test_no_log.yml -vvv -e secret=NEWSECRET -e dest={{dest}}" + register: result + + - assert: + that: + - "'NEWSECRET' not in result.stdout" + + always: + + - name: remove temp test files + file: + path: "{{ item }}" + state: absent + loop: + - "{{ local_temp_dir }}/test_no_log.yml" + - "{{ dest }}"
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
12- github.com/advisories/GHSA-j667-c2hm-f2wpghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2020-14332ghsaADVISORY
- www.debian.org/security/2021/dsa-4950ghsavendor-advisoryx_refsource_DEBIANWEB
- bugzilla.redhat.com/show_bug.cgighsax_refsource_CONFIRMWEB
- github.com/ansible/ansible/blob/stable-2.10/changelogs/CHANGELOG-v2.10.rstghsaWEB
- github.com/ansible/ansible/blob/stable-2.8/changelogs/CHANGELOG-v2.8.rstghsaWEB
- github.com/ansible/ansible/blob/stable-2.9/changelogs/CHANGELOG-v2.9.rstghsaWEB
- github.com/ansible/ansible/commit/291f94934c8c49eef85e6539087f2dfcd001fe4fghsaWEB
- github.com/ansible/ansible/commit/6cae9a4b168df776bf82deb04b2c62e00c38b49aghsaWEB
- github.com/ansible/ansible/commit/714cd2ad2eff7f003d728414afcb91591fad5d9aghsaWEB
- github.com/ansible/ansible/pull/71033ghsax_refsource_MISCWEB
- github.com/pypa/advisory-database/tree/main/vulns/ansible/PYSEC-2020-4.yamlghsaWEB
News mentions
0No linked articles in our index yet.