fix: Fail-closed pinned resolver, close session on HA stop, drop dead workflow step

- _PinnedResolver raises on unpinned hosts instead of falling back to
  live DNS: nothing legitimate resolves other hosts on a pinned session
  (redirects are off), so fail closed
- dedicated session is also closed on EVENT_HOMEASSISTANT_CLOSE: core
  stop does not unload entries, and the raw ClientSession has no HA
  auto-cleanup, which left 'Unclosed client session' logs at shutdown
- hassfest.yaml: remove the version-print step, hassfest runs inside
  the action's docker image and is never on the runner PATH
This commit is contained in:
SMKRV
2026-07-07 01:49:03 +03:00
parent fc59f584a3
commit 65dbb5dfc0
3 changed files with 20 additions and 11 deletions
+6 -5
View File
@@ -17,7 +17,6 @@ from urllib.parse import urlparse
import aiohttp
from aiohttp.abc import AbstractResolver
from aiohttp.resolver import DefaultResolver
from homeassistant.const import CONF_API_KEY
from homeassistant.core import HomeAssistant
@@ -86,10 +85,8 @@ class _PinnedResolver(AbstractResolver):
def __init__(
self,
pinned: dict[str, list[tuple[str, int]]],
fallback: AbstractResolver | None = None,
) -> None:
self._pinned = pinned
self._fallback = fallback or DefaultResolver()
async def resolve(
self,
@@ -99,7 +96,11 @@ class _PinnedResolver(AbstractResolver):
) -> list[dict[str, Any]]:
entries = self._pinned.get(host.lower())
if entries is None:
return await self._fallback.resolve(host, port, family)
# Every request on a pinned session must target the validated
# host. Resolving anything else means a request escaped the pin
# (a new call site or a config bug) — fail closed rather than
# fall back to live, unvalidated DNS.
raise OSError(f"Refusing to resolve unpinned host: {host}")
return [
{
"hostname": host,
@@ -113,7 +114,7 @@ class _PinnedResolver(AbstractResolver):
]
async def close(self) -> None:
await self._fallback.close()
"""Nothing to close; the resolver holds only the pinned map."""
def _family_for(ip: str) -> int:
"""Return AF_INET or AF_INET6 based on the IP literal."""