| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081 |
- """A refused header's value must not reach a notification's stored error.
- A Home Assistant token with a stray space makes httpx refuse the header and
- quote its value. The send's error text went into the provider's last_error and
- the notification log, which notification readers (Viewers, by default) read
- through the API, and back to whoever ran a test notification. The token is an
- admin-only setting.
- """
- from __future__ import annotations
- import socket
- import pytest
- from sqlalchemy import select
- from backend.app.models.notification import NotificationLog, NotificationProvider
- from backend.app.services.notification_service import NotificationService
- # Made up. Never paste a value from a real log here.
- SECRET = "ha-notify-value-7"
- @pytest.fixture
- def home_assistant(monkeypatch):
- """HA settings from the environment, pointed at a socket that accepts."""
- server = socket.socket()
- server.bind(("127.0.0.1", 0))
- server.listen()
- monkeypatch.setenv("HA_URL", f"http://127.0.0.1:{server.getsockname()[1]}")
- monkeypatch.setenv("HA_TOKEN", f"{SECRET} ")
- yield
- server.close()
- @pytest.mark.asyncio
- async def test_a_failed_send_stores_no_header_value(db_session, home_assistant):
- provider = NotificationProvider(
- name="HA", provider_type="homeassistant", enabled=True, config="{}", attach_photo=False
- )
- db_session.add(provider)
- await db_session.commit()
- await NotificationService()._send_to_providers([provider], "Title", "Body", db_session, event_type="test")
- await db_session.refresh(provider)
- log = (await db_session.execute(select(NotificationLog))).scalar_one()
- assert provider.last_error and "Illegal header value [REDACTED]" in provider.last_error
- assert SECRET not in provider.last_error
- assert log.error_message and SECRET not in log.error_message
- @pytest.mark.asyncio
- async def test_a_test_notification_returns_no_header_value(db_session, home_assistant):
- success, message = await NotificationService().send_test_notification(
- "homeassistant", {}, db=db_session, attach_photo=False
- )
- assert success is False
- assert "Illegal header value [REDACTED]" in message
- assert SECRET not in message
- @pytest.mark.asyncio
- async def test_an_error_raised_out_of_a_send_is_stored_masked(db_session):
- """The dispatcher's own except stores str(e) as well."""
- provider = NotificationProvider(name="X", provider_type="webhook", enabled=True, config="{}")
- db_session.add(provider)
- await db_session.commit()
- service = NotificationService()
- async def _raise(*_args, **_kwargs):
- raise RuntimeError(f"Illegal header value b'Bearer {SECRET} '")
- service._send_to_provider = _raise
- await service._send_to_providers([provider], "Title", "Body", db_session, event_type="test")
- await db_session.refresh(provider)
- log = (await db_session.execute(select(NotificationLog))).scalar_one()
- assert SECRET not in (provider.last_error or "") and "[REDACTED]" in provider.last_error
- assert SECRET not in (log.error_message or "")
|