mirror of
https://github.com/home-assistant/core.git
synced 2026-09-25 07:51:46 -05:00
Redact credentials from go2rtc server log output (#181583)
This commit is contained in:
@@ -4,6 +4,7 @@ import asyncio
|
||||
from collections import deque
|
||||
from contextlib import suppress
|
||||
import logging
|
||||
import re
|
||||
from tempfile import NamedTemporaryFile
|
||||
|
||||
from aiohttp import ClientSession
|
||||
@@ -105,6 +106,21 @@ _LOG_LEVEL_MAP = {
|
||||
}
|
||||
|
||||
|
||||
# go2rtc logs stream urls verbatim, which may embed camera credentials.
|
||||
# Upstream strips the url userinfo since AlexxIT/go2rtc#2051, but no release
|
||||
# includes it yet (v1.9.14 is the newest). It does not touch query parameters.
|
||||
_URL_USERINFO = re.compile(r"://[^/?#\s@]+@")
|
||||
_URL_CREDENTIAL_QUERY = re.compile(
|
||||
r"([?&](?:auth|user|password)=)[^&\s]+", re.IGNORECASE
|
||||
)
|
||||
|
||||
|
||||
def _redact_url_credentials(msg: str) -> str:
|
||||
"""Redact credentials from urls in a go2rtc log message."""
|
||||
msg = _URL_USERINFO.sub("://****@", msg)
|
||||
return _URL_CREDENTIAL_QUERY.sub(r"\1****", msg)
|
||||
|
||||
|
||||
class Go2RTCServerStartError(HomeAssistantError):
|
||||
"""Raised when server does not start."""
|
||||
|
||||
@@ -238,7 +254,7 @@ class Server:
|
||||
assert process.stdout is not None
|
||||
|
||||
async for line in process.stdout:
|
||||
msg = line[:-1].decode().strip()
|
||||
msg = _redact_url_credentials(line[:-1].decode().strip())
|
||||
self._log_buffer.append(msg)
|
||||
loglevel = logging.WARNING
|
||||
if len(split_msg := msg.split(" ", 2)) == 3:
|
||||
|
||||
@@ -425,3 +425,48 @@ async def test_server_restart_error(
|
||||
assert "Unexpected error when restarting go2rtc server" in caplog.text
|
||||
|
||||
await server.stop()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("server_stdout", "expected_stdout"),
|
||||
[
|
||||
(
|
||||
[
|
||||
"09:00:03.467 INF [api] listen addr=127.0.0.1:1984",
|
||||
"10:27:02.622 WRN producer.go:170 >"
|
||||
' error="read tcp 192.168.1.96:42550->192.168.1.145:554: i/o timeout"'
|
||||
" url=rtsp://admin:hunter2@192.168.1.145:554/Preview_01_sub",
|
||||
"10:27:02.623 WRN [streams] url=http://192.168.1.145/snapshot"
|
||||
"?auth=token&channel=0&user=admin&password=hunter2",
|
||||
"10:27:02.624 WRN [streams] url=http://camera.local"
|
||||
"?user=alice@example.com",
|
||||
],
|
||||
[
|
||||
"10:27:02.622 WRN producer.go:170 >"
|
||||
' error="read tcp 192.168.1.96:42550->192.168.1.145:554: i/o timeout"'
|
||||
" url=rtsp://****@192.168.1.145:554/Preview_01_sub",
|
||||
"10:27:02.623 WRN [streams] url=http://192.168.1.145/snapshot"
|
||||
"?auth=****&channel=0&user=****&password=****",
|
||||
"10:27:02.624 WRN [streams] url=http://camera.local?user=****",
|
||||
],
|
||||
)
|
||||
],
|
||||
)
|
||||
@pytest.mark.usefixtures("mock_tempfile", "rest_client")
|
||||
async def test_credentials_redacted_from_server_output(
|
||||
hass: HomeAssistant,
|
||||
mock_create_subprocess: MagicMock,
|
||||
server: Server,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
expected_stdout: list[str],
|
||||
) -> None:
|
||||
"""Test credentials in urls are redacted from the logged server output."""
|
||||
await server.start()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert_server_output_logged(expected_stdout, caplog, logging.WARNING)
|
||||
assert "hunter2" not in caplog.text
|
||||
assert "token" not in caplog.text
|
||||
assert "alice@example.com" not in caplog.text
|
||||
|
||||
await server.stop()
|
||||
|
||||
Reference in New Issue
Block a user