Skip to content

Commit 4f81be1

Browse files
committed
fix: redact sensitive data from debug logs
Reclassify the self-healing full-login path as debug and install scoped log redaction for aiounifi and integration debug output. Redact structured auth data, JSON websocket payloads, URLs, IPs, MACs, entity IDs, and raw response bodies before log formatting. Bump the manifest version to 4.4.4.
1 parent ef6c18e commit 4f81be1

14 files changed

Lines changed: 428 additions & 40 deletions

File tree

custom_components/unifi_network_rules/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,15 @@
3030

3131
# Import the modular API implementation
3232
from .udm.api import UDMAPI
33+
from .utils.logger import install_aiounifi_log_redaction
3334

3435
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
3536

3637

3738
async def async_setup(hass: HomeAssistant, config: dict) -> bool:
3839
"""Set up UniFi Network Rules component."""
40+
install_aiounifi_log_redaction()
41+
3942
# Initialize domain data if not already done
4043
hass.data.setdefault(DOMAIN, {})
4144
# Store shared data
@@ -52,6 +55,8 @@ async def async_setup(hass: HomeAssistant, config: dict) -> bool:
5255

5356
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
5457
"""Set up UniFi Network Rules from a config entry."""
58+
install_aiounifi_log_redaction()
59+
5560
try:
5661
# Initialize API
5762
api = UDMAPI(

custom_components/unifi_network_rules/config_flow.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
LOGGER,
3333
)
3434
from .udm import UDMAPI, CannotConnect, InvalidAuth
35+
from .utils.logger import install_aiounifi_log_redaction
3536

3637
# Display the update interval in minutes for better UX
3738
DEFAULT_UPDATE_INTERVAL_MINUTES = DEFAULT_UPDATE_INTERVAL // 60
@@ -52,6 +53,8 @@
5253

5354
async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> None:
5455
"""Validate the user input allows us to connect."""
56+
install_aiounifi_log_redaction()
57+
5558
api = UDMAPI(
5659
data[CONF_HOST],
5760
data[CONF_USERNAME],

custom_components/unifi_network_rules/manifest.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@
1515
"aiounifi>=87.0.0",
1616
"orjson>=3.8.0"
1717
],
18-
"version": "4.4.3"
19-
}
18+
"version": "4.4.4"
19+
}

custom_components/unifi_network_rules/udm/api.py

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from ..const import LOGGER
88
from ..queue import ApiOperationQueue
9+
from ..utils.logger import sanitize_auth_data
910
from .api_base import UDMAPI as BaseAPI
1011
from .api_handlers import ApiHandlerMixin
1112
from .authentication import AuthenticationMixin
@@ -322,19 +323,7 @@ def create_api_request(self, method: str, path: str, data: Any = None, is_v2: bo
322323

323324
def _sanitize_data_for_logging(self, data: Any) -> Any:
324325
"""Remove sensitive information from data before logging."""
325-
if not isinstance(data, dict):
326-
return data
327-
328-
# Create a copy to avoid modifying the original
329-
safe_data = data.copy()
330-
331-
# Remove sensitive fields (passwords, tokens, etc.)
332-
sensitive_fields = ["password", "key", "psk", "secret", "token", "auth"]
333-
for field in sensitive_fields:
334-
if field in safe_data:
335-
safe_data[field] = "***REDACTED***"
336-
337-
return safe_data
326+
return sanitize_auth_data(data)
338327

339328
async def refresh_all(self) -> None:
340329
"""Refresh all API data from the controller."""

custom_components/unifi_network_rules/udm/authentication.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ async def refresh_session(self, force: bool = False) -> bool:
278278
)
279279

280280
if not has_session:
281-
LOGGER.warning("No valid session token, performing full login")
281+
LOGGER.debug("No valid session token, performing full login")
282282
login_success = await self._try_login()
283283

284284
# After login, ensure proxy prefix is in base path

custom_components/unifi_network_rules/udm/firewall.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ async def get_firewall_policies(
5555

5656
async def add_firewall_policy(self, policy_data: dict[str, Any]) -> FirewallPolicy | None:
5757
"""Add a new firewall policy."""
58-
LOGGER.debug("Adding firewall policy: %s", policy_data)
58+
LOGGER.debug("Adding firewall policy: %s", policy_data.get("name", "unnamed"))
5959
try:
6060
# Using is_v2=True because this is a v2 API endpoint
6161
request = self.create_api_request("POST", API_PATH_FIREWALL_POLICIES, data=policy_data, is_v2=True)
@@ -193,7 +193,7 @@ async def get_legacy_firewall_rules(self) -> list[FirewallRule]:
193193

194194
async def add_legacy_firewall_rule(self, rule_data: dict[str, Any]) -> FirewallRule | None:
195195
"""Add a new legacy firewall rule."""
196-
LOGGER.debug("Adding legacy firewall rule: %s", rule_data)
196+
LOGGER.debug("Adding legacy firewall rule: %s", rule_data.get("name", "unnamed"))
197197
try:
198198
# Using the correct path constant
199199
request = self.create_api_request("POST", API_PATH_LEGACY_FIREWALL_RULES, data=rule_data)

custom_components/unifi_network_rules/udm/network.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -235,16 +235,12 @@ async def get_device_led_states(self) -> list[Device]:
235235
device = Device(device_data)
236236
led_capable_devices.append(device)
237237
LOGGER.debug(
238-
"Created LED-capable device: %s (%s) - LED state: %s",
239-
device_data.get("name", "Unknown"),
240-
mac,
238+
"Created LED-capable device - LED state: %s",
241239
device_data.get("led_override", "unknown"),
242240
)
243241
except Exception as device_err:
244242
LOGGER.warning(
245-
"Error creating Device object for %s (%s): %s",
246-
device_data.get("name", "unknown"),
247-
mac,
243+
"Error creating Device object: %s",
248244
str(device_err),
249245
)
250246
continue

custom_components/unifi_network_rules/udm/port_forward.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ async def get_port_forwards(self) -> list[PortForward]:
3838

3939
async def add_port_forward(self, forward_data: dict[str, Any]) -> PortForward | None:
4040
"""Add a new port forward."""
41-
LOGGER.debug("Adding port forward: %s", forward_data)
41+
LOGGER.debug("Adding port forward: %s", forward_data.get("name", "unnamed"))
4242
try:
4343
# Using the path constant from const.py
4444
request = self.create_api_request("POST", API_PATH_PORT_FORWARDS, data=forward_data)

custom_components/unifi_network_rules/udm/qos.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ async def add_qos_rule(self, rule_data: dict[str, Any]) -> QoSRule | None:
7373
Returns:
7474
QoSRule object if successful, None otherwise
7575
"""
76-
LOGGER.debug("Adding QoS rule: %s", rule_data)
76+
LOGGER.debug("Adding QoS rule: %s", rule_data.get("name", "unnamed"))
7777
try:
7878
# Using is_v2=True because this is a v2 API endpoint
7979
request = self.create_api_request("POST", API_PATH_QOS_RULES, data=rule_data, is_v2=True)

custom_components/unifi_network_rules/udm/routes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ async def get_traffic_routes(self) -> list[TrafficRoute]:
4242

4343
async def add_traffic_route(self, route_data: dict[str, Any]) -> TrafficRoute | None:
4444
"""Add a new traffic route."""
45-
LOGGER.debug("Adding traffic route: %s", route_data)
45+
LOGGER.debug("Adding traffic route: %s", route_data.get("name", "unnamed"))
4646
try:
4747
# Using is_v2=True because this is a v2 API endpoint
4848
request = self.create_api_request("POST", API_PATH_TRAFFIC_ROUTES, data=route_data, is_v2=True)

0 commit comments

Comments
 (0)