From 94e6d2a72aa80a811becbfdb9772f714944132a8 Mon Sep 17 00:00:00 2001 From: Akita Noek Date: Thu, 28 Jan 2016 11:34:43 -0500 Subject: [PATCH] Obey no_log even more when using ansible 2.0 Hopefully fixes #645 this time. New function handles recursing down our results array when it's present, also attempts to proactively protect against future data leaks by only allowing white listed fields through. --- awx/plugins/callback/job_event_callback.py | 65 +++++++++++++--------- 1 file changed, 40 insertions(+), 25 deletions(-) diff --git a/awx/plugins/callback/job_event_callback.py b/awx/plugins/callback/job_event_callback.py index b4eaae083d..b5cdea63e4 100644 --- a/awx/plugins/callback/job_event_callback.py +++ b/awx/plugins/callback/job_event_callback.py @@ -38,6 +38,7 @@ import os import pwd import urlparse import re +from copy import deepcopy # Requests import requests @@ -47,6 +48,42 @@ import zmq import psutil + +CENSOR_FIELD_WHITELIST=[ + 'msg', + 'failed', + 'changed', + 'results', + 'start', + 'end', + 'delta', + 'cmd', + '_ansible_no_log', + 'cmd', + 'rc', + 'failed_when_result', + 'skip_reason', +] + +def censor(obj): + if obj.get('_ansible_no_log', False): + new_obj = {} + for k in CENSOR_FIELD_WHITELIST: + if k in obj: + new_obj[k] = obj[k] + if k == 'cmd' and k in obj: + if re.search(r'\s', obj['cmd']): + new_obj['cmd'] = re.sub(r'^(([^\s\\]|\\\s)+).*$', + r'\1 ', + obj['cmd']) + new_obj['censored'] = "the output has been hidden due to the fact that 'no_log: true' was specified for this result" + obj = new_obj + if 'results' in obj: + for i in xrange(len(obj['results'])): + obj['results'][i] = censor(obj['results'][i]) + return obj + + class TokenAuth(requests.auth.AuthBase): def __init__(self, token): @@ -127,31 +164,6 @@ class BaseCallbackModule(object): self._init_connection() if self.context is None: self._start_connection() - if 'res' in event_data \ - and event_data['res'].get('_ansible_no_log', False): - res = event_data['res'] - if 'stdout' in res and res['stdout']: - res['stdout'] = '' - if 'stdout_lines' in res and res['stdout_lines']: - res['stdout_lines'] = [''] - if 'stderr' in res and res['stderr']: - res['stderr'] = '' - if 'stderr_lines' in res and res['stderr_lines']: - res['stderr_lines'] = [''] - if res.get('cmd', None) and re.search(r'\s', res['cmd']): - res['cmd'] = re.sub(r'^(([^\s\\]|\\\s)+).*$', - r'\1 ', - res['cmd']) - if 'invocation' in res \ - and 'module_args' in res['invocation'] \ - and '_raw_params' in res['invocation']['module_args'] \ - and re.search(r'\s', - res['invocation']['module_args']['_raw_params']): - res['invocation']['module_args']['_raw_params'] = \ - re.sub(r'^(([^\s\\]|\\\s)+).*$', - r'\1 ', - res['invocation']['module_args']['_raw_params']) - msg['event_data']['res'] = res self.socket.send_json(msg) self.socket.recv() @@ -185,6 +197,9 @@ class BaseCallbackModule(object): response.raise_for_status() def _log_event(self, event, **event_data): + if 'res' in event_data: + event_data['res'] = censor(deepcopy(event_data['res'])) + if self.callback_consumer_port: self._post_job_event_queue_msg(event, event_data) else: