Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,26 @@


def mask_ip(ip: str | None) -> str | None:
"""Partially mask an IPv4 address, gracefully handling ``None`` values.

The previous implementation assumed ``ip`` was always a string which caused
``AttributeError`` when ``None`` was passed (e.g. when a test record had a
null ``client_ip``). This version returns the input unchanged if it is
falsy and only performs masking for valid dotted IPv4 strings.
"""Return a partially masked representation of ``ip``.

This helper is used by the Jinja2 templates when rendering client
information. In production the ``client_ip`` column may contain ``NULL``
values (or other unexpected types) which previously resulted in an
``AttributeError`` when ``split`` was called on the non-string object.
The function now defensively handles ``None`` and non-string inputs and
provides basic masking for both IPv4 and IPv6 addresses.
"""

if not ip:
return ip
if not isinstance(ip, str):
ip = str(ip)

if ":" in ip:
parts = ip.split(":")
if len(parts) >= 2:
return f"{parts[0]}:***:{parts[-1]}"
return ip

parts = ip.split(".")
if len(parts) == 4:
Expand Down
9 changes: 9 additions & 0 deletions backend/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,15 @@ def test_get_client_ip_handles_missing_client():
assert _get_client_ip(req) == ""


def test_mask_ip_handles_none_and_ipv6():
from backend.main import mask_ip

assert mask_ip(None) is None
assert mask_ip("127.0.0.1") == "127.***.***.1"
# Basic masking for IPv6 addresses; exact pattern is less important
assert mask_ip("2001:db8::1").startswith("2001:")


def test_ping_endpoint_localhost():
res = client.get("/ping", params={"host": "127.0.0.1", "count": 1})
data = res.json()
Expand Down