393 files
This commit is contained in:
@@ -17,6 +17,7 @@ from .const import (
|
||||
MaintenanceStatus,
|
||||
TriggerEntityState,
|
||||
)
|
||||
from .entity.triggers import normalize_entity_ids
|
||||
from .helpers.schedule import read_legacy_fields
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -44,8 +45,36 @@ TO_REDACT = {
|
||||
"mpn",
|
||||
"gtin",
|
||||
"storage_location",
|
||||
# Per-member avatar overrides (#169): user ids with names/initials.
|
||||
"member_display",
|
||||
# The HA user an on-complete action runs as (a user UUID, bug audit
|
||||
# 2026-09-26) — see also _redact_actions for the action's payload.
|
||||
"configured_by",
|
||||
}
|
||||
|
||||
REDACTED = "**REDACTED**"
|
||||
|
||||
|
||||
def _redact_actions(data: Mapping[str, Any]) -> dict[str, Any]:
|
||||
"""Redact the free-form payload of every task's on-complete action.
|
||||
|
||||
The ``data`` of a service call is whatever the user typed (a message, a
|
||||
phone number, a URL with a token) — while the service name and target
|
||||
stay, they are what one debugs. A blanket ``"data"`` key in TO_REDACT
|
||||
would hit unrelated structures, so this is targeted (bug audit
|
||||
2026-09-26, SEC-11).
|
||||
"""
|
||||
tasks = data.get(CONF_TASKS)
|
||||
if not isinstance(tasks, dict):
|
||||
return dict(data)
|
||||
new_tasks: dict[str, Any] = {}
|
||||
for task_id, task in tasks.items():
|
||||
action = task.get("on_complete_action") if isinstance(task, dict) else None
|
||||
if isinstance(action, dict) and action.get("data"):
|
||||
task = {**task, "on_complete_action": {**action, "data": REDACTED}}
|
||||
new_tasks[task_id] = task
|
||||
return {**data, CONF_TASKS: new_tasks}
|
||||
|
||||
|
||||
async def async_get_config_entry_diagnostics(hass: HomeAssistant, entry: MaintenanceSupporterConfigEntry) -> dict[str, Any]:
|
||||
"""Return diagnostics for a config entry."""
|
||||
@@ -53,12 +82,15 @@ async def async_get_config_entry_diagnostics(hass: HomeAssistant, entry: Mainten
|
||||
|
||||
diag: dict[str, Any] = {
|
||||
"entry": {
|
||||
"title": entry.title,
|
||||
"unique_id": entry.unique_id,
|
||||
# An object entry's title IS the object name (redacted in the
|
||||
# data below) and its unique id is the slug of that name — both
|
||||
# went out in the clear (bug audit 2026-09-26, SEC-11).
|
||||
"title": entry.title if is_global else REDACTED,
|
||||
"unique_id": entry.unique_id if is_global else REDACTED,
|
||||
"version": entry.version,
|
||||
"is_global": is_global,
|
||||
},
|
||||
"data": async_redact_data(entry.data, TO_REDACT),
|
||||
"data": async_redact_data(_redact_actions(entry.data), TO_REDACT),
|
||||
}
|
||||
|
||||
if is_global:
|
||||
@@ -157,20 +189,9 @@ def _check_trigger_status(hass: HomeAssistant, data: Mapping[str, Any]) -> list[
|
||||
if not trigger_config:
|
||||
continue
|
||||
|
||||
entity_ids: list[str] = list(trigger_config.get("entity_ids", []))
|
||||
if not entity_ids:
|
||||
single = trigger_config.get("entity_id")
|
||||
if single:
|
||||
entity_ids = [single]
|
||||
# Compound triggers: collect entity_ids from conditions
|
||||
if not entity_ids and trigger_config.get("type") == "compound":
|
||||
for cond in trigger_config.get("conditions", []):
|
||||
for eid in cond.get("entity_ids", []):
|
||||
if eid not in entity_ids:
|
||||
entity_ids.append(eid)
|
||||
cond_eid = cond.get("entity_id")
|
||||
if cond_eid and cond_eid not in entity_ids:
|
||||
entity_ids.append(cond_eid)
|
||||
# The shared walk (compound conditions incl. their nested
|
||||
# trigger_config, which this copy missed — DRY audit 2026-09-26 B).
|
||||
entity_ids = normalize_entity_ids(trigger_config)
|
||||
if not entity_ids:
|
||||
continue
|
||||
|
||||
@@ -223,7 +244,8 @@ def _check_data_quality(data: Mapping[str, Any]) -> list[str]:
|
||||
warnings.append(f"Task {task_id} is time-based but has no interval")
|
||||
|
||||
trigger = task.get("trigger_config")
|
||||
if trigger and trigger.get("type") != "compound" and not trigger.get("entity_id"):
|
||||
# A trigger stored with only the plural entity_ids has an entity too.
|
||||
if trigger and trigger.get("type") != "compound" and not normalize_entity_ids(trigger):
|
||||
warnings.append(f"Task {task_id} has trigger config but no entity")
|
||||
|
||||
return warnings
|
||||
|
||||
Reference in New Issue
Block a user