fix: v2.5.1 security/ML/reliability patch

Security:
- H1 DNS rebinding TOCTOU: validate_endpoint returns (endpoint, resolved_ips);
  create_pinned_session builds an aiohttp session with a custom resolver that
  returns only the pre-validated IPs, closing the re-resolve gap.
- H2/M3 Shared session cookie pollution: integration no longer uses HA shared
  clientsession; isolated session with DummyCookieJar prevents cross-integration
  cookie leaks.
- Cloud metadata / link-local block added to allow_local_network mode to
  prevent IMDS exfiltration on cloud VMs.
- L3 hard cap extended to history.async_get_history (not only service schema).

ML/LLM correctness:
- _is_openai_reasoning_model uses regex with gpt-5-chat* blacklist and handles
  OpenRouter-style openai/ prefix. Future o5/gpt-6 auto-recognized.
- reasoning_effort=minimal for gpt-5 family, low for o-series.
- Gemini 2.5 Pro gets thinking_budget=128 (Pro rejects 0, flash accepts 0).
- Anthropic extracts first type=text block instead of hardcoded content[0].
- /no_think dedup uses word-boundary regex instead of substring.
- DeepSeek-reasoner detected: skips /no_think; preserves reasoning_content.

Reliability:
- Exception chaining added to Gemini-block re-raises.
- Top-level imports for json/re/hashlib (no more lazy stdlib imports).
- allow_local_network logged at INFO, not WARNING on every setup.

Code style:
- PEP 604 type hints across all .py files (dict/list/| None).

No breaking changes for users. Internal API: validate_endpoint return type
changed from str to tuple[str, list[str]].
This commit is contained in:
SMKRV
2026-04-17 01:58:16 +03:00
parent e8b9b911ef
commit 6e635f7e2f
11 changed files with 367 additions and 204 deletions
+3 -4
View File
@@ -10,7 +10,7 @@ from __future__ import annotations
import logging
import math
from typing import Any, Dict
from typing import Any
from homeassistant.components.sensor import (
SensorEntity,
SensorEntityDescription,
@@ -79,7 +79,6 @@ _LOGGER = logging.getLogger(__name__)
_ATTR_TEXT_LIMIT = 2048
_ATTR_PROMPT_LIMIT = 512
async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
@@ -185,7 +184,7 @@ class HATextAISensor(CoordinatorEntity, SensorEntity):
return None
return value
def _sanitize_attributes(self, attributes: Dict[str, Any]) -> Dict[str, Any]:
def _sanitize_attributes(self, attributes: dict[str, Any]) -> dict[str, Any]:
"""Sanitize all attributes for JSON serialization."""
sanitized = {
key: self._sanitize_value(value)
@@ -231,7 +230,7 @@ class HATextAISensor(CoordinatorEntity, SensorEntity):
return ENTITY_ICON
@property
def extra_state_attributes(self) -> Dict[str, Any]:
def extra_state_attributes(self) -> dict[str, Any]:
"""Return entity specific state attributes."""
if not self.coordinator.data:
return {}