[stable-2.9] Do not treat AnsibleUndefined as being unsafe (#65202) (#65427)

* [stable-2.9] Do not treat AnsibleUndefined as being unsafe (#65202)

* Do not treat AnsibleUndefined as being unsafe. Fixes #65198

* fix yaml formatting.
(cherry picked from commit b08e7daf46)

Co-authored-by: Matt Martz <matt@sivel.net>

* Linting fix

* additional linting fix
pull/65514/head
Matt Martz 5 years ago committed by Matt Davis
parent 612d70ba3c
commit 95ff4372ec

@ -0,0 +1,4 @@
bugfixes:
- >-
``AnsibleUnsafe``/``AnsibleContext``/``Templar`` - Do not treat ``AnsibleUndefined`` as being "unsafe"
(https://github.com/ansible/ansible/issues/65198)

@ -235,6 +235,10 @@ class AnsibleUndefined(StrictUndefined):
rather than throwing an exception. rather than throwing an exception.
''' '''
def __getattr__(self, name): def __getattr__(self, name):
if name == '__UNSAFE__':
# AnsibleUndefined should never be assumed to be unsafe
# This prevents ``hasattr(val, '__UNSAFE__')`` from evaluating to ``True``
raise AttributeError(name)
# Return original Undefined object to preserve the first failure context # Return original Undefined object to preserve the first failure context
return self return self
@ -272,7 +276,7 @@ class AnsibleContext(Context):
for item in val: for item in val:
if self._is_unsafe(item): if self._is_unsafe(item):
return True return True
elif hasattr(val, '__UNSAFE__'): elif getattr(val, '__UNSAFE__', False) is True:
return True return True
return False return False

@ -27,7 +27,7 @@ from units.compat.mock import patch
from ansible import constants as C from ansible import constants as C
from ansible.errors import AnsibleError, AnsibleUndefinedVariable from ansible.errors import AnsibleError, AnsibleUndefinedVariable
from ansible.module_utils.six import string_types from ansible.module_utils.six import string_types
from ansible.template import Templar, AnsibleContext, AnsibleEnvironment from ansible.template import Templar, AnsibleContext, AnsibleEnvironment, AnsibleUndefined
from ansible.utils.unsafe_proxy import AnsibleUnsafe, wrap_var from ansible.utils.unsafe_proxy import AnsibleUnsafe, wrap_var
from units.mock.loader import DictDataLoader from units.mock.loader import DictDataLoader
@ -56,31 +56,10 @@ class BaseTemplar(object):
"/path/to/my_file.txt": "foo\n", "/path/to/my_file.txt": "foo\n",
}) })
self.templar = Templar(loader=self.fake_loader, variables=self.test_vars) self.templar = Templar(loader=self.fake_loader, variables=self.test_vars)
self._ansible_context = AnsibleContext(self.templar.environment, {}, {}, {})
def is_unsafe(self, obj): def is_unsafe(self, obj):
if obj is None: return self._ansible_context._is_unsafe(obj)
return False
if hasattr(obj, '__UNSAFE__'):
return True
if isinstance(obj, AnsibleUnsafe):
return True
if isinstance(obj, dict):
for key in obj.keys():
if self.is_unsafe(key) or self.is_unsafe(obj[key]):
return True
if isinstance(obj, list):
for item in obj:
if self.is_unsafe(item):
return True
if isinstance(obj, string_types) and hasattr(obj, '__UNSAFE__'):
return True
return False
# class used for testing arbitrary objects passed to template # class used for testing arbitrary objects passed to template
@ -461,3 +440,7 @@ class TestAnsibleContext(BaseTemplar, unittest.TestCase):
# self.assertNotIsInstance(res, AnsibleUnsafe) # self.assertNotIsInstance(res, AnsibleUnsafe)
self.assertFalse(self.is_unsafe(res), self.assertFalse(self.is_unsafe(res),
'return of AnsibleContext.resolve (%s) was not expected to be marked unsafe but was' % res) 'return of AnsibleContext.resolve (%s) was not expected to be marked unsafe but was' % res)
def test_is_unsafe(self):
context = self._context()
self.assertFalse(context._is_unsafe(AnsibleUndefined()))

@ -22,7 +22,7 @@ __metaclass__ = type
import jinja2 import jinja2
from units.compat import unittest from units.compat import unittest
from ansible.template import _escape_backslashes, _count_newlines_from_end from ansible.template import AnsibleUndefined, _escape_backslashes, _count_newlines_from_end
# These are internal utility functions only needed for templating. They're # These are internal utility functions only needed for templating. They're
# algorithmic so good candidates for unittesting by themselves # algorithmic so good candidates for unittesting by themselves
@ -115,3 +115,10 @@ class TestCountNewlines(unittest.TestCase):
def test_mostly_newlines(self): def test_mostly_newlines(self):
self.assertEquals(_count_newlines_from_end(u'The quick brown fox jumped over the lazy dog' + u'\n' * 1000), 1000) self.assertEquals(_count_newlines_from_end(u'The quick brown fox jumped over the lazy dog' + u'\n' * 1000), 1000)
class TestAnsibleUndefined(unittest.TestCase):
def test_getattr(self):
val = AnsibleUndefined()
self.assertIs(getattr(val, 'foo'), val)
self.assertRaises(AttributeError, getattr, val, '__UNSAFE__')

Loading…
Cancel
Save