From d4f1fd7396864ca10c05d733d6dcc9f2d196b9c0 Mon Sep 17 00:00:00 2001 From: Anders Melchiorsen Date: Tue, 18 Aug 2020 21:10:43 +0200 Subject: [PATCH] Improve time_pattern validation schema (#38982) --- .../homeassistant/triggers/time_pattern.py | 36 +++++++++++++++++-- .../triggers/test_time_pattern.py | 19 ++++++++++ 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/homeassistant/triggers/time_pattern.py b/homeassistant/components/homeassistant/triggers/time_pattern.py index 65d44f5b1ca..ecca4ed444c 100644 --- a/homeassistant/components/homeassistant/triggers/time_pattern.py +++ b/homeassistant/components/homeassistant/triggers/time_pattern.py @@ -16,13 +16,43 @@ CONF_SECONDS = "seconds" _LOGGER = logging.getLogger(__name__) + +class TimePattern: + """Validate a time pattern value. + + :raises Invalid: If the value has a wrong format or is outside the range. + """ + + def __init__(self, maximum): + """Initialize time pattern.""" + self.maximum = maximum + + def __call__(self, value): + """Validate input.""" + try: + if value == "*": + return value + + if isinstance(value, str) and value.startswith("/"): + number = int(value[1:]) + else: + number = int(value) + + if not (0 <= number <= self.maximum): + raise vol.Invalid(f"must be a value between 0 and {self.maximum}") + except ValueError: + raise vol.Invalid("invalid time_pattern value") + + return value + + TRIGGER_SCHEMA = vol.All( vol.Schema( { vol.Required(CONF_PLATFORM): "time_pattern", - CONF_HOURS: vol.Any(vol.Coerce(int), vol.Coerce(str)), - CONF_MINUTES: vol.Any(vol.Coerce(int), vol.Coerce(str)), - CONF_SECONDS: vol.Any(vol.Coerce(int), vol.Coerce(str)), + CONF_HOURS: TimePattern(maximum=23), + CONF_MINUTES: TimePattern(maximum=59), + CONF_SECONDS: TimePattern(maximum=59), } ), cv.has_at_least_one_key(CONF_HOURS, CONF_MINUTES, CONF_SECONDS), diff --git a/tests/components/homeassistant/triggers/test_time_pattern.py b/tests/components/homeassistant/triggers/test_time_pattern.py index b5141f088e4..f2a2cd29d79 100644 --- a/tests/components/homeassistant/triggers/test_time_pattern.py +++ b/tests/components/homeassistant/triggers/test_time_pattern.py @@ -1,8 +1,10 @@ """The tests for the time_pattern automation.""" from asynctest.mock import patch import pytest +import voluptuous as vol import homeassistant.components.automation as automation +import homeassistant.components.homeassistant.triggers.time_pattern as time_pattern from homeassistant.setup import async_setup_component import homeassistant.util.dt as dt_util @@ -294,3 +296,20 @@ async def test_default_values(hass, calls): await hass.async_block_till_done() assert len(calls) == 2 + + +async def test_invalid_schemas(hass, calls): + """Test invalid schemas.""" + schemas = ( + None, + {}, + {"platform": "time_pattern"}, + {"platform": "time_pattern", "minutes": "/"}, + {"platform": "time_pattern", "minutes": "*/5"}, + {"platform": "time_pattern", "minutes": "/90"}, + {"platform": "time_pattern", "hours": 12, "minutes": 0, "seconds": 100}, + ) + + for value in schemas: + with pytest.raises(vol.Invalid): + time_pattern.TRIGGER_SCHEMA(value)