Fix DeviceInfo configuration_url validation (#97319)
This commit is contained in:
@@ -10,6 +10,7 @@ from typing import TYPE_CHECKING, Any, Literal, TypedDict, TypeVar, cast
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import attr
|
||||
from yarl import URL
|
||||
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_STARTED, EVENT_HOMEASSISTANT_STOP
|
||||
from homeassistant.core import Event, HomeAssistant, callback
|
||||
@@ -48,6 +49,8 @@ ORPHANED_DEVICE_KEEP_SECONDS = 86400 * 30
|
||||
|
||||
RUNTIME_ONLY_ATTRS = {"suggested_area"}
|
||||
|
||||
CONFIGURATION_URL_SCHEMES = {"http", "https", "homeassistant"}
|
||||
|
||||
|
||||
class DeviceEntryDisabler(StrEnum):
|
||||
"""What disabled a device entry."""
|
||||
@@ -168,28 +171,36 @@ def _validate_device_info(
|
||||
),
|
||||
)
|
||||
|
||||
if (config_url := device_info.get("configuration_url")) is not None:
|
||||
if type(config_url) is not str or urlparse(config_url).scheme not in [
|
||||
"http",
|
||||
"https",
|
||||
"homeassistant",
|
||||
]:
|
||||
raise DeviceInfoError(
|
||||
config_entry.domain if config_entry else "unknown",
|
||||
device_info,
|
||||
f"invalid configuration_url '{config_url}'",
|
||||
)
|
||||
|
||||
return device_info_type
|
||||
|
||||
|
||||
def _validate_configuration_url(value: Any) -> str | None:
|
||||
"""Validate and convert configuration_url."""
|
||||
if value is None:
|
||||
return None
|
||||
if (
|
||||
isinstance(value, URL)
|
||||
and (value.scheme not in CONFIGURATION_URL_SCHEMES or not value.host)
|
||||
) or (
|
||||
(parsed_url := urlparse(str(value)))
|
||||
and (
|
||||
parsed_url.scheme not in CONFIGURATION_URL_SCHEMES
|
||||
or not parsed_url.hostname
|
||||
)
|
||||
):
|
||||
raise ValueError(f"invalid configuration_url '{value}'")
|
||||
return str(value)
|
||||
|
||||
|
||||
@attr.s(slots=True, frozen=True)
|
||||
class DeviceEntry:
|
||||
"""Device Registry Entry."""
|
||||
|
||||
area_id: str | None = attr.ib(default=None)
|
||||
config_entries: set[str] = attr.ib(converter=set, factory=set)
|
||||
configuration_url: str | None = attr.ib(default=None)
|
||||
configuration_url: str | URL | None = attr.ib(
|
||||
converter=_validate_configuration_url, default=None
|
||||
)
|
||||
connections: set[tuple[str, str]] = attr.ib(converter=set, factory=set)
|
||||
disabled_by: DeviceEntryDisabler | None = attr.ib(default=None)
|
||||
entry_type: DeviceEntryType | None = attr.ib(default=None)
|
||||
@@ -453,7 +464,7 @@ class DeviceRegistry:
|
||||
self,
|
||||
*,
|
||||
config_entry_id: str,
|
||||
configuration_url: str | None | UndefinedType = UNDEFINED,
|
||||
configuration_url: str | URL | None | UndefinedType = UNDEFINED,
|
||||
connections: set[tuple[str, str]] | None | UndefinedType = UNDEFINED,
|
||||
default_manufacturer: str | None | UndefinedType = UNDEFINED,
|
||||
default_model: str | None | UndefinedType = UNDEFINED,
|
||||
@@ -582,7 +593,7 @@ class DeviceRegistry:
|
||||
*,
|
||||
add_config_entry_id: str | UndefinedType = UNDEFINED,
|
||||
area_id: str | None | UndefinedType = UNDEFINED,
|
||||
configuration_url: str | None | UndefinedType = UNDEFINED,
|
||||
configuration_url: str | URL | None | UndefinedType = UNDEFINED,
|
||||
disabled_by: DeviceEntryDisabler | None | UndefinedType = UNDEFINED,
|
||||
entry_type: DeviceEntryType | None | UndefinedType = UNDEFINED,
|
||||
hw_version: str | None | UndefinedType = UNDEFINED,
|
||||
|
||||
@@ -15,6 +15,7 @@ from timeit import default_timer as timer
|
||||
from typing import TYPE_CHECKING, Any, Final, Literal, TypedDict, TypeVar, final
|
||||
|
||||
import voluptuous as vol
|
||||
from yarl import URL
|
||||
|
||||
from homeassistant.backports.functools import cached_property
|
||||
from homeassistant.config import DATA_CUSTOMIZE
|
||||
@@ -177,7 +178,7 @@ def get_unit_of_measurement(hass: HomeAssistant, entity_id: str) -> str | None:
|
||||
class DeviceInfo(TypedDict, total=False):
|
||||
"""Entity device information for device registry."""
|
||||
|
||||
configuration_url: str | None
|
||||
configuration_url: str | URL | None
|
||||
connections: set[tuple[str, str]]
|
||||
default_manufacturer: str
|
||||
default_model: str
|
||||
|
||||
Reference in New Issue
Block a user