summaryrefslogtreecommitdiff
path: root/git
diff options
context:
space:
mode:
authorSebastian Thiel <sebastian.thiel@icloud.com>2021-03-19 19:09:44 +0800
committerGitHub <noreply@github.com>2021-03-19 19:09:44 +0800
commitd1297f65226e3bfdb31e224c514c362b304c904c (patch)
tree10e34ab1af6bdd7229b2a6da6436447ef4d236f3 /git
parentd906f31a283785e9864cb1eaf12a27faf4f72c42 (diff)
parentd283c83c43f5e52a1a14e55b35ffe85a780615d8 (diff)
downloadgitpython-d1297f65226e3bfdb31e224c514c362b304c904c.tar.gz
Merge pull request #1198 from RyaxTech/replace-password-in-uri-by-stars
Replace password in URI by stars if present to avoid leaking secrets in logs
Diffstat (limited to 'git')
-rw-r--r--git/cmd.py24
-rw-r--r--git/repo/base.py7
-rw-r--r--git/util.py29
3 files changed, 47 insertions, 13 deletions
diff --git a/git/cmd.py b/git/cmd.py
index 0395a708..40e32e37 100644
--- a/git/cmd.py
+++ b/git/cmd.py
@@ -29,7 +29,7 @@ from git.compat import (
is_win,
)
from git.exc import CommandError
-from git.util import is_cygwin_git, cygpath, expand_path
+from git.util import is_cygwin_git, cygpath, expand_path, remove_password_if_present
from .exc import (
GitCommandError,
@@ -83,8 +83,8 @@ def handle_process_output(process, stdout_handler, stderr_handler,
line = line.decode(defenc)
handler(line)
except Exception as ex:
- log.error("Pumping %r of cmd(%s) failed due to: %r", name, cmdline, ex)
- raise CommandError(['<%s-pump>' % name] + cmdline, ex) from ex
+ log.error("Pumping %r of cmd(%s) failed due to: %r", name, remove_password_if_present(cmdline), ex)
+ raise CommandError(['<%s-pump>' % name] + remove_password_if_present(cmdline), ex) from ex
finally:
stream.close()
@@ -406,7 +406,7 @@ class Git(LazyMixin):
if status != 0:
errstr = read_all_from_possibly_closed_stream(self.proc.stderr)
log.debug('AutoInterrupt wait stderr: %r' % (errstr,))
- raise GitCommandError(self.args, status, errstr)
+ raise GitCommandError(remove_password_if_present(self.args), status, errstr)
# END status handling
return status
# END auto interrupt
@@ -683,8 +683,10 @@ class Git(LazyMixin):
:note:
If you add additional keyword arguments to the signature of this method,
you must update the execute_kwargs tuple housed in this module."""
+ # Remove password for the command if present
+ redacted_command = remove_password_if_present(command)
if self.GIT_PYTHON_TRACE and (self.GIT_PYTHON_TRACE != 'full' or as_process):
- log.info(' '.join(command))
+ log.info(' '.join(redacted_command))
# Allow the user to have the command executed in their working dir.
cwd = self._working_dir or os.getcwd()
@@ -705,7 +707,7 @@ class Git(LazyMixin):
if is_win:
cmd_not_found_exception = OSError
if kill_after_timeout:
- raise GitCommandError(command, '"kill_after_timeout" feature is not supported on Windows.')
+ raise GitCommandError(redacted_command, '"kill_after_timeout" feature is not supported on Windows.')
else:
if sys.version_info[0] > 2:
cmd_not_found_exception = FileNotFoundError # NOQA # exists, flake8 unknown @UndefinedVariable
@@ -720,7 +722,7 @@ class Git(LazyMixin):
if istream:
istream_ok = "<valid stream>"
log.debug("Popen(%s, cwd=%s, universal_newlines=%s, shell=%s, istream=%s)",
- command, cwd, universal_newlines, shell, istream_ok)
+ redacted_command, cwd, universal_newlines, shell, istream_ok)
try:
proc = Popen(command,
env=env,
@@ -736,7 +738,7 @@ class Git(LazyMixin):
**subprocess_kwargs
)
except cmd_not_found_exception as err:
- raise GitCommandNotFound(command, err) from err
+ raise GitCommandNotFound(redacted_command, err) from err
if as_process:
return self.AutoInterrupt(proc, command)
@@ -786,7 +788,7 @@ class Git(LazyMixin):
watchdog.cancel()
if kill_check.isSet():
stderr_value = ('Timeout: the command "%s" did not complete in %d '
- 'secs.' % (" ".join(command), kill_after_timeout))
+ 'secs.' % (" ".join(redacted_command), kill_after_timeout))
if not universal_newlines:
stderr_value = stderr_value.encode(defenc)
# strip trailing "\n"
@@ -810,7 +812,7 @@ class Git(LazyMixin):
proc.stderr.close()
if self.GIT_PYTHON_TRACE == 'full':
- cmdstr = " ".join(command)
+ cmdstr = " ".join(redacted_command)
def as_text(stdout_value):
return not output_stream and safe_decode(stdout_value) or '<OUTPUT_STREAM>'
@@ -826,7 +828,7 @@ class Git(LazyMixin):
# END handle debug printing
if with_exceptions and status != 0:
- raise GitCommandError(command, status, stderr_value, stdout_value)
+ raise GitCommandError(redacted_command, status, stderr_value, stdout_value)
if isinstance(stdout_value, bytes) and stdout_as_string: # could also be output_stream
stdout_value = safe_decode(stdout_value)
diff --git a/git/repo/base.py b/git/repo/base.py
index 24bc5754..a28c9d28 100644
--- a/git/repo/base.py
+++ b/git/repo/base.py
@@ -25,7 +25,7 @@ from git.index import IndexFile
from git.objects import Submodule, RootModule, Commit
from git.refs import HEAD, Head, Reference, TagReference
from git.remote import Remote, add_progress, to_progress_instance
-from git.util import Actor, finalize_process, decygpath, hex_to_bin, expand_path
+from git.util import Actor, finalize_process, decygpath, hex_to_bin, expand_path, remove_password_if_present
import os.path as osp
from .fun import rev_parse, is_git_dir, find_submodule_git_dir, touch, find_worktree_git_dir
@@ -1018,7 +1018,10 @@ class Repo(object):
finalize_process, decode_streams=False)
else:
(stdout, stderr) = proc.communicate()
- log.debug("Cmd(%s)'s unused stdout: %s", getattr(proc, 'args', ''), stdout)
+ cmdline = getattr(proc, 'args', '')
+ cmdline = remove_password_if_present(cmdline)
+
+ log.debug("Cmd(%s)'s unused stdout: %s", cmdline, stdout)
finalize_process(proc, stderr=stderr)
# our git command could have a different working dir than our actual
diff --git a/git/util.py b/git/util.py
index 0f475a46..af499028 100644
--- a/git/util.py
+++ b/git/util.py
@@ -17,6 +17,7 @@ import stat
from sys import maxsize
import time
from unittest import SkipTest
+from urllib.parse import urlsplit, urlunsplit
# typing ---------------------------------------------------------
@@ -362,6 +363,34 @@ def expand_path(p: PathLike, expand_vars: bool = True) -> Optional[PathLike]:
except Exception:
return None
+
+def remove_password_if_present(cmdline):
+ """
+ Parse any command line argument and if on of the element is an URL with a
+ password, replace it by stars (in-place).
+
+ If nothing found just returns the command line as-is.
+
+ This should be used for every log line that print a command line.
+ """
+ new_cmdline = []
+ for index, to_parse in enumerate(cmdline):
+ new_cmdline.append(to_parse)
+ try:
+ url = urlsplit(to_parse)
+ # Remove password from the URL if present
+ if url.password is None:
+ continue
+
+ edited_url = url._replace(
+ netloc=url.netloc.replace(url.password, "*****"))
+ new_cmdline[index] = urlunsplit(edited_url)
+ except ValueError:
+ # This is not a valid URL
+ continue
+ return new_cmdline
+
+
#} END utilities
#{ Classes