From edbd9b7003e728301d77348a134490f419839e8a Mon Sep 17 00:00:00 2001 From: jjlawren Date: Mon, 28 Sep 2026 16:12:45 -0500 Subject: [PATCH] Sanitize Sonos diagnostics (#183486) --- homeassistant/components/sonos/diagnostics.py | 8 +++-- tests/components/sonos/test_diagnostics.py | 30 +++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/sonos/diagnostics.py b/homeassistant/components/sonos/diagnostics.py index 2b0ee88a00d5..6de4458704f4 100644 --- a/homeassistant/components/sonos/diagnostics.py +++ b/homeassistant/components/sonos/diagnostics.py @@ -3,6 +3,7 @@ import time from typing import Any +from homeassistant.components.diagnostics import async_redact_data from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er from homeassistant.helpers.device_registry import AnyDeviceEntry @@ -41,6 +42,7 @@ SPEAKER_DIAGNOSTIC_ATTRIBUTES = ( "_last_activity", "_last_event_cache", ) +TO_REDACT = {"third_party_media_servers_x"} async def async_get_config_entry_diagnostics( @@ -62,7 +64,7 @@ async def async_get_config_entry_diagnostics( ) else: payload[section][key] = value - return payload + return async_redact_data(payload, TO_REDACT) async def async_get_device_diagnostics( @@ -79,7 +81,9 @@ async def async_get_device_diagnostics( if (speaker := config_entry.runtime_data.discovered.get(uid)) is None: return {} - return await async_generate_speaker_info(hass, config_entry, speaker) + return async_redact_data( + await async_generate_speaker_info(hass, config_entry, speaker), TO_REDACT + ) async def async_generate_media_info( diff --git a/tests/components/sonos/test_diagnostics.py b/tests/components/sonos/test_diagnostics.py index 4c4cf6509c85..266e3a258935 100644 --- a/tests/components/sonos/test_diagnostics.py +++ b/tests/components/sonos/test_diagnostics.py @@ -1,9 +1,11 @@ """Tests for the diagnostics data provided by the Sonos integration.""" +import pytest from syrupy.assertion import SnapshotAssertion from syrupy.filters import paths from homeassistant.components.sonos.const import DOMAIN +from homeassistant.components.sonos.diagnostics import TO_REDACT from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceRegistry @@ -63,3 +65,31 @@ async def test_diagnostics_device( "sonos_group_entities", ) ) + + +@pytest.mark.parametrize("key", sorted(TO_REDACT)) +async def test_diagnostics_redacts_keys( + hass: HomeAssistant, + hass_client: ClientSessionGenerator, + async_autosetup_sonos, + config_entry: MockConfigEntry, + key: str, +) -> None: + """Test sensitive keys are redacted at any level.""" + speaker = config_entry.runtime_data.discovered["RINCON_test"] + speaker._last_event_cache = { + "zone_group_topology": { + key: "secret", + "nested": [{key: "secret", "keep": 1}], + } + } + + result = await get_diagnostics_for_config_entry(hass, hass_client, config_entry) + + assert "secret" not in str(result) + assert result["discovered"]["RINCON_test"]["_last_event_cache"] == { + "zone_group_topology": { + key: "**REDACTED**", + "nested": [{key: "**REDACTED**", "keep": 1}], + } + }