summaryrefslogtreecommitdiff
path: root/lib/ansible/utils/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ansible/utils/__init__.py')
-rw-r--r--lib/ansible/utils/__init__.py62
1 files changed, 48 insertions, 14 deletions
diff --git a/lib/ansible/utils/__init__.py b/lib/ansible/utils/__init__.py
index a6856e5872..f5719ec4f0 100644
--- a/lib/ansible/utils/__init__.py
+++ b/lib/ansible/utils/__init__.py
@@ -51,6 +51,10 @@ VERBOSITY=0
MAX_FILE_SIZE_FOR_DIFF=1*1024*1024
+# caching the compilation of the regex used
+# to check for lookup calls within data
+LOOKUP_REGEX=re.compile(r'lookup\s*\(')
+
try:
import json
except ImportError:
@@ -313,38 +317,44 @@ def json_loads(data):
return json.loads(data)
-def _clean_data(orig_data):
+def _clean_data(orig_data, from_remote=False, from_inventory=False):
''' remove template tags from a string '''
data = orig_data
if isinstance(orig_data, basestring):
- for pattern,replacement in (('{{','{#'), ('}}','#}'), ('{%','{#'), ('%}','#}')):
+ sub_list = [('{%','{#'), ('%}','#}')]
+ if from_remote or (from_inventory and '{{' in data and LOOKUP_REGEX.search(data)):
+ # if from a remote, we completely disable any jinja2 blocks
+ sub_list.extend([('{{','{#'), ('}}','#}')])
+ for pattern,replacement in sub_list:
data = data.replace(pattern, replacement)
return data
-def _clean_data_struct(orig_data):
+def _clean_data_struct(orig_data, from_remote=False, from_inventory=False):
'''
walk a complex data structure, and use _clean_data() to
remove any template tags that may exist
'''
+ if not from_remote and not from_inventory:
+ raise errors.AnsibleErrors("when cleaning data, you must specify either from_remote or from_inventory")
if isinstance(orig_data, dict):
data = orig_data.copy()
for key in data:
- new_key = _clean_data_struct(key)
- new_val = _clean_data_struct(data[key])
+ new_key = _clean_data_struct(key, from_remote, from_inventory)
+ new_val = _clean_data_struct(data[key], from_remote, from_inventory)
if key != new_key:
del data[key]
data[new_key] = new_val
elif isinstance(orig_data, list):
data = orig_data[:]
for i in range(0, len(data)):
- data[i] = _clean_data_struct(data[i])
+ data[i] = _clean_data_struct(data[i], from_remote, from_inventory)
elif isinstance(orig_data, basestring):
- data = _clean_data(orig_data)
+ data = _clean_data(orig_data, from_remote, from_inventory)
else:
data = orig_data
return data
-def parse_json(raw_data, from_remote=False):
+def parse_json(raw_data, from_remote=False, from_inventory=False):
''' this version for module return data only '''
orig_data = raw_data
@@ -379,10 +389,31 @@ def parse_json(raw_data, from_remote=False):
return { "failed" : True, "parsed" : False, "msg" : orig_data }
if from_remote:
- results = _clean_data_struct(results)
+ results = _clean_data_struct(results, from_remote, from_inventory)
return results
+def merge_module_args(current_args, new_args):
+ '''
+ merges either a dictionary or string of k=v pairs with another string of k=v pairs,
+ and returns a new k=v string without duplicates.
+ '''
+ if not isinstance(current_args, basestring):
+ raise errors.AnsibleError("expected current_args to be a basestring")
+ # we use parse_kv to split up the current args into a dictionary
+ final_args = parse_kv(current_args)
+ if isinstance(new_args, dict):
+ final_args.update(new_args)
+ elif isinstance(new_args, basestring):
+ new_args_kv = parse_kv(new_args)
+ final_args.update(new_args_kv)
+ # then we re-assemble into a string
+ module_args = ""
+ for (k,v) in final_args.iteritems():
+ if isinstance(v, basestring):
+ module_args = "%s=%s %s" % (k, pipes.quote(v), module_args)
+ return module_args.strip()
+
def smush_braces(data):
''' smush Jinaj2 braces so unresolved templates like {{ foo }} don't get parsed weird by key=value code '''
while '{{ ' in data:
@@ -615,7 +646,7 @@ def parse_kv(args):
for x in vargs:
if "=" in x:
k, v = x.split("=",1)
- options[k]=v
+ options[k] = v
return options
def merge_hash(a, b):
@@ -1062,11 +1093,14 @@ def list_intersection(a, b):
def safe_eval(expr, locals={}, include_exceptions=False):
'''
- this is intended for allowing things like:
+ This is intended for allowing things like:
with_items: a_list_variable
- where Jinja2 would return a string
- but we do not want to allow it to call functions (outside of Jinja2, where
- the env is constrained)
+
+ Where Jinja2 would return a string but we do not want to allow it to
+ call functions (outside of Jinja2, where the env is constrained). If
+ the input data to this function came from an untrusted (remote) source,
+ it should first be run through _clean_data_struct() to ensure the data
+ is further sanitized prior to evaluation.
Based on:
http://stackoverflow.com/questions/12523516/using-ast-and-whitelists-to-make-pythons-eval-safe