mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-09 10:16:29 +00:00
Sanitize event (#1908)
* Sanitise event * No need to remove supervisor token * cleanup * Typo * Review comments * Move and test * Move and use hdr
This commit is contained in:
parent
2d312c276f
commit
c9585033cb
@ -23,7 +23,6 @@ from .const import (
|
|||||||
MACHINE_ID,
|
MACHINE_ID,
|
||||||
SOCKET_DOCKER,
|
SOCKET_DOCKER,
|
||||||
SUPERVISOR_VERSION,
|
SUPERVISOR_VERSION,
|
||||||
CoreStates,
|
|
||||||
LogLevel,
|
LogLevel,
|
||||||
UpdateChannels,
|
UpdateChannels,
|
||||||
)
|
)
|
||||||
@ -35,6 +34,7 @@ from .hassos import HassOS
|
|||||||
from .homeassistant import HomeAssistant
|
from .homeassistant import HomeAssistant
|
||||||
from .host import HostManager
|
from .host import HostManager
|
||||||
from .ingress import Ingress
|
from .ingress import Ingress
|
||||||
|
from .misc.filter import filter_data
|
||||||
from .misc.hwmon import HwMonitor
|
from .misc.hwmon import HwMonitor
|
||||||
from .misc.scheduler import Scheduler
|
from .misc.scheduler import Scheduler
|
||||||
from .misc.secrets import SecretsManager
|
from .misc.secrets import SecretsManager
|
||||||
@ -281,45 +281,9 @@ def supervisor_debugger(coresys: CoreSys) -> None:
|
|||||||
|
|
||||||
def setup_diagnostics(coresys: CoreSys) -> None:
|
def setup_diagnostics(coresys: CoreSys) -> None:
|
||||||
"""Sentry diagnostic backend."""
|
"""Sentry diagnostic backend."""
|
||||||
dev_env: bool = bool(os.environ.get(ENV_SUPERVISOR_DEV, 0))
|
|
||||||
|
|
||||||
def filter_data(event, hint):
|
def filter_event(event, _hint):
|
||||||
# Ignore issue if system is not supported or diagnostics is disabled
|
return filter_data(coresys, event)
|
||||||
if not coresys.config.diagnostics or not coresys.supported or dev_env:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Not full startup - missing information
|
|
||||||
if coresys.core.state in (CoreStates.INITIALIZE, CoreStates.SETUP):
|
|
||||||
return event
|
|
||||||
|
|
||||||
# Update information
|
|
||||||
event.setdefault("extra", {}).update(
|
|
||||||
{
|
|
||||||
"supervisor": {
|
|
||||||
"machine": coresys.machine,
|
|
||||||
"arch": coresys.arch.default,
|
|
||||||
"docker": coresys.docker.info.version,
|
|
||||||
"channel": coresys.updater.channel,
|
|
||||||
"supervisor": coresys.supervisor.version,
|
|
||||||
"os": coresys.hassos.version,
|
|
||||||
"host": coresys.host.info.operating_system,
|
|
||||||
"kernel": coresys.host.info.kernel,
|
|
||||||
"core": coresys.homeassistant.version,
|
|
||||||
"audio": coresys.plugins.audio.version,
|
|
||||||
"dns": coresys.plugins.dns.version,
|
|
||||||
"multicast": coresys.plugins.multicast.version,
|
|
||||||
"cli": coresys.plugins.cli.version,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
event.setdefault("tags", {}).update(
|
|
||||||
{
|
|
||||||
"installation_type": "os" if coresys.hassos.available else "supervised",
|
|
||||||
"machine": coresys.machine,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return event
|
|
||||||
|
|
||||||
# Set log level
|
# Set log level
|
||||||
sentry_logging = LoggingIntegration(
|
sentry_logging = LoggingIntegration(
|
||||||
@ -329,7 +293,7 @@ def setup_diagnostics(coresys: CoreSys) -> None:
|
|||||||
_LOGGER.info("Initialize Supervisor Sentry")
|
_LOGGER.info("Initialize Supervisor Sentry")
|
||||||
sentry_sdk.init(
|
sentry_sdk.init(
|
||||||
dsn="https://9c6ea70f49234442b4746e447b24747e@o427061.ingest.sentry.io/5370612",
|
dsn="https://9c6ea70f49234442b4746e447b24747e@o427061.ingest.sentry.io/5370612",
|
||||||
before_send=filter_data,
|
before_send=filter_event,
|
||||||
max_breadcrumbs=30,
|
max_breadcrumbs=30,
|
||||||
integrations=[AioHttpIntegration(), sentry_logging],
|
integrations=[AioHttpIntegration(), sentry_logging],
|
||||||
release=SUPERVISOR_VERSION,
|
release=SUPERVISOR_VERSION,
|
||||||
|
82
supervisor/misc/filter.py
Normal file
82
supervisor/misc/filter.py
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
"""Filter tools."""
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
|
from aiohttp import hdrs
|
||||||
|
|
||||||
|
from ..const import ENV_SUPERVISOR_DEV, HEADER_TOKEN_OLD, CoreStates
|
||||||
|
from ..coresys import CoreSys
|
||||||
|
|
||||||
|
RE_URL: re.Pattern = re.compile(r"(\w+:\/\/)(.*\.\w+)(.*)")
|
||||||
|
|
||||||
|
|
||||||
|
def sanitize_url(url: str) -> str:
|
||||||
|
"""Return a sanitized url."""
|
||||||
|
if not re.match(RE_URL, url):
|
||||||
|
# Not a URL, just return it back
|
||||||
|
return url
|
||||||
|
|
||||||
|
return re.sub(RE_URL, r"\1example.com\3", url)
|
||||||
|
|
||||||
|
|
||||||
|
def filter_data(coresys: CoreSys, event: dict) -> dict:
|
||||||
|
"""Filter event data before sending to sentry."""
|
||||||
|
dev_env: bool = bool(os.environ.get(ENV_SUPERVISOR_DEV, 0))
|
||||||
|
|
||||||
|
# Ignore issue if system is not supported or diagnostics is disabled
|
||||||
|
if not coresys.config.diagnostics or not coresys.supported or dev_env:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Not full startup - missing information
|
||||||
|
if coresys.core.state in (CoreStates.INITIALIZE, CoreStates.SETUP):
|
||||||
|
return event
|
||||||
|
|
||||||
|
# Update information
|
||||||
|
event.setdefault("extra", {}).update(
|
||||||
|
{
|
||||||
|
"supervisor": {
|
||||||
|
"machine": coresys.machine,
|
||||||
|
"arch": coresys.arch.default,
|
||||||
|
"docker": coresys.docker.info.version,
|
||||||
|
"channel": coresys.updater.channel,
|
||||||
|
"supervisor": coresys.supervisor.version,
|
||||||
|
"os": coresys.hassos.version,
|
||||||
|
"host": coresys.host.info.operating_system,
|
||||||
|
"kernel": coresys.host.info.kernel,
|
||||||
|
"core": coresys.homeassistant.version,
|
||||||
|
"audio": coresys.plugins.audio.version,
|
||||||
|
"dns": coresys.plugins.dns.version,
|
||||||
|
"multicast": coresys.plugins.multicast.version,
|
||||||
|
"cli": coresys.plugins.cli.version,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
event.setdefault("tags", []).extend(
|
||||||
|
[
|
||||||
|
["installation_type", "os" if coresys.hassos.available else "supervised"],
|
||||||
|
["machine", coresys.machine],
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Sanitize event
|
||||||
|
for i, tag in enumerate(event.get("tags", [])):
|
||||||
|
key, value = tag
|
||||||
|
if key == "url":
|
||||||
|
event["tags"][i] = [key, sanitize_url(value)]
|
||||||
|
|
||||||
|
if event.get("request"):
|
||||||
|
if event["request"].get("url"):
|
||||||
|
event["request"]["url"] = sanitize_url(event["request"]["url"])
|
||||||
|
|
||||||
|
for i, header in enumerate(event["request"].get("headers", [])):
|
||||||
|
key, value = header
|
||||||
|
if key == hdrs.REFERER:
|
||||||
|
event["request"]["headers"][i] = [key, sanitize_url(value)]
|
||||||
|
|
||||||
|
if key == HEADER_TOKEN_OLD:
|
||||||
|
event["request"]["headers"][i] = [key, "XXXXXXXXXXXXXXXXXXX"]
|
||||||
|
|
||||||
|
if key in [hdrs.HOST, hdrs.X_FORWARDED_HOST]:
|
||||||
|
event["request"]["headers"][i] = [key, "example.com"]
|
||||||
|
print(event)
|
||||||
|
return event
|
@ -8,7 +8,8 @@ import socket
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||||
RE_STRING = re.compile(r"\x1b(\[.*?[@-~]|\].*?(\x07|\x1b\\))")
|
|
||||||
|
RE_STRING: re.Pattern = re.compile(r"\x1b(\[.*?[@-~]|\].*?(\x07|\x1b\\))")
|
||||||
|
|
||||||
|
|
||||||
def convert_to_ascii(raw: bytes) -> str:
|
def convert_to_ascii(raw: bytes) -> str:
|
||||||
|
@ -5,7 +5,7 @@ import pytest
|
|||||||
|
|
||||||
from supervisor.bootstrap import initialize_coresys
|
from supervisor.bootstrap import initialize_coresys
|
||||||
|
|
||||||
# pylint: disable=redefined-outer-name
|
# pylint: disable=redefined-outer-name, protected-access
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@ -26,6 +26,7 @@ async def coresys(loop, docker):
|
|||||||
coresys_obj = await initialize_coresys()
|
coresys_obj = await initialize_coresys()
|
||||||
|
|
||||||
coresys_obj.ingress.save_data = MagicMock()
|
coresys_obj.ingress.save_data = MagicMock()
|
||||||
|
coresys_obj.arch._default_arch = "amd64"
|
||||||
|
|
||||||
yield coresys_obj
|
yield coresys_obj
|
||||||
|
|
||||||
|
85
tests/misc/test_filter_data.py
Normal file
85
tests/misc/test_filter_data.py
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
"""Test sentry data filter."""
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from supervisor.const import CoreStates
|
||||||
|
from supervisor.misc.filter import filter_data
|
||||||
|
|
||||||
|
SAMPLE_EVENT = {"sample": "event"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_diagnostics_disabled(coresys):
|
||||||
|
"""Test if diagnostics is disabled."""
|
||||||
|
coresys.config.diagnostics = False
|
||||||
|
coresys.supported = True
|
||||||
|
assert filter_data(coresys, SAMPLE_EVENT) is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_not_supported(coresys):
|
||||||
|
"""Test if not supported."""
|
||||||
|
coresys.config.diagnostics = True
|
||||||
|
coresys.supported = False
|
||||||
|
assert filter_data(coresys, SAMPLE_EVENT) is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_is_dev(coresys):
|
||||||
|
"""Test if dev."""
|
||||||
|
coresys.config.diagnostics = True
|
||||||
|
coresys.supported = True
|
||||||
|
with patch("os.environ", return_value=[("ENV_SUPERVISOR_DEV", "1")]):
|
||||||
|
assert filter_data(coresys, SAMPLE_EVENT) is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_not_started(coresys):
|
||||||
|
"""Test if supervisor not fully started."""
|
||||||
|
coresys.config.diagnostics = True
|
||||||
|
coresys.supported = True
|
||||||
|
|
||||||
|
coresys.core.state = CoreStates.INITIALIZE
|
||||||
|
assert filter_data(coresys, SAMPLE_EVENT) == SAMPLE_EVENT
|
||||||
|
|
||||||
|
coresys.core.state = CoreStates.SETUP
|
||||||
|
assert filter_data(coresys, SAMPLE_EVENT) == SAMPLE_EVENT
|
||||||
|
|
||||||
|
|
||||||
|
def test_defaults(coresys):
|
||||||
|
"""Test event defaults."""
|
||||||
|
coresys.config.diagnostics = True
|
||||||
|
coresys.supported = True
|
||||||
|
|
||||||
|
coresys.core.state = CoreStates.RUNNING
|
||||||
|
filtered = filter_data(coresys, SAMPLE_EVENT)
|
||||||
|
|
||||||
|
assert ["installation_type", "supervised"] in filtered["tags"]
|
||||||
|
assert filtered["extra"]["supervisor"]["arch"] == "amd64"
|
||||||
|
|
||||||
|
|
||||||
|
def test_sanitize(coresys):
|
||||||
|
"""Test event sanitation."""
|
||||||
|
event = {
|
||||||
|
"tags": [["url", "https://mydomain.com"]],
|
||||||
|
"request": {
|
||||||
|
"url": "https://mydomain.com",
|
||||||
|
"headers": [
|
||||||
|
["Host", "mydomain.com"],
|
||||||
|
["Referer", "https://mydomain.com/api/hassio_ingress/xxx-xxx/"],
|
||||||
|
["X-Forwarded-Host", "mydomain.com"],
|
||||||
|
["X-Hassio-Key", "xxx"],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
coresys.config.diagnostics = True
|
||||||
|
coresys.supported = True
|
||||||
|
|
||||||
|
coresys.core.state = CoreStates.RUNNING
|
||||||
|
filtered = filter_data(coresys, event)
|
||||||
|
|
||||||
|
assert ["url", "https://example.com"] in filtered["tags"]
|
||||||
|
|
||||||
|
assert filtered["request"]["url"] == "https://example.com"
|
||||||
|
|
||||||
|
assert ["Host", "example.com"] in filtered["request"]["headers"]
|
||||||
|
assert ["Referer", "https://example.com/api/hassio_ingress/xxx-xxx/"] in filtered[
|
||||||
|
"request"
|
||||||
|
]["headers"]
|
||||||
|
assert ["X-Forwarded-Host", "example.com"] in filtered["request"]["headers"]
|
||||||
|
assert ["X-Hassio-Key", "XXXXXXXXXXXXXXXXXXX"] in filtered["request"]["headers"]
|
9
tests/misc/test_sanitise_url.py
Normal file
9
tests/misc/test_sanitise_url.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
"""Test supervisor.utils.sanitize_url."""
|
||||||
|
from supervisor.misc.filter import sanitize_url
|
||||||
|
|
||||||
|
|
||||||
|
def test_sanitize_url():
|
||||||
|
"""Test supervisor.utils.sanitize_url."""
|
||||||
|
assert sanitize_url("test") == "test"
|
||||||
|
assert sanitize_url("http://my.duckdns.org") == "http://example.com"
|
||||||
|
assert sanitize_url("http://my.duckdns.org/test") == "http://example.com/test"
|
Loading…
x
Reference in New Issue
Block a user