From 3761bc113295e20771544491d007f1d328f40b99 Mon Sep 17 00:00:00 2001 From: James Cammarata Date: Mon, 30 Jun 2014 15:59:32 -0500 Subject: [PATCH] When parsing json from untrusted sources, remove templating tags --- lib/ansible/inventory/script.py | 2 +- lib/ansible/runner/__init__.py | 2 +- lib/ansible/runner/return_data.py | 3 +-- lib/ansible/utils/__init__.py | 42 ++++++++++++++++++++++++++++--- lib/ansible/utils/template.py | 11 -------- 5 files changed, 41 insertions(+), 19 deletions(-) diff --git a/lib/ansible/inventory/script.py b/lib/ansible/inventory/script.py index 05348b68397..aec52256f90 100644 --- a/lib/ansible/inventory/script.py +++ b/lib/ansible/inventory/script.py @@ -49,7 +49,7 @@ class InventoryScript(object): def _parse(self, err): all_hosts = {} - self.raw = utils.parse_json(self.data) + self.raw = utils.parse_json(self.data, from_remote=True) all = Group('all') groups = dict(all=all) group = None diff --git a/lib/ansible/runner/__init__.py b/lib/ansible/runner/__init__.py index 5f321e47298..44764cd2a58 100644 --- a/lib/ansible/runner/__init__.py +++ b/lib/ansible/runner/__init__.py @@ -507,7 +507,7 @@ class Runner(object): cmd2 = "rm -rf %s >/dev/null 2>&1" % tmp self._low_level_exec_command(conn, cmd2, tmp, sudoable=False) - data = utils.parse_json(res['stdout']) + data = utils.parse_json(res['stdout'], from_remote=True) if 'parsed' in data and data['parsed'] == False: data['msg'] += res['stderr'] return ReturnData(conn=conn, result=data) diff --git a/lib/ansible/runner/return_data.py b/lib/ansible/runner/return_data.py index 8e2252d4a3f..5b99c458985 100644 --- a/lib/ansible/runner/return_data.py +++ b/lib/ansible/runner/return_data.py @@ -43,8 +43,7 @@ class ReturnData(object): self.diff = diff if type(self.result) in [ str, unicode ]: - self.result = utils.parse_json(self.result) - + self.result = utils.parse_json(self.result, from_remote=True) if self.host is None: raise Exception("host not set") diff --git a/lib/ansible/utils/__init__.py b/lib/ansible/utils/__init__.py index 1f7ce46f672..a6856e58728 100644 --- a/lib/ansible/utils/__init__.py +++ b/lib/ansible/utils/__init__.py @@ -313,7 +313,38 @@ def json_loads(data): return json.loads(data) -def parse_json(raw_data): +def _clean_data(orig_data): + ''' remove template tags from a string ''' + data = orig_data + if isinstance(orig_data, basestring): + for pattern,replacement in (('{{','{#'), ('}}','#}'), ('{%','{#'), ('%}','#}')): + data = data.replace(pattern, replacement) + return data + +def _clean_data_struct(orig_data): + ''' + walk a complex data structure, and use _clean_data() to + remove any template tags that may exist + ''' + 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]) + 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]) + elif isinstance(orig_data, basestring): + data = _clean_data(orig_data) + else: + data = orig_data + return data + +def parse_json(raw_data, from_remote=False): ''' this version for module return data only ''' orig_data = raw_data @@ -322,7 +353,7 @@ def parse_json(raw_data): data = filter_leading_non_json_lines(raw_data) try: - return json.loads(data) + results = json.loads(data) except: # not JSON, but try "Baby JSON" which allows many of our modules to not # require JSON and makes writing modules in bash much simpler @@ -332,7 +363,6 @@ def parse_json(raw_data): except: print "failed to parse json: "+ data raise - for t in tokens: if "=" not in t: raise errors.AnsibleError("failed to parse: %s" % orig_data) @@ -347,7 +377,11 @@ def parse_json(raw_data): results[key] = value if len(results.keys()) == 0: return { "failed" : True, "parsed" : False, "msg" : orig_data } - return results + + if from_remote: + results = _clean_data_struct(results) + + return results def smush_braces(data): ''' smush Jinaj2 braces so unresolved templates like {{ foo }} don't get parsed weird by key=value code ''' diff --git a/lib/ansible/utils/template.py b/lib/ansible/utils/template.py index 8ec27ac0976..7108c8d9068 100644 --- a/lib/ansible/utils/template.py +++ b/lib/ansible/utils/template.py @@ -80,7 +80,6 @@ class Flags: FILTER_PLUGINS = None _LISTRE = re.compile(r"(\w+)\[(\d+)\]") -JINJA2_OVERRIDE='#jinja2:' def lookup(name, *args, **kwargs): from ansible import utils @@ -228,16 +227,6 @@ def template_from_file(basedir, path, vars, vault_password=None): except: raise errors.AnsibleError("unable to read %s" % realpath) - - # Get jinja env overrides from template - if data.startswith(JINJA2_OVERRIDE): - eol = data.find('\n') - line = data[len(JINJA2_OVERRIDE):eol] - data = data[eol+1:] - for pair in line.split(','): - (key,val) = pair.split(':') - setattr(environment,key.strip(),ast.literal_eval(val.strip())) - environment.template_class = J2Template try: t = environment.from_string(data)