From 7ef3dc2b8b4428d20887777de3921c316feaf7be Mon Sep 17 00:00:00 2001 From: Rick Elrod Date: Thu, 15 Apr 2021 17:06:58 -0500 Subject: [PATCH] [controller ansiballz] escape directory regex (#74270) Change: - We were passing a directory name directly to re.compile(). If the directory isn't valid regex (or is) this can have odd side effects, such as crashing. - Fix a few other similar cases, but less likely to be a real issue. Test Plan: - New test Signed-off-by: Rick Elrod --- .../ansiballz-re-escape-site-packages.yml | 4 ++++ lib/ansible/executor/module_common.py | 2 +- lib/ansible/plugins/action/__init__.py | 3 ++- lib/ansible/vars/clean.py | 2 +- .../ansible/module_common_regex_regression.sh | 15 +++++++++++++++ test/integration/targets/ansible/runme.sh | 4 ++++ 6 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 changelogs/fragments/ansiballz-re-escape-site-packages.yml create mode 100755 test/integration/targets/ansible/module_common_regex_regression.sh diff --git a/changelogs/fragments/ansiballz-re-escape-site-packages.yml b/changelogs/fragments/ansiballz-re-escape-site-packages.yml new file mode 100644 index 00000000000..223ea04158d --- /dev/null +++ b/changelogs/fragments/ansiballz-re-escape-site-packages.yml @@ -0,0 +1,4 @@ +bugfixes: + - >- + ansiballz - avoid treating path to site_packages as regex; escape it. + This prevents a crash when ansible is installed to, or running from, an oddly named directory like ``ansi[ble`` diff --git a/lib/ansible/executor/module_common.py b/lib/ansible/executor/module_common.py index cdfa139d8d6..81cce0cc75d 100644 --- a/lib/ansible/executor/module_common.py +++ b/lib/ansible/executor/module_common.py @@ -417,7 +417,7 @@ else: # Do this instead of getting site-packages from distutils.sysconfig so we work when we # haven't been installed site_packages = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) -CORE_LIBRARY_PATH_RE = re.compile(r'%s/(?Pansible/modules/.*)\.(py|ps1)$' % site_packages) +CORE_LIBRARY_PATH_RE = re.compile(r'%s/(?Pansible/modules/.*)\.(py|ps1)$' % re.escape(site_packages)) COLLECTION_PATH_RE = re.compile(r'/(?Pansible_collections/[^/]+/[^/]+/plugins/modules/.*)\.(py|ps1)$') # Detect new-style Python modules by looking for required imports: diff --git a/lib/ansible/plugins/action/__init__.py b/lib/ansible/plugins/action/__init__.py index 9f5103be675..ed3f3eff13b 100644 --- a/lib/ansible/plugins/action/__init__.py +++ b/lib/ansible/plugins/action/__init__.py @@ -1173,7 +1173,8 @@ class ActionBase(with_metaclass(ABCMeta, object)): # try to figure out if we are missing interpreter if self._used_interpreter is not None: - match = re.compile('%s: (?:No such file or directory|not found)' % self._used_interpreter.lstrip('!#')) + interpreter = re.escape(self._used_interpreter.lstrip('!#')) + match = re.compile('%s: (?:No such file or directory|not found)' % interpreter) if match.search(data['module_stderr']) or match.search(data['module_stdout']): data['msg'] = "The module failed to execute correctly, you probably need to set the interpreter." diff --git a/lib/ansible/vars/clean.py b/lib/ansible/vars/clean.py index 4b89b7b429b..334197acdf3 100644 --- a/lib/ansible/vars/clean.py +++ b/lib/ansible/vars/clean.py @@ -133,7 +133,7 @@ def clean_facts(facts): # next we remove any connection plugin specific vars for conn_path in connection_loader.all(path_only=True): conn_name = os.path.splitext(os.path.basename(conn_path))[0] - re_key = re.compile('^ansible_%s_' % conn_name) + re_key = re.compile('^ansible_%s_' % re.escape(conn_name)) for fact_key in fact_keys: # most lightweight VM or container tech creates devices with this pattern, this avoids filtering them out if (re_key.match(fact_key) and not fact_key.endswith(('_bridge', '_gwbridge'))) or fact_key.startswith('ansible_become_'): diff --git a/test/integration/targets/ansible/module_common_regex_regression.sh b/test/integration/targets/ansible/module_common_regex_regression.sh new file mode 100755 index 00000000000..4869f4f0bf7 --- /dev/null +++ b/test/integration/targets/ansible/module_common_regex_regression.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +# #74270 -- ensure we escape directory names before passing to re.compile() +# particularly in module_common. + +set -eux + +lib_path=$(python -c 'import os, ansible; print(os.path.dirname(os.path.dirname(ansible.__file__)))') +bad_dir="${OUTPUT_DIR}/ansi[ble" + +mkdir "${bad_dir}" +cp -a "${lib_path}" "${bad_dir}" + +PYTHONPATH="${bad_dir}/lib" ansible -m ping localhost -i ../../inventory "$@" +rm -rf "${bad_dir}" diff --git a/test/integration/targets/ansible/runme.sh b/test/integration/targets/ansible/runme.sh index fc79e33e73b..e9e72a9fec6 100755 --- a/test/integration/targets/ansible/runme.sh +++ b/test/integration/targets/ansible/runme.sh @@ -80,3 +80,7 @@ if ansible-playbook -i ../../inventory --extra-vars ./vars.yml playbook.yml; the fi ansible-playbook -i ../../inventory --extra-vars @./vars.yml playbook.yml + +# #74270 -- ensure we escape directory names before passing to re.compile() +# particularly in module_common. +bash module_common_regex_regression.sh