diff --git a/lib/ansible/module_utils/basic.py b/lib/ansible/module_utils/basic.py index 8d1b62af75..72fa010137 100644 --- a/lib/ansible/module_utils/basic.py +++ b/lib/ansible/module_utils/basic.py @@ -101,6 +101,44 @@ except ImportError: import syslog has_journal = False +try: + from ast import literal_eval as _literal_eval +except ImportError: + # a replacement for literal_eval that works with python 2.4. from: + # https://mail.python.org/pipermail/python-list/2009-September/551880.html + # which is essentially a cut/past from an earlier (2.6) version of python's + # ast.py + from compiler import parse + from compiler.ast import * + def _literal_eval(node_or_string): + """ + Safely evaluate an expression node or a string containing a Python + expression. The string or node provided may only consist of the following + Python literal structures: strings, numbers, tuples, lists, dicts, booleans, + and None. + """ + _safe_names = {'None': None, 'True': True, 'False': False} + if isinstance(node_or_string, basestring): + node_or_string = parse(node_or_string, mode='eval') + if isinstance(node_or_string, Expression): + node_or_string = node_or_string.node + def _convert(node): + if isinstance(node, Const) and isinstance(node.value, (basestring, int, float, long, complex)): + return node.value + elif isinstance(node, Tuple): + return tuple(map(_convert, node.nodes)) + elif isinstance(node, List): + return list(map(_convert, node.nodes)) + elif isinstance(node, Dict): + return dict((_convert(k), _convert(v)) for k, v in node.items) + elif isinstance(node, Name): + if node.name in _safe_names: + return _safe_names[node.name] + elif isinstance(node, UnarySub): + return -_convert(node.expr) + raise ValueError('malformed string') + return _convert(node_or_string) + FILE_COMMON_ARGUMENTS=dict( src = dict(), mode = dict(), @@ -700,6 +738,38 @@ class AnsibleModule(object): else: self.fail_json(msg="internal error: do not know how to interpret argument_spec") + def safe_eval(self, str, locals=None, include_exceptions=False): + + # do not allow method calls to modules + if not isinstance(str, basestring): + # already templated to a datastructure, perhaps? + if include_exceptions: + return (str, None) + return str + if re.search(r'\w\.\w+\(', str): + if include_exceptions: + return (str, None) + return str + # do not allow imports + if re.search(r'import \w+', str): + if include_exceptions: + return (str, None) + return str + try: + result = None + if not locals: + result = _literal_eval(str) + else: + result = _literal_eval(str, None, locals) + if include_exceptions: + return (result, None) + else: + return result + except Exception, e: + if include_exceptions: + return (str, e) + return str + def _check_argument_types(self): ''' ensure all arguments have the requested type ''' for (k, v) in self.argument_spec.iteritems():