From c51ed4b3281368b6724610e6c7c4374f90367f52 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Tue, 28 Mar 2023 03:59:01 -0400 Subject: [PATCH] Redact secret zwave values in diagnostics (#90389) * redact secret zwave values from diagnostics * shhrink * rename --- .../components/zwave_js/diagnostics.py | 20 ++++++--- tests/components/zwave_js/test_diagnostics.py | 42 +++++++++++++++++++ 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/zwave_js/diagnostics.py b/homeassistant/components/zwave_js/diagnostics.py index acb87a239ae..4f52c41a085 100644 --- a/homeassistant/components/zwave_js/diagnostics.py +++ b/homeassistant/components/zwave_js/diagnostics.py @@ -34,16 +34,23 @@ VALUES_TO_REDACT = ( ) -def redact_value_of_zwave_value(zwave_value: ValueDataType) -> ValueDataType: - """Redact value of a Z-Wave value.""" +def _redacted_value(zwave_value: ValueDataType) -> ValueDataType: + """Return redacted value of a Z-Wave value.""" + redacted_value: ValueDataType = deepcopy(zwave_value) + redacted_value["value"] = REDACTED + return redacted_value + + +def optionally_redact_value_of_zwave_value(zwave_value: ValueDataType) -> ValueDataType: + """Redact value of a Z-Wave value if it matches criteria to redact.""" # If the value has no value, there is nothing to redact if zwave_value.get("value") in (None, ""): return zwave_value + if zwave_value.get("metadata", {}).get("secret"): + return _redacted_value(zwave_value) for value_to_redact in VALUES_TO_REDACT: if value_matches_matcher(value_to_redact, zwave_value): - redacted_value: ValueDataType = deepcopy(zwave_value) - redacted_value["value"] = REDACTED - return redacted_value + return _redacted_value(zwave_value) return zwave_value @@ -51,7 +58,8 @@ def redact_node_state(node_state: NodeDataType) -> NodeDataType: """Redact node state.""" redacted_state: NodeDataType = deepcopy(node_state) redacted_state["values"] = [ - redact_value_of_zwave_value(zwave_value) for zwave_value in node_state["values"] + optionally_redact_value_of_zwave_value(zwave_value) + for zwave_value in node_state["values"] ] return redacted_state diff --git a/tests/components/zwave_js/test_diagnostics.py b/tests/components/zwave_js/test_diagnostics.py index 773b799cd6f..c7a711d1067 100644 --- a/tests/components/zwave_js/test_diagnostics.py +++ b/tests/components/zwave_js/test_diagnostics.py @@ -1,10 +1,14 @@ """Test the Z-Wave JS diagnostics.""" +import copy from unittest.mock import patch import pytest +from zwave_js_server.const import CommandClass from zwave_js_server.event import Event +from zwave_js_server.model.node import Node from homeassistant.components.zwave_js.diagnostics import ( + REDACTED, ZwaveValueMatcher, async_get_device_diagnostics, ) @@ -179,3 +183,41 @@ async def test_device_diagnostics_missing_primary_value( assert air_entity["value_id"] == value.value_id assert air_entity["primary_value"] is None + + +async def test_device_diagnostics_secret_value( + hass: HomeAssistant, + client, + multisensor_6_state, + integration, + hass_client: ClientSessionGenerator, + version_state, +) -> None: + """Test that secret value in device level diagnostics gets redacted.""" + + def _find_ultraviolet_val(data: dict) -> dict: + """Find ultraviolet property value in data.""" + return next( + val + for val in data["values"] + if val["commandClass"] == CommandClass.SENSOR_MULTILEVEL + and val["property"] == PROPERTY_ULTRAVIOLET + ) + + node_state = copy.deepcopy(multisensor_6_state) + # Force a value to be secret so we can check if it gets redacted + secret_value = _find_ultraviolet_val(node_state) + secret_value["metadata"]["secret"] = True + node = Node(client, node_state) + client.driver.controller.nodes[node.node_id] = node + client.driver.controller.emit("node added", {"node": node}) + await hass.async_block_till_done() + dev_reg = async_get_dev_reg(hass) + device = dev_reg.async_get_device({get_device_id(client.driver, node)}) + assert device + + diagnostics_data = await get_diagnostics_for_device( + hass, hass_client, integration, device + ) + test_value = _find_ultraviolet_val(diagnostics_data["state"]) + assert test_value["value"] == REDACTED