Index: head/sysutils/ansible/Makefile =================================================================== --- head/sysutils/ansible/Makefile +++ head/sysutils/ansible/Makefile @@ -2,8 +2,8 @@ # $FreeBSD$ PORTNAME= ansible -PORTVERSION?= 2.2.0.0 -PORTREVISION?= 3 +PORTVERSION?= 2.2.1.0 +PORTREVISION?= 0 CATEGORIES= sysutils python MASTER_SITES= http://releases.ansible.com/ansible/ @@ -18,11 +18,6 @@ ${PYTHON_PKGNAMEPREFIX}paramiko>0:security/py-paramiko \ ${PYTHON_PKGNAMEPREFIX}Jinja2>0:devel/py-Jinja2 -EXTRA_PATCHES?= ${FILESDIR}/extra-patch-872594b \ - ${FILESDIR}/extra-patch-ec84ff6 \ - ${FILESDIR}/extra-patch-eb8c26c \ - ${FILESDIR}/extra-patch-cc4634a - NO_ARCH= yes USES?= cpe python shebangfix USE_PYTHON= autoplist distutils Index: head/sysutils/ansible/distinfo =================================================================== --- head/sysutils/ansible/distinfo +++ head/sysutils/ansible/distinfo @@ -1,3 +1,3 @@ -TIMESTAMP = 1478009653 -SHA256 (ansible-2.2.0.0.tar.gz) = d9f198d293394ce0f8ba802561b21368888e3301036a706b5584641b49408586 -SIZE (ansible-2.2.0.0.tar.gz) = 2441851 +TIMESTAMP = 1484943834 +SHA256 (ansible-2.2.1.0.tar.gz) = 63a12ea784c0f90e43293b973d5c75263634c7415e463352846cd676c188e93f +SIZE (ansible-2.2.1.0.tar.gz) = 2511062 Index: head/sysutils/ansible/files/extra-patch-872594b =================================================================== --- head/sysutils/ansible/files/extra-patch-872594b +++ head/sysutils/ansible/files/extra-patch-872594b @@ -1,41 +0,0 @@ -From 872594b49a69a1f3795e0de3f7cf0194b6bdfd53 Mon Sep 17 00:00:00 2001 -From: Michael Scherer -Date: Sun, 23 Oct 2016 19:24:00 +0200 -Subject: [PATCH] Make service work when the service is not present in rc.conf - -After installing a package from the ports collection on a -fresh FreeBSD 11.0, Ansible was unable to enable it, failing with -"unable to get current rcvar value". Debugging showed that sysrc -didn't see the variable from /usr/local/etc/rc.d/myservice, but -adding the value was working. - -So we will just fallback to the default value if we can't find it. ---- - system/service.py | 6 ++++-- - 1 file changed, 4 insertions(+), 2 deletions(-) - -diff --git system/service.py system/service.py -index d216e68..c8781b1 100644 ---- lib/ansible/modules/core/system/service.py -+++ lib/ansible/modules/core/system/service.py -@@ -988,7 +988,7 @@ def service_enable(self): - # and hope for the best. - for rcvar in rcvars: - if '=' in rcvar: -- self.rcconf_key = rcvar.split('=')[0] -+ self.rcconf_key, default_rcconf_value = rcvar.split('=', 1) - break - - if self.rcconf_key is None: -@@ -997,8 +997,10 @@ def service_enable(self): - if self.sysrc_cmd: # FreeBSD >= 9.2 - - rc, current_rcconf_value, stderr = self.execute_command("%s -n %s" % (self.sysrc_cmd, self.rcconf_key)) -+ # it can happen that rcvar is not set (case of a system coming from the ports collection) -+ # so we will fallback on the default - if rc != 0: -- self.module.fail_json(msg="unable to get current rcvar value", stdout=stdout, stderr=stderr) -+ current_rcconf_value = default_rcconf_value - - if current_rcconf_value.strip().upper() != self.rcconf_value: - Index: head/sysutils/ansible/files/extra-patch-cc4634a =================================================================== --- head/sysutils/ansible/files/extra-patch-cc4634a +++ head/sysutils/ansible/files/extra-patch-cc4634a @@ -1,159 +0,0 @@ -From cc4634a5e73c06c6b4581f11171289ca9228391e Mon Sep 17 00:00:00 2001 -From: James Cammarata -Date: Tue, 10 Jan 2017 16:54:00 -0600 -Subject: [PATCH] Additional fixes for security related to CVE-2016-9587 - -(cherry picked from commit d316068831f9e08ef96833200ec7df2132263966) ---- - lib/ansible/playbook/conditional.py | 10 +++++----- - lib/ansible/template/__init__.py | 28 ++++++++++++++-------------- - 2 files changed, 19 insertions(+), 19 deletions(-) - -diff --git lib/ansible/playbook/conditional.py lib/ansible/playbook/conditional.py -index 99e377c..57e20a0 100644 ---- lib/ansible/playbook/conditional.py -+++ lib/ansible/playbook/conditional.py -@@ -104,7 +104,7 @@ def _check_conditional(self, conditional, templar, all_vars): - if conditional is None or conditional == '': - return True - -- if conditional in all_vars and '-' not in text_type(all_vars[conditional]): -+ if conditional in all_vars and re.match("^[_A-Za-z][_a-zA-Z0-9]*$", conditional): - conditional = all_vars[conditional] - - # make sure the templar is using the variables specified with this method -@@ -116,12 +116,12 @@ def _check_conditional(self, conditional, templar, all_vars): - return conditional - - # a Jinja2 evaluation that results in something Python can eval! -- if hasattr(conditional, '__UNSAFE__') and LOOKUP_REGEX.match(conditional): -- raise AnsibleError("The conditional '%s' contains variables which came from an unsafe " \ -- "source and also contains a lookup() call, failing conditional check" % conditional) -+ disable_lookups = False -+ if hasattr(conditional, '__UNSAFE__'): -+ disable_lookups = True - - presented = "{%% if %s %%} True {%% else %%} False {%% endif %%}" % conditional -- val = templar.template(presented).strip() -+ val = templar.template(presented, disable_lookups=disable_lookups).strip() - if val == "True": - return True - elif val == "False": -diff --git lib/ansible/template/__init__.py lib/ansible/template/__init__.py -index 53b2675..1a43486 100644 ---- lib/ansible/template/__init__.py -+++ lib/ansible/template/__init__.py -@@ -30,10 +30,8 @@ - from jinja2 import Environment - from jinja2.loaders import FileSystemLoader - from jinja2.exceptions import TemplateSyntaxError, UndefinedError --from jinja2.nodes import EvalContext - from jinja2.utils import concat as j2_concat - from jinja2.runtime import Context, StrictUndefined -- - from ansible import constants as C - from ansible.compat.six import string_types, text_type - from ansible.errors import AnsibleError, AnsibleFilterError, AnsibleUndefinedVariable -@@ -42,7 +40,6 @@ - from ansible.template.template import AnsibleJ2Template - from ansible.template.vars import AnsibleJ2Vars - from ansible.module_utils._text import to_native, to_text --from ansible.vars.unsafe_proxy import UnsafeProxy, wrap_var - - try: - from hashlib import sha1 -@@ -127,13 +124,6 @@ def _count_newlines_from_end(in_str): - # Uncommon cases: zero length string and string containing only newlines - return i - --class AnsibleEvalContext(EvalContext): -- ''' -- A custom jinja2 EvalContext, which is currently unused and saved -- here for possible future use. -- ''' -- pass -- - class AnsibleContext(Context): - ''' - A custom context, which intercepts resolve() calls and sets a flag -@@ -143,7 +133,6 @@ class AnsibleContext(Context): - ''' - def __init__(self, *args, **kwargs): - super(AnsibleContext, self).__init__(*args, **kwargs) -- self.eval_ctx = AnsibleEvalContext(self.environment, self.name) - self.unsafe = False - - def _is_unsafe(self, val): -@@ -335,7 +324,7 @@ def set_available_variables(self, variables): - self._available_variables = variables - self._cached_result = {} - -- def template(self, variable, convert_bare=False, preserve_trailing_newlines=True, escape_backslashes=True, fail_on_undefined=None, overrides=None, convert_data=True, static_vars = [''], cache = True, bare_deprecated=True): -+ def template(self, variable, convert_bare=False, preserve_trailing_newlines=True, escape_backslashes=True, fail_on_undefined=None, overrides=None, convert_data=True, static_vars = [''], cache = True, bare_deprecated=True, disable_lookups=False): - ''' - Templates (possibly recursively) any given data as input. If convert_bare is - set to True, the given data will be wrapped as a jinja2 variable ('{{foo}}') -@@ -391,6 +380,7 @@ def template(self, variable, convert_bare=False, preserve_trailing_newlines=True - escape_backslashes=escape_backslashes, - fail_on_undefined=fail_on_undefined, - overrides=overrides, -+ disable_lookups=disable_lookups, - ) - unsafe = hasattr(result, '__UNSAFE__') - if convert_data and not self._no_type_regex.match(variable) and not unsafe: -@@ -401,6 +391,7 @@ def template(self, variable, convert_bare=False, preserve_trailing_newlines=True - if eval_results[1] is None: - result = eval_results[0] - if unsafe: -+ from ansible.vars.unsafe_proxy import wrap_var - result = wrap_var(result) - else: - # FIXME: if the safe_eval raised an error, should we do something with it? -@@ -482,6 +473,9 @@ def _finalize(self, thing): - ''' - return thing if thing is not None else '' - -+ def _fail_lookup(self, name, *args, **kwargs): -+ raise AnsibleError("The lookup `%s` was found, however lookups were disabled from templating" % name) -+ - def _lookup(self, name, *args, **kwargs): - instance = self._lookup_loader.get(name.lower(), loader=self._loader, templar=self) - -@@ -501,6 +495,7 @@ def _lookup(self, name, *args, **kwargs): - ran = None - - if ran: -+ from ansible.vars.unsafe_proxy import UnsafeProxy, wrap_var - if wantlist: - ran = wrap_var(ran) - else: -@@ -516,7 +511,7 @@ def _lookup(self, name, *args, **kwargs): - else: - raise AnsibleError("lookup plugin (%s) not found" % name) - -- def _do_template(self, data, preserve_trailing_newlines=True, escape_backslashes=True, fail_on_undefined=None, overrides=None): -+ def _do_template(self, data, preserve_trailing_newlines=True, escape_backslashes=True, fail_on_undefined=None, overrides=None, disable_lookups=False): - # For preserving the number of input newlines in the output (used - # later in this method) - data_newlines = _count_newlines_from_end(data) -@@ -560,7 +555,11 @@ def do_template(self, data, preserve_trailing_newlines=True, escape_backslashes= - else: - return data - -- t.globals['lookup'] = self._lookup -+ if disable_lookups: -+ t.globals['lookup'] = self._fail_lookup -+ else: -+ t.globals['lookup'] = self._lookup -+ - t.globals['finalize'] = self._finalize - - jvars = AnsibleJ2Vars(self, t.globals) -@@ -571,6 +570,7 @@ def do_template(self, data, preserve_trailing_newlines=True, escape_backslashes= - try: - res = j2_concat(rf) - if new_context.unsafe: -+ from ansible.vars.unsafe_proxy import wrap_var - res = wrap_var(res) - except TypeError as te: - if 'StrictUndefined' in to_native(te): Index: head/sysutils/ansible/files/extra-patch-eb8c26c =================================================================== --- head/sysutils/ansible/files/extra-patch-eb8c26c +++ head/sysutils/ansible/files/extra-patch-eb8c26c @@ -1,60 +0,0 @@ -From eb8c26c105e8457b86324b64a13fac37d8862d47 Mon Sep 17 00:00:00 2001 -From: Computest -Date: Tue, 10 Jan 2017 16:51:40 -0600 -Subject: [PATCH] Fixing another corner case for security related to - CVE-2016-9587 - -(cherry picked from commit bcceada5d9b78ad77069c78226f8e9b336ff8949) ---- - lib/ansible/template/__init__.py | 6 +++--- - lib/ansible/vars/unsafe_proxy.py | 8 ++++++-- - 2 files changed, 9 insertions(+), 5 deletions(-) - -diff --git lib/ansible/template/__init__.py lib/ansible/template/__init__.py -index 4e24fbe..53b2675 100644 ---- lib/ansible/template/__init__.py -+++ lib/ansible/template/__init__.py -@@ -155,7 +155,7 @@ def _is_unsafe(self, val): - ''' - if isinstance(val, dict): - for key in val.keys(): -- if self._is_unsafe(val[key]): -+ if self._is_unsafe(key) or self._is_unsafe(val[key]): - return True - elif isinstance(val, list): - for item in val: -@@ -392,11 +392,11 @@ def template(self, variable, convert_bare=False, preserve_trailing_newlines=True - fail_on_undefined=fail_on_undefined, - overrides=overrides, - ) -- if convert_data and not self._no_type_regex.match(variable): -+ unsafe = hasattr(result, '__UNSAFE__') -+ if convert_data and not self._no_type_regex.match(variable) and not unsafe: - # if this looks like a dictionary or list, convert it to such using the safe_eval method - if (result.startswith("{") and not result.startswith(self.environment.variable_start_string)) or \ - result.startswith("[") or result in ("True", "False"): -- unsafe = hasattr(result, '__UNSAFE__') - eval_results = safe_eval(result, locals=self._available_variables, include_exceptions=True) - if eval_results[1] is None: - result = eval_results[0] -diff --git lib/ansible/vars/unsafe_proxy.py lib/ansible/vars/unsafe_proxy.py -index 426410a..4284705 100644 ---- lib/ansible/vars/unsafe_proxy.py -+++ lib/ansible/vars/unsafe_proxy.py -@@ -98,10 +98,14 @@ def decode(self, obj): - - - def _wrap_dict(v): -+ # Create new dict to get rid of the keys that are not wrapped. -+ new = {} - for k in v.keys(): - if v[k] is not None: -- v[wrap_var(k)] = wrap_var(v[k]) -- return v -+ new[wrap_var(k)] = wrap_var(v[k]) -+ else: -+ new[wrap_var(k)] = None -+ return new - - - def _wrap_list(v): Index: head/sysutils/ansible/files/extra-patch-ec84ff6 =================================================================== --- head/sysutils/ansible/files/extra-patch-ec84ff6 +++ head/sysutils/ansible/files/extra-patch-ec84ff6 @@ -1,359 +0,0 @@ ---- lib/ansible/playbook/conditional.py.orig 2016-11-01 03:43:19 UTC -+++ lib/ansible/playbook/conditional.py -@@ -19,6 +19,8 @@ - from __future__ import (absolute_import, division, print_function) - __metaclass__ = type - -+import re -+ - from jinja2.exceptions import UndefinedError - - from ansible.compat.six import text_type -@@ -26,6 +28,9 @@ from ansible.errors import AnsibleError, - from ansible.playbook.attribute import FieldAttribute - from ansible.template import Templar - from ansible.module_utils._text import to_native -+from ansible.vars.unsafe_proxy import wrap_var -+ -+LOOKUP_REGEX = re.compile(r'lookup\s*\(') - - class Conditional: - -@@ -100,9 +105,12 @@ class Conditional: - return conditional - - # a Jinja2 evaluation that results in something Python can eval! -+ if hasattr(conditional, '__UNSAFE__') and LOOKUP_REGEX.match(conditional): -+ raise AnsibleError("The conditional '%s' contains variables which came from an unsafe " \ -+ "source and also contains a lookup() call, failing conditional check" % conditional) -+ - presented = "{%% if %s %%} True {%% else %%} False {%% endif %%}" % conditional -- conditional = templar.template(presented) -- val = conditional.strip() -+ val = templar.template(presented).strip() - if val == "True": - return True - elif val == "False": ---- lib/ansible/plugins/action/__init__.py.orig 2016-11-01 03:43:19 UTC -+++ lib/ansible/plugins/action/__init__.py -@@ -30,9 +30,8 @@ import tempfile - import time - from abc import ABCMeta, abstractmethod - --from ansible.compat.six import binary_type, text_type, iteritems, with_metaclass -- - from ansible import constants as C -+from ansible.compat.six import binary_type, string_types, text_type, iteritems, with_metaclass - from ansible.errors import AnsibleError, AnsibleConnectionFailure - from ansible.executor.module_common import modify_module - from ansible.module_utils._text import to_bytes, to_native, to_text -@@ -40,6 +39,7 @@ from ansible.module_utils.json_utils imp - from ansible.parsing.utils.jsonify import jsonify - from ansible.playbook.play_context import MAGIC_VARIABLE_MAPPING - from ansible.release import __version__ -+from ansible.vars.unsafe_proxy import wrap_var - - - try: -@@ -449,6 +449,8 @@ class ActionBase(with_metaclass(ABCMeta, - # happens sometimes when it is a dir and not on bsd - if 'checksum' not in mystat['stat']: - mystat['stat']['checksum'] = '' -+ elif not isinstance(mystat['stat']['checksum'], string_types): -+ raise AnsibleError("Invalid checksum returned by stat: expected a string type but got %s" % type(mystat['stat']['checksum'])) - - return mystat['stat'] - -@@ -664,6 +666,39 @@ class ActionBase(with_metaclass(ABCMeta, - display.debug("done with _execute_module (%s, %s)" % (module_name, module_args)) - return data - -+ def _clean_returned_data(self, data): -+ remove_keys = set() -+ fact_keys = set(data.keys()) -+ # first we add all of our magic variable names to the set of -+ # keys we want to remove from facts -+ for magic_var in MAGIC_VARIABLE_MAPPING: -+ remove_keys.update(fact_keys.intersection(MAGIC_VARIABLE_MAPPING[magic_var])) -+ # next we remove any connection plugin specific vars -+ for conn_path in self._shared_loader_obj.connection_loader.all(path_only=True): -+ try: -+ conn_name = os.path.splitext(os.path.basename(conn_path))[0] -+ re_key = re.compile('^ansible_%s_' % conn_name) -+ for fact_key in fact_keys: -+ if re_key.match(fact_key): -+ remove_keys.add(fact_key) -+ except AttributeError: -+ pass -+ -+ # remove some KNOWN keys -+ for hard in ['ansible_rsync_path', 'ansible_playbook_python']: -+ if hard in fact_keys: -+ remove_keys.add(hard) -+ -+ # finally, we search for interpreter keys to remove -+ re_interp = re.compile('^ansible_.*_interpreter$') -+ for fact_key in fact_keys: -+ if re_interp.match(fact_key): -+ remove_keys.add(fact_key) -+ # then we remove them (except for ssh host keys) -+ for r_key in remove_keys: -+ if not r_key.startswith('ansible_ssh_host_key_'): -+ del data[r_key] -+ - def _parse_returned_data(self, res): - try: - filtered_output, warnings = _filter_non_json_lines(res.get('stdout', u'')) -@@ -672,31 +707,11 @@ class ActionBase(with_metaclass(ABCMeta, - data = json.loads(filtered_output) - data['_ansible_parsed'] = True - if 'ansible_facts' in data and isinstance(data['ansible_facts'], dict): -- remove_keys = set() -- fact_keys = set(data['ansible_facts'].keys()) -- # first we add all of our magic variable names to the set of -- # keys we want to remove from facts -- for magic_var in MAGIC_VARIABLE_MAPPING: -- remove_keys.update(fact_keys.intersection(MAGIC_VARIABLE_MAPPING[magic_var])) -- # next we remove any connection plugin specific vars -- for conn_path in self._shared_loader_obj.connection_loader.all(path_only=True): -- try: -- conn_name = os.path.splitext(os.path.basename(conn_path))[0] -- re_key = re.compile('^ansible_%s_' % conn_name) -- for fact_key in fact_keys: -- if re_key.match(fact_key): -- remove_keys.add(fact_key) -- except AttributeError: -- pass -- # finally, we search for interpreter keys to remove -- re_interp = re.compile('^ansible_.*_interpreter$') -- for fact_key in fact_keys: -- if re_interp.match(fact_key): -- remove_keys.add(fact_key) -- # then we remove them (except for ssh host keys) -- for r_key in remove_keys: -- if not r_key.startswith('ansible_ssh_host_key_'): -- del data['ansible_facts'][r_key] -+ self._clean_returned_data(data['ansible_facts']) -+ data['ansible_facts'] = wrap_var(data['ansible_facts']) -+ if 'add_host' in data and isinstance(data['add_host'].get('host_vars', None), dict): -+ self._clean_returned_data(data['add_host']['host_vars']) -+ data['add_host'] = wrap_var(data['add_host']) - except ValueError: - # not valid json, lets try to capture error - data = dict(failed=True, _ansible_parsed=False) ---- lib/ansible/plugins/action/template.py.orig 2016-11-01 03:43:19 UTC -+++ lib/ansible/plugins/action/template.py -@@ -23,6 +23,7 @@ import pwd - import time - - from ansible import constants as C -+from ansible.compat.six import string_types - from ansible.errors import AnsibleError - from ansible.module_utils._text import to_bytes, to_native, to_text - from ansible.plugins.action import ActionBase ---- lib/ansible/template/__init__.py.orig 2016-11-01 03:43:19 UTC -+++ lib/ansible/template/__init__.py -@@ -30,8 +30,9 @@ from numbers import Number - from jinja2 import Environment - from jinja2.loaders import FileSystemLoader - from jinja2.exceptions import TemplateSyntaxError, UndefinedError -+from jinja2.nodes import EvalContext - from jinja2.utils import concat as j2_concat --from jinja2.runtime import StrictUndefined -+from jinja2.runtime import Context, StrictUndefined - - from ansible import constants as C - from ansible.compat.six import string_types, text_type -@@ -41,7 +42,7 @@ from ansible.template.safe_eval import s - from ansible.template.template import AnsibleJ2Template - from ansible.template.vars import AnsibleJ2Vars - from ansible.module_utils._text import to_native, to_text -- -+from ansible.vars.unsafe_proxy import UnsafeProxy, wrap_var - - try: - from hashlib import sha1 -@@ -126,6 +127,62 @@ def _count_newlines_from_end(in_str): - # Uncommon cases: zero length string and string containing only newlines - return i - -+class AnsibleEvalContext(EvalContext): -+ ''' -+ A custom jinja2 EvalContext, which is currently unused and saved -+ here for possible future use. -+ ''' -+ pass -+ -+class AnsibleContext(Context): -+ ''' -+ A custom context, which intercepts resolve() calls and sets a flag -+ internally if any variable lookup returns an AnsibleUnsafe value. This -+ flag is checked post-templating, and (when set) will result in the -+ final templated result being wrapped via UnsafeProxy. -+ ''' -+ def __init__(self, *args, **kwargs): -+ super(AnsibleContext, self).__init__(*args, **kwargs) -+ self.eval_ctx = AnsibleEvalContext(self.environment, self.name) -+ self.unsafe = False -+ -+ def _is_unsafe(self, val): -+ ''' -+ Our helper function, which will also recursively check dict and -+ list entries due to the fact that they may be repr'd and contain -+ a key or value which contains jinja2 syntax and would otherwise -+ lose the AnsibleUnsafe value. -+ ''' -+ if isinstance(val, dict): -+ for key in val.keys(): -+ if self._is_unsafe(val[key]): -+ return True -+ elif isinstance(val, list): -+ for item in val: -+ if self._is_unsafe(item): -+ return True -+ elif isinstance(val, string_types) and hasattr(val, '__UNSAFE__'): -+ return True -+ return False -+ -+ def resolve(self, key): -+ ''' -+ The intercepted resolve(), which uses the helper above to set the -+ internal flag whenever an unsafe variable value is returned. -+ ''' -+ val = super(AnsibleContext, self).resolve(key) -+ if val is not None and not self.unsafe: -+ if self._is_unsafe(val): -+ self.unsafe = True -+ return val -+ -+class AnsibleEnvironment(Environment): -+ ''' -+ Our custom environment, which simply allows us to override the class-level -+ values for the Template and Context classes used by jinja2 internally. -+ ''' -+ context_class = AnsibleContext -+ template_class = AnsibleJ2Template - - class Templar: - ''' -@@ -159,14 +216,13 @@ class Templar: - self._fail_on_filter_errors = True - self._fail_on_undefined_errors = C.DEFAULT_UNDEFINED_VAR_BEHAVIOR - -- self.environment = Environment( -+ self.environment = AnsibleEnvironment( - trim_blocks=True, - undefined=StrictUndefined, - extensions=self._get_extensions(), - finalize=self._finalize, - loader=FileSystemLoader(self._basedir), - ) -- self.environment.template_class = AnsibleJ2Template - - self.SINGLE_VAR = re.compile(r"^%s\s*(\w*)\s*%s$" % (self.environment.variable_start_string, self.environment.variable_end_string)) - -@@ -229,7 +285,7 @@ class Templar: - def _clean_data(self, orig_data): - ''' remove jinja2 template tags from a string ''' - -- if not isinstance(orig_data, string_types) or hasattr(orig_data, '__ENCRYPTED__'): -+ if not isinstance(orig_data, string_types) or hasattr(orig_data, '__ENCRYPTED__') or hasattr(orig_data, '__UNSAFE__'): - return orig_data - - with contextlib.closing(StringIO(orig_data)) as data: -@@ -292,11 +348,12 @@ class Templar: - # Don't template unsafe variables, instead drop them back down to their constituent type. - if hasattr(variable, '__UNSAFE__'): - if isinstance(variable, text_type): -- return self._clean_data(variable) -+ rval = self._clean_data(variable) - else: - # Do we need to convert these into text_type as well? - # return self._clean_data(to_text(variable._obj, nonstring='passthru')) -- return self._clean_data(variable._obj) -+ rval = self._clean_data(variable._obj) -+ return rval - - try: - if convert_bare: -@@ -328,14 +385,23 @@ class Templar: - if cache and sha1_hash in self._cached_result: - result = self._cached_result[sha1_hash] - else: -- result = self._do_template(variable, preserve_trailing_newlines=preserve_trailing_newlines, escape_backslashes=escape_backslashes, fail_on_undefined=fail_on_undefined, overrides=overrides) -+ result = self._do_template( -+ variable, -+ preserve_trailing_newlines=preserve_trailing_newlines, -+ escape_backslashes=escape_backslashes, -+ fail_on_undefined=fail_on_undefined, -+ overrides=overrides, -+ ) - if convert_data and not self._no_type_regex.match(variable): - # if this looks like a dictionary or list, convert it to such using the safe_eval method - if (result.startswith("{") and not result.startswith(self.environment.variable_start_string)) or \ - result.startswith("[") or result in ("True", "False"): -+ unsafe = hasattr(result, '__UNSAFE__') - eval_results = safe_eval(result, locals=self._available_variables, include_exceptions=True) - if eval_results[1] is None: - result = eval_results[0] -+ if unsafe: -+ result = wrap_var(result) - else: - # FIXME: if the safe_eval raised an error, should we do something with it? - pass -@@ -435,7 +501,6 @@ class Templar: - ran = None - - if ran: -- from ansible.vars.unsafe_proxy import UnsafeProxy, wrap_var - if wantlist: - ran = wrap_var(ran) - else: -@@ -505,6 +570,8 @@ class Templar: - - try: - res = j2_concat(rf) -+ if new_context.unsafe: -+ res = wrap_var(res) - except TypeError as te: - if 'StrictUndefined' in to_native(te): - errmsg = "Unable to look up a name or access an attribute in template string (%s).\n" % to_native(data) ---- lib/ansible/template/template.py.orig 2016-11-01 03:43:19 UTC -+++ lib/ansible/template/template.py -@@ -33,5 +33,5 @@ class AnsibleJ2Template(jinja2.environme - ''' - - def new_context(self, vars=None, shared=False, locals=None): -- return jinja2.runtime.Context(self.environment, vars.add_locals(locals), self.name, self.blocks) -+ return self.environment.context_class(self.environment, vars.add_locals(locals), self.name, self.blocks) - ---- lib/ansible/template/vars.py.orig 2016-11-01 03:43:19 UTC -+++ lib/ansible/template/vars.py -@@ -82,7 +82,7 @@ class AnsibleJ2Vars: - # HostVars is special, return it as-is, as is the special variable - # 'vars', which contains the vars structure - from ansible.vars.hostvars import HostVars -- if isinstance(variable, dict) and varname == "vars" or isinstance(variable, HostVars): -+ if isinstance(variable, dict) and varname == "vars" or isinstance(variable, HostVars) or hasattr(variable, '__UNSAFE__'): - return variable - else: - value = None ---- lib/ansible/vars/unsafe_proxy.py.orig 2016-11-01 03:43:19 UTC -+++ lib/ansible/vars/unsafe_proxy.py -@@ -64,7 +64,6 @@ __all__ = ['UnsafeProxy', 'AnsibleUnsafe - class AnsibleUnsafe(object): - __UNSAFE__ = True - -- - class AnsibleUnsafeText(text_type, AnsibleUnsafe): - pass - -@@ -101,7 +100,7 @@ class AnsibleJSONUnsafeDecoder(json.JSON - def _wrap_dict(v): - for k in v.keys(): - if v[k] is not None: -- v[k] = wrap_var(v[k]) -+ v[wrap_var(k)] = wrap_var(v[k]) - return v - -