Repository: ansible/ansible

Issue:
Don't log the repo parameter in certain cases
If you feed the git/hg/svn module a repo containing a "foo:bar@baz" pattern with a password in it, module common will still log this parameter.

As such, module common should not log a "repo" parameter if it contains a : preceeding a "@" in any token.   

Do not be thrown/confused by  "foo@github.com/bar" , it's only if this occurs before a slash.



Discussion:

Resolving patch:
diff --git a/lib/ansible/callbacks.py b/lib/ansible/callbacks.py
--- a/lib/ansible/callbacks.py
+++ b/lib/ansible/callbacks.py
@@ -179,6 +179,7 @@ def vvvv(msg, host=None):
     return verbose(msg, host=host, caplevel=3)
 
 def verbose(msg, host=None, caplevel=2):
+    msg = utils.sanitize_output(msg)
     if utils.VERBOSITY > caplevel:
         if host is None:
             display(msg, color='blue')
diff --git a/lib/ansible/module_utils/basic.py b/lib/ansible/module_utils/basic.py
--- a/lib/ansible/module_utils/basic.py
+++ b/lib/ansible/module_utils/basic.py
@@ -761,7 +761,13 @@ def _log_invocation(self):
         # Sanitize possible password argument when logging.
         log_args = dict()
         passwd_keys = ['password', 'login_password']
-        
+
+        filter_re = [
+            # filter out things like user:pass@foo/whatever
+            # and http://username:pass@wherever/foo
+            re.compile('^(?P<before>.*:)(?P<password>.*)(?P<after>\@.*)$'), 
+        ]
+
         for param in self.params:
             canon  = self.aliases.get(param, param)
             arg_opts = self.argument_spec.get(canon, {})
@@ -772,7 +778,16 @@ def _log_invocation(self):
             elif param in passwd_keys:
                 log_args[param] = 'NOT_LOGGING_PASSWORD'
             else:
-                log_args[param] = self.params[param]
+                found = False
+                for filter in filter_re:
+                    m = filter.match(str(self.params[param]))
+                    if m:
+                        d = m.groupdict()
+                        log_args[param] = d['before'] + "********" + d['after']
+                        found = True
+                        break
+                if not found:
+                    log_args[param] = self.params[param]
 
         module = 'ansible-%s' % os.path.basename(__file__)
         msg = ''
diff --git a/lib/ansible/utils/__init__.py b/lib/ansible/utils/__init__.py
--- a/lib/ansible/utils/__init__.py
+++ b/lib/ansible/utils/__init__.py
@@ -623,6 +623,40 @@ def getch():
         termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
     return ch
 
+def sanitize_output(str):
+    ''' strips private info out of a string '''
+
+    private_keys = ['password', 'login_password']
+
+    filter_re = [
+        # filter out things like user:pass@foo/whatever
+        # and http://username:pass@wherever/foo
+        re.compile('^(?P<before>.*:)(?P<password>.*)(?P<after>\@.*)$'),
+    ]
+
+    parts = str.split()
+    output = ''
+    for part in parts:
+        try:
+            (k,v) = part.split('=', 1)
+            if k in private_keys:
+                output += " %s=VALUE_HIDDEN" % k
+            else:
+                found = False
+                for filter in filter_re:
+                    m = filter.match(v)
+                    if m:
+                        d = m.groupdict()
+                        output += " %s=%s" % (k, d['before'] + "********" + d['after'])
+                        found = True
+                        break
+                if not found:
+                    output += " %s" % part
+        except:
+            output += " %s" % part
+
+    return output.strip()
+
 ####################################################################
 # option handling code for /usr/bin/ansible and ansible-playbook
 # below this line


Test patch: