| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -658,10 +658,11 @@ def remove_password_if_present(cmdline: Sequence[str]) -> List[str]: | |||
| 658 | 658 | if url.password is None and url.username is None: | |
| 659 | 659 | continue | |
| 660 | 660 | ||
| 661 | - if url.password is not None: | ||
| 662 | - url = url._replace(netloc=url.netloc.replace(url.password, "*****")) | ||
| 663 | - if url.username is not None: | ||
| 664 | - url = url._replace(netloc=url.netloc.replace(url.username, "*****")) | ||
| 661 | + # Match urllib.parse's userinfo boundary. Keeping the raw hostinfo | ||
| 662 | + # preserves hostname case, IPv6 brackets, and port formatting. | ||
| 663 | + _, _, hostinfo = url.netloc.rpartition("@") | ||
| 664 | + redacted = "*****:*****" if url.password is not None else "*****" | ||
| 665 | + url = url._replace(netloc=f"{redacted}@{hostinfo}") | ||
| 665 | 666 | new_cmdline[index] = urlunsplit(url) | |
| 666 | 667 | except ValueError: | |
| 667 | 668 | # This is not a valid URL. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -828,6 +828,34 @@ def test_remove_password_from_command_line(self): | |||
| 828 | 828 | assert authorization not in " ".join(redacted_cmd_6) | |
| 829 | 829 | assert "http.extraHeader=Authorization: *****" in redacted_cmd_6 | |
| 830 | 830 | ||
| 831 | + def test_remove_password_keeps_host_intact(self): | ||
| 832 | + """Redaction must not touch the host, even when it contains the username.""" | ||
| 833 | + redacted = remove_password_if_present(["git", "clone", "https://git@github.com/user/repo.git"]) | ||
| 834 | + assert redacted == ["git", "clone", "https://*****@github.com/user/repo.git"] | ||
| 835 | + | ||
| 836 | + redacted = remove_password_if_present(["git", "clone", "ssh://git@github.com/u/r.git"]) | ||
| 837 | + assert redacted == ["git", "clone", "ssh://*****@github.com/u/r.git"] | ||
| 838 | + | ||
| 839 | + def test_remove_empty_password_keeps_host_intact(self): | ||
| 840 | + """An empty password must not expand into every position of the netloc.""" | ||
| 841 | + redacted = remove_password_if_present(["git", "clone", "https://:@fakerepo.example.com/testrepo"]) | ||
| 842 | + assert redacted == ["git", "clone", "https://*****:*****@fakerepo.example.com/testrepo"] | ||
| 843 | + | ||
| 844 | + @ddt.data( | ||
| 845 | + ( | ||
| 846 | + "https://user%40example.com:p%40ss@GitHub.COM:00443/repo@name?q=a@b#c@d", | ||
| 847 | + "https://*****:*****@GitHub.COM:00443/repo@name?q=a@b#c@d", | ||
| 848 | + ), | ||
| 849 | + ("//user:pass@[2001:db8::1]:0080/repo", "//*****:*****@[2001:db8::1]:0080/repo"), | ||
| 850 | + ("https://user:p@ss@example.com/repo", "https://*****:*****@example.com/repo"), | ||
| 851 | + ("https://user:@example.com/repo", "https://*****:*****@example.com/repo"), | ||
| 852 | + ("https://@example.com/repo", "https://*****@example.com/repo"), | ||
| 853 | + ("https://example.com/repo@name?q=a@b#c@d", "https://example.com/repo@name?q=a@b#c@d"), | ||
| 854 | + ) | ||
| 855 | + @ddt.unpack | ||
| 856 | + def test_remove_password_preserves_url_components(self, url, expected): | ||
| 857 | + assert remove_password_if_present([url]) == [expected] | ||
| 858 | + | ||
| 831 | 859 | ||
| 832 | 860 | def test_mode_str_to_int_accepts_bytes(): | |
| 833 | 861 | assert mode_str_to_int("100644") == 0o100644 | |
| Back | FazBrowse Home | New Git URL |
0 commit comments