From 3b4c3e6e176a5634acdec710c3980e755e7afa36 Mon Sep 17 00:00:00 2001 From: Joe Gross Date: Tue, 21 Jan 2020 16:03:42 -0800 Subject: [PATCH] Fix prometheus component to fully sanitize Unicode characters (#31037) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix prometheus component to fully santize Unicode characters. Sanitization used isdigit() to match numerics but that also matches Unicode "digit" characters that require special handling, like superscripts. Failures would look like this: ValueError: Invalid metric name: sensor_unit_u0x23_per_m³ Changed sanitize to use string.digits, which matches only ascii digits, and added test. See https://stackoverflow.com/questions/44891070/whats-the-difference-between-str-isdigit-isnumeric-and-isdecimal-in-python for discussion. https://docs.python.org/3/library/stdtypes.html#str.isdigit https://docs.python.org/3.7/library/string.html#string.digits * fix formatting to comply with black --- homeassistant/components/prometheus/__init__.py | 5 ++++- tests/components/prometheus/test_init.py | 13 +++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/prometheus/__init__.py b/homeassistant/components/prometheus/__init__.py index 71d56cda18a..c20296a2c18 100644 --- a/homeassistant/components/prometheus/__init__.py +++ b/homeassistant/components/prometheus/__init__.py @@ -168,7 +168,10 @@ class PrometheusMetrics: return "".join( [ c - if c in string.ascii_letters or c.isdigit() or c == "_" or c == ":" + if c in string.ascii_letters + or c in string.digits + or c == "_" + or c == ":" else f"u{hex(ord(c))}" for c in metric ] diff --git a/tests/components/prometheus/test_init.py b/tests/components/prometheus/test_init.py index 206d7477509..5c6189a811e 100644 --- a/tests/components/prometheus/test_init.py +++ b/tests/components/prometheus/test_init.py @@ -46,6 +46,13 @@ async def prometheus_client(loop, hass, hass_client): sensor4.entity_id = "sensor.wind_direction" await sensor4.async_update_ha_state() + sensor5 = DemoSensor( + None, "SPS30 PM <1µm Weight concentration", 3.7069, None, "µg/m³", None + ) + sensor5.hass = hass + sensor5.entity_id = "sensor.sps30_pm_1um_weight_concentration" + await sensor5.async_update_ha_state() + return await hass_client() @@ -113,3 +120,9 @@ async def test_view(prometheus_client): # pylint: disable=redefined-outer-name 'entity="sensor.wind_direction",' 'friendly_name="Wind Direction"} 25.0' in body ) + + assert ( + 'sensor_unit_u0xb5g_per_mu0xb3{domain="sensor",' + 'entity="sensor.sps30_pm_1um_weight_concentration",' + 'friendly_name="SPS30 PM <1µm Weight concentration"} 3.7069' in body + )