From 3a62eb5e030dcd45c14fd06624ff3d90747fe4fd Mon Sep 17 00:00:00 2001
From: Matt Clay <matt@mystile.com>
Date: Mon, 19 Feb 2018 13:32:07 -0800
Subject: [PATCH] Add option to hide sensitive ansible-test output.

This option is enabled automatically on Shippable.
---
 test/runner/lib/core_ci.py |  7 +++++++
 test/runner/lib/util.py    | 10 ++++++++++
 test/runner/test.py        |  6 ++++++
 3 files changed, 23 insertions(+)

diff --git a/test/runner/lib/core_ci.py b/test/runner/lib/core_ci.py
index 29ba6bb5106..db3a88c85e2 100644
--- a/test/runner/lib/core_ci.py
+++ b/test/runner/lib/core_ci.py
@@ -166,6 +166,8 @@ class AnsibleCoreCI(object):
             self.instance_id = str(uuid.uuid4())
             self.endpoint = None
 
+            display.sensitive.add(self.instance_id)
+
     def _get_parallels_endpoints(self):
         """
         :rtype: tuple[str]
@@ -299,6 +301,9 @@ class AnsibleCoreCI(object):
                 password=con.get('password'),
             )
 
+            if self.connection.password:
+                display.sensitive.add(self.connection.password)
+
         status = 'running' if self.connection.running else 'starting'
 
         display.info('Status update: %s/%s on instance %s is %s.' %
@@ -453,6 +458,8 @@ class AnsibleCoreCI(object):
         self.endpoint = config['endpoint']
         self.started = True
 
+        display.sensitive.add(self.instance_id)
+
         return True
 
     def _save(self):
diff --git a/test/runner/lib/util.py b/test/runner/lib/util.py
index 81650f1b34d..1e8bf71a748 100644
--- a/test/runner/lib/util.py
+++ b/test/runner/lib/util.py
@@ -491,6 +491,8 @@ class Display(object):
         self.rows = 0
         self.columns = 0
         self.truncate = 0
+        self.redact = False
+        self.sensitive = set()
 
         if os.isatty(0):
             self.rows, self.columns = unpack('HHHH', fcntl.ioctl(0, TIOCGWINSZ, pack('HHHH', 0, 0, 0, 0)))[:2]
@@ -554,6 +556,10 @@ class Display(object):
         :type fd: file
         :type truncate: bool
         """
+        if self.redact and self.sensitive:
+            for item in self.sensitive:
+                message = message.replace(item, '*' * len(item))
+
         if truncate:
             if len(message) > self.truncate > 5:
                 message = message[:self.truncate - 5] + ' ...'
@@ -633,6 +639,10 @@ class CommonConfig(object):
         self.verbosity = args.verbosity  # type: int
         self.debug = args.debug  # type: bool
         self.truncate = args.truncate  # type: int
+        self.redact = args.redact  # type: bool
+
+        if is_shippable():
+            self.redact = True
 
 
 def docker_qualify_image(name):
diff --git a/test/runner/test.py b/test/runner/test.py
index b4aa9220c75..b26e0b7dcbc 100755
--- a/test/runner/test.py
+++ b/test/runner/test.py
@@ -80,6 +80,7 @@ def main():
         config = args.config(args)
         display.verbosity = config.verbosity
         display.truncate = config.truncate
+        display.redact = config.redact
         display.color = config.color
         display.info_stderr = (isinstance(config, SanityConfig) and config.lint) or (isinstance(config, IntegrationConfig) and config.list_targets)
         check_startup()
@@ -157,6 +158,11 @@ def parse_args():
                         default=display.columns,
                         help='truncate some long output (0=disabled) (default: auto)')
 
+    common.add_argument('--redact',
+                        dest='redact',
+                        action='store_true',
+                        help='redact sensitive values in output')
+
     test = argparse.ArgumentParser(add_help=False, parents=[common])
 
     test.add_argument('include',