Ensure that data within a tuple is marked as unsafe (#65918)
* Use is_sequence, and Mapping throughout, add support for tuples. Fixes #65722 * Address tests * Remove unused import * Add changelog * Add docstring for clarity * Argh, linting fix * Not chasing this rabbit * wrap_var doesn't return a ref to the original item * no ref tests * Remove unused import
This commit is contained in:
parent
f9e315671a
commit
f8654de851
5 changed files with 46 additions and 20 deletions
3
changelogs/fragments/65722-unsafe-tuples.yml
Normal file
3
changelogs/fragments/65722-unsafe-tuples.yml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
bugfixes:
|
||||||
|
- unsafe_proxy - Ensure that data within a tuple is marked as unsafe
|
||||||
|
(https://github.com/ansible/ansible/issues/65722)
|
|
@ -662,7 +662,7 @@ class TaskExecutor:
|
||||||
if not isidentifier(self._task.register):
|
if not isidentifier(self._task.register):
|
||||||
raise AnsibleError("Invalid variable name in 'register' specified: '%s'" % self._task.register)
|
raise AnsibleError("Invalid variable name in 'register' specified: '%s'" % self._task.register)
|
||||||
|
|
||||||
vars_copy[self._task.register] = wrap_var(result)
|
vars_copy[self._task.register] = result = wrap_var(result)
|
||||||
|
|
||||||
if self._task.async_val > 0:
|
if self._task.async_val > 0:
|
||||||
if self._task.poll > 0 and not result.get('skipped') and not result.get('failed'):
|
if self._task.poll > 0 and not result.get('skipped') and not result.get('failed'):
|
||||||
|
@ -720,7 +720,7 @@ class TaskExecutor:
|
||||||
# This gives changed/failed_when access to additional recently modified
|
# This gives changed/failed_when access to additional recently modified
|
||||||
# attributes of result
|
# attributes of result
|
||||||
if self._task.register:
|
if self._task.register:
|
||||||
vars_copy[self._task.register] = wrap_var(result)
|
vars_copy[self._task.register] = result = wrap_var(result)
|
||||||
|
|
||||||
# if we didn't skip this task, use the helpers to evaluate the changed/
|
# if we didn't skip this task, use the helpers to evaluate the changed/
|
||||||
# failed_when properties
|
# failed_when properties
|
||||||
|
@ -751,7 +751,7 @@ class TaskExecutor:
|
||||||
# do the final update of the local variables here, for both registered
|
# do the final update of the local variables here, for both registered
|
||||||
# values and any facts which may have been created
|
# values and any facts which may have been created
|
||||||
if self._task.register:
|
if self._task.register:
|
||||||
variables[self._task.register] = wrap_var(result)
|
variables[self._task.register] = result = wrap_var(result)
|
||||||
|
|
||||||
if 'ansible_facts' in result:
|
if 'ansible_facts' in result:
|
||||||
if self._task.action in ('set_fact', 'include_vars'):
|
if self._task.action in ('set_fact', 'include_vars'):
|
||||||
|
|
|
@ -44,6 +44,7 @@ from ansible.errors import AnsibleError, AnsibleFilterError, AnsibleUndefinedVar
|
||||||
from ansible.module_utils.six import iteritems, string_types, text_type
|
from ansible.module_utils.six import iteritems, string_types, text_type
|
||||||
from ansible.module_utils._text import to_native, to_text, to_bytes
|
from ansible.module_utils._text import to_native, to_text, to_bytes
|
||||||
from ansible.module_utils.common._collections_compat import Sequence, Mapping, MutableMapping
|
from ansible.module_utils.common._collections_compat import Sequence, Mapping, MutableMapping
|
||||||
|
from ansible.module_utils.common.collections import is_sequence
|
||||||
from ansible.plugins.loader import filter_loader, lookup_loader, test_loader
|
from ansible.plugins.loader import filter_loader, lookup_loader, test_loader
|
||||||
from ansible.template.safe_eval import safe_eval
|
from ansible.template.safe_eval import safe_eval
|
||||||
from ansible.template.template import AnsibleJ2Template
|
from ansible.template.template import AnsibleJ2Template
|
||||||
|
@ -632,7 +633,7 @@ class Templar:
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
elif isinstance(variable, (list, tuple)):
|
elif is_sequence(variable):
|
||||||
return [self.template(
|
return [self.template(
|
||||||
v,
|
v,
|
||||||
preserve_trailing_newlines=preserve_trailing_newlines,
|
preserve_trailing_newlines=preserve_trailing_newlines,
|
||||||
|
@ -640,7 +641,7 @@ class Templar:
|
||||||
overrides=overrides,
|
overrides=overrides,
|
||||||
disable_lookups=disable_lookups,
|
disable_lookups=disable_lookups,
|
||||||
) for v in variable]
|
) for v in variable]
|
||||||
elif isinstance(variable, (dict, Mapping)):
|
elif isinstance(variable, Mapping):
|
||||||
d = {}
|
d = {}
|
||||||
# we don't use iteritems() here to avoid problems if the underlying dict
|
# we don't use iteritems() here to avoid problems if the underlying dict
|
||||||
# changes sizes due to the templating, which can happen with hostvars
|
# changes sizes due to the templating, which can happen with hostvars
|
||||||
|
|
|
@ -54,7 +54,8 @@ from __future__ import (absolute_import, division, print_function)
|
||||||
__metaclass__ = type
|
__metaclass__ = type
|
||||||
|
|
||||||
from ansible.module_utils._text import to_bytes, to_text
|
from ansible.module_utils._text import to_bytes, to_text
|
||||||
from ansible.module_utils.common._collections_compat import Mapping, MutableSequence, Set
|
from ansible.module_utils.common._collections_compat import Mapping, Set
|
||||||
|
from ansible.module_utils.common.collections import is_sequence
|
||||||
from ansible.module_utils.six import string_types, binary_type, text_type
|
from ansible.module_utils.six import string_types, binary_type, text_type
|
||||||
|
|
||||||
|
|
||||||
|
@ -97,21 +98,19 @@ class UnsafeProxy(object):
|
||||||
|
|
||||||
|
|
||||||
def _wrap_dict(v):
|
def _wrap_dict(v):
|
||||||
for k in v.keys():
|
return dict((wrap_var(k), wrap_var(item)) for k, item in v.items())
|
||||||
if v[k] is not None:
|
|
||||||
v[wrap_var(k)] = wrap_var(v[k])
|
|
||||||
return v
|
|
||||||
|
|
||||||
|
|
||||||
def _wrap_list(v):
|
def _wrap_sequence(v):
|
||||||
for idx, item in enumerate(v):
|
"""Wraps a sequence with unsafe, not meant for strings, primarily
|
||||||
if item is not None:
|
``tuple`` and ``list``
|
||||||
v[idx] = wrap_var(item)
|
"""
|
||||||
return v
|
v_type = type(v)
|
||||||
|
return v_type(wrap_var(item) for item in v)
|
||||||
|
|
||||||
|
|
||||||
def _wrap_set(v):
|
def _wrap_set(v):
|
||||||
return set(item if item is None else wrap_var(item) for item in v)
|
return set(wrap_var(item) for item in v)
|
||||||
|
|
||||||
|
|
||||||
def wrap_var(v):
|
def wrap_var(v):
|
||||||
|
@ -120,10 +119,10 @@ def wrap_var(v):
|
||||||
|
|
||||||
if isinstance(v, Mapping):
|
if isinstance(v, Mapping):
|
||||||
v = _wrap_dict(v)
|
v = _wrap_dict(v)
|
||||||
elif isinstance(v, MutableSequence):
|
|
||||||
v = _wrap_list(v)
|
|
||||||
elif isinstance(v, Set):
|
elif isinstance(v, Set):
|
||||||
v = _wrap_set(v)
|
v = _wrap_set(v)
|
||||||
|
elif is_sequence(v):
|
||||||
|
v = _wrap_sequence(v)
|
||||||
elif isinstance(v, binary_type):
|
elif isinstance(v, binary_type):
|
||||||
v = AnsibleUnsafeBytes(v)
|
v = AnsibleUnsafeBytes(v)
|
||||||
elif isinstance(v, text_type):
|
elif isinstance(v, text_type):
|
||||||
|
|
|
@ -62,8 +62,12 @@ def test_wrap_var_set_None():
|
||||||
def test_wrap_var_tuple():
|
def test_wrap_var_tuple():
|
||||||
assert isinstance(wrap_var(('foo',)), tuple)
|
assert isinstance(wrap_var(('foo',)), tuple)
|
||||||
assert not isinstance(wrap_var(('foo',)), AnsibleUnsafe)
|
assert not isinstance(wrap_var(('foo',)), AnsibleUnsafe)
|
||||||
assert isinstance(wrap_var(('foo',))[0], type(''))
|
assert isinstance(wrap_var(('foo',))[0], AnsibleUnsafe)
|
||||||
assert not isinstance(wrap_var(('foo',))[0], AnsibleUnsafe)
|
|
||||||
|
|
||||||
|
def test_wrap_var_tuple_None():
|
||||||
|
assert wrap_var((None,))[0] is None
|
||||||
|
assert not isinstance(wrap_var((None,))[0], AnsibleUnsafe)
|
||||||
|
|
||||||
|
|
||||||
def test_wrap_var_None():
|
def test_wrap_var_None():
|
||||||
|
@ -79,6 +83,25 @@ def test_wrap_var_unsafe_bytes():
|
||||||
assert isinstance(wrap_var(AnsibleUnsafeBytes(b'foo')), AnsibleUnsafeBytes)
|
assert isinstance(wrap_var(AnsibleUnsafeBytes(b'foo')), AnsibleUnsafeBytes)
|
||||||
|
|
||||||
|
|
||||||
|
def test_wrap_var_no_ref():
|
||||||
|
thing = {
|
||||||
|
'foo': {
|
||||||
|
'bar': 'baz'
|
||||||
|
},
|
||||||
|
'bar': ['baz', 'qux'],
|
||||||
|
'baz': ('qux',),
|
||||||
|
'none': None,
|
||||||
|
'text': 'text',
|
||||||
|
}
|
||||||
|
wrapped_thing = wrap_var(thing)
|
||||||
|
thing is not wrapped_thing
|
||||||
|
thing['foo'] is not wrapped_thing['foo']
|
||||||
|
thing['bar'][0] is not wrapped_thing['bar'][0]
|
||||||
|
thing['baz'][0] is not wrapped_thing['baz'][0]
|
||||||
|
thing['none'] is not wrapped_thing['none']
|
||||||
|
thing['text'] is not wrapped_thing['text']
|
||||||
|
|
||||||
|
|
||||||
def test_AnsibleUnsafeText():
|
def test_AnsibleUnsafeText():
|
||||||
assert isinstance(AnsibleUnsafeText(u'foo'), AnsibleUnsafe)
|
assert isinstance(AnsibleUnsafeText(u'foo'), AnsibleUnsafe)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue