diff --git a/splunklib/binding.py b/splunklib/binding.py index 370f076cf..bf7d1ac0e 100644 --- a/splunklib/binding.py +++ b/splunklib/binding.py @@ -27,6 +27,7 @@ from __future__ import absolute_import import io +import json import logging import socket import ssl @@ -60,12 +61,17 @@ "HTTPError" ] +SENSITIVE_KEYS = ['Authorization', 'Cookie', 'action.email.auth_password', 'auth', 'auth_password', 'clear_password', 'clientId', + 'crc-salt', 'encr_password', 'oldpassword', 'passAuth', 'password', 'session', 'suppressionKey', + 'token'] + # If you change these, update the docstring # on _authority as well. DEFAULT_HOST = "localhost" DEFAULT_PORT = "8089" DEFAULT_SCHEME = "https" + def _log_duration(f): @wraps(f) def new_f(*args, **kwargs): @@ -77,6 +83,28 @@ def new_f(*args, **kwargs): return new_f +def mask_sensitive_data(data): + ''' + Masked sensitive fields data for logging purpose + ''' + if not isinstance(data, dict): + try: + data = json.loads(data) + except Exception as ex: + return data + + # json.loads will return "123"(str) as 123(int), so return the data if it's not 'dict' type + if not isinstance(data, dict): + return data + mdata = {} + for k, v in data.items(): + if k in SENSITIVE_KEYS: + mdata[k] = "******" + else: + mdata[k] = mask_sensitive_data(v) + return mdata + + def _parse_cookies(cookie_str, dictionary): """Tries to parse any key-value pairs of cookies in a string, then updates the the dictionary with any key-value pairs found. @@ -630,7 +658,7 @@ def delete(self, path_segment, owner=None, app=None, sharing=None, **query): """ path = self.authority + self._abspath(path_segment, owner=owner, app=app, sharing=sharing) - logger.debug("DELETE request to %s (body: %s)", path, repr(query)) + logger.debug("DELETE request to %s (body: %s)", path, mask_sensitive_data(query)) response = self.http.delete(path, self._auth_headers, **query) return response @@ -693,7 +721,7 @@ def get(self, path_segment, owner=None, app=None, headers=None, sharing=None, ** path = self.authority + self._abspath(path_segment, owner=owner, app=app, sharing=sharing) - logger.debug("GET request to %s (body: %s)", path, repr(query)) + logger.debug("GET request to %s (body: %s)", path, mask_sensitive_data(query)) all_headers = headers + self.additional_headers + self._auth_headers response = self.http.get(path, all_headers, **query) return response @@ -772,12 +800,7 @@ def post(self, path_segment, owner=None, app=None, sharing=None, headers=None, * path = self.authority + self._abspath(path_segment, owner=owner, app=app, sharing=sharing) - # To avoid writing sensitive data in debug logs - endpoint_having_sensitive_data = ["/storage/passwords"] - if any(endpoint in path for endpoint in endpoint_having_sensitive_data): - logger.debug("POST request to %s ", path) - else: - logger.debug("POST request to %s (body: %s)", path, repr(query)) + logger.debug("POST request to %s (body: %s)", path, mask_sensitive_data(query)) all_headers = headers + self.additional_headers + self._auth_headers response = self.http.post(path, all_headers, **query) return response @@ -844,8 +867,7 @@ def request(self, path_segment, method="GET", headers=None, body={}, all_headers = headers + self.additional_headers + self._auth_headers logger.debug("%s request to %s (headers: %s, body: %s)", - method, path, str(all_headers), repr(body)) - + method, path, str(mask_sensitive_data(dict(all_headers))), mask_sensitive_data(body)) if body: body = _encode(**body)