Skip to content

Commit 951cc44

Browse files
authored
Merge pull request #2188 from pick7/codex/redact-http-extraheader
Redact Authorization extra headers from command errors
2 parents 2e5b13f + 5a53ae6 commit 951cc44

3 files changed

Lines changed: 35 additions & 2 deletions

File tree

git/util.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -540,8 +540,7 @@ def expand_path(p: Union[None, PathLike], expand_vars: bool = True) -> Optional[
540540

541541

542542
def remove_password_if_present(cmdline: Sequence[str]) -> List[str]:
543-
"""Parse any command line argument and if one of the elements is an URL with a
544-
username and/or password, replace them by stars (in-place).
543+
"""Redact credentials in URLs and HTTP Authorization extra headers in a command line.
545544
546545
If nothing is found, this just returns the command line as-is.
547546
@@ -551,6 +550,16 @@ def remove_password_if_present(cmdline: Sequence[str]) -> List[str]:
551550
new_cmdline = []
552551
for index, to_parse in enumerate(cmdline):
553552
new_cmdline.append(to_parse)
553+
config_key, separator, header = to_parse.partition("=")
554+
header_name, colon, _ = header.partition(":")
555+
if (
556+
separator
557+
and colon
558+
and config_key.lower().endswith(".extraheader")
559+
and header_name.strip().lower() == "authorization"
560+
):
561+
new_cmdline[index] = "%s%s%s%s *****" % (config_key, separator, header_name, colon)
562+
continue
554563
try:
555564
url = urlsplit(to_parse)
556565
# Remove password from the URL if present.

test/test_git.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,24 @@ def test_it_raises_proper_exception_with_output_stream(self):
419419
with self.assertRaises(GitCommandError):
420420
self.git.checkout("non-existent-branch", output_stream=tmp_file)
421421

422+
def test_it_redacts_authorization_extra_header_from_error(self):
423+
token = "fake-token-1234"
424+
command = [
425+
"git",
426+
"-c",
427+
"http.extraHeader=Authorization: Bearer %s" % token,
428+
"rev-parse",
429+
"--verify",
430+
"refs/does-not-exist",
431+
]
432+
433+
with self.assertRaises(GitCommandError) as context:
434+
self.git.execute(command)
435+
436+
message = str(context.exception)
437+
self.assertNotIn(token, message)
438+
self.assertIn("http.extraHeader=Authorization: *****", message)
439+
422440
def test_it_accepts_environment_variables(self):
423441
filename = fixture_path("ls_tree_empty")
424442
with open(filename, "r") as fh:

test/test_util.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,7 @@ def test_pickle_tzoffset(self):
671671
def test_remove_password_from_command_line(self):
672672
username = "fakeuser"
673673
password = "fakepassword1234"
674+
authorization = "Bearer fake-token-1234"
674675
url_with_user_and_pass = "https://{}:{}@fakerepo.example.com/testrepo".format(username, password)
675676
url_with_user = "https://{}@fakerepo.example.com/testrepo".format(username)
676677
url_with_pass = "https://:{}@fakerepo.example.com/testrepo".format(password)
@@ -681,6 +682,7 @@ def test_remove_password_from_command_line(self):
681682
cmd_3 = ["git", "clone", "-v", url_with_pass]
682683
cmd_4 = ["git", "clone", "-v", url_without_user_or_pass]
683684
cmd_5 = ["no", "url", "in", "this", "one"]
685+
cmd_6 = ["git", "-c", "http.extraHeader=Authorization: %s" % authorization, "fetch"]
684686

685687
redacted_cmd_1 = remove_password_if_present(cmd_1)
686688
assert username not in " ".join(redacted_cmd_1)
@@ -700,3 +702,7 @@ def test_remove_password_from_command_line(self):
700702

701703
assert cmd_4 == remove_password_if_present(cmd_4)
702704
assert cmd_5 == remove_password_if_present(cmd_5)
705+
706+
redacted_cmd_6 = remove_password_if_present(cmd_6)
707+
assert authorization not in " ".join(redacted_cmd_6)
708+
assert "http.extraHeader=Authorization: *****" in redacted_cmd_6

0 commit comments

Comments
 (0)