aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--abusectl/redact.py14
-rw-r--r--tests/test_redact.py12
2 files changed, 24 insertions, 2 deletions
diff --git a/abusectl/redact.py b/abusectl/redact.py
index 5e7c4d3..15b1345 100644
--- a/abusectl/redact.py
+++ b/abusectl/redact.py
@@ -105,8 +105,18 @@ def _redact_fragment(fragment: str) -> str:
def _netloc_without_userinfo(parts) -> str:
- """Return scheme://host[:port] worth of netloc, dropping any userinfo."""
- netloc = parts.hostname or ""
+ """Return scheme://host[:port] worth of netloc, dropping any userinfo.
+
+ ``parts.hostname`` returns an IPv6 literal WITHOUT its brackets
+ (``2001:db8::1``, not ``[2001:db8::1]``), so re-bracketing it here is
+ required, not cosmetic: without it, appending ``:8080`` produces
+ ``2001:db8::1:8080``, which no consumer can parse back into a host and
+ a port, and which is not even the same address any more as text.
+ """
+ host = parts.hostname or ""
+ if ":" in host:
+ host = f"[{host}]"
+ netloc = host
if parts.port is not None:
netloc = f"{netloc}:{parts.port}"
return netloc
diff --git a/tests/test_redact.py b/tests/test_redact.py
index e6d65c0..553ffec 100644
--- a/tests/test_redact.py
+++ b/tests/test_redact.py
@@ -65,6 +65,18 @@ class TestRedactUrl(unittest.TestCase):
"http://a.invalid/p#REDACTED",
)
+ def test_an_ipv6_host_survives_userinfo_stripping(self):
+ self.assertEqual(
+ redact.url("http://victim@[2001:db8::1]:8080/p?x=1"),
+ "http://[2001:db8::1]:8080/p?x=REDACTED",
+ )
+
+ def test_an_ipv6_host_without_userinfo_is_untouched(self):
+ self.assertEqual(
+ redact.url("http://[2001:db8::1]:8080/p?x=1"),
+ "http://[2001:db8::1]:8080/p?x=REDACTED",
+ )
+
class TestSuspectPathSegments(unittest.TestCase):
def test_a_base64_looking_segment_is_flagged(self):