Logs: httpx records every outbound URL at INFO, so staff read other people's capture, webhook and connection addresses #548
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
performance
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo#548
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found on 2026-09-29 in a read-only review of postulo at
95348908d, by reading the logging configuration and watching what an outbound request writes.Checked again on 2026-10-01 against
mainat0a5cad587: still present. The code is now atcore/server_views.py:476-505(LogsView) andplugins/http.py:139-145(_build);config/settings/base.py:798-808and the webhook lines are where they were. The same request repeated on today's code wrote the same record, with thehttpxlogger still at level 20.httpxlogs every request it makes at INFO, asHTTP Request: <METHOD> <full URL> "<status>". The root logger runs at INFO andLOGGINGquietens onlyfontToolsandweasyprint, so each of those lines reaches the console and the JSON file log, which is what Server settings → Logs and the/logsendpoint serve. The URLs are other people's data: the postings they capture, their portfolio links, the paths on their DAV and Paperless servers, and their webhook receiver addresses. Some receivers treat the address itself as the only secret (a Home Assistant/api/webhook/<id>, an n8n/webhook/<uuid>). The threat model promises that administrators see accounts, never anyone's applications or documents; this crosses that line and writes credentials into the log (CWE-532).config/settings/base.py:798-808("loggers"names onlyfontToolsandweasyprint; root level fromPOSTULO_LOG_LEVEL, defaultINFO); the file log read byLogsView(core/server_views.py:474-503,logs.read,logs.loggers) and served bycore/views_logs.py:collect; requests made throughplugins/http.py:143(_build), for exampleplugins/webhook/__init__.py:241-242, called from the scheduler atnotifications/webhooks.py:131.postulo.plugins.http.client()tohttps://ha.example/api/webhook/SECRETID?x=1produced the recordhttpx INFO HTTP Request: POST https://ha.example/api/webhook/SECRETID?x=1 "HTTP/1.1 200 OK", with the effective level of thehttpxlogger at 20. Under the default policy the host is replaced by the pinned address, but the path and query are logged in full; with private destinations allowed, the host name is logged as well.core/logs.pyhas no redacting filter;install_record_factoryadds only a request id.httpxlogger; a log collector holdingPOSTULO_LOGS_TOKENreceives the same lines."httpx": {"level": "WARNING"}and"httpcore": {"level": "WARNING"}toLOGGING["loggers"], as was done for WeasyPrint. If a request trail is wanted, log it from a response hook inplugins/http._buildwith the scheme, host and status only, never the path or query.Done when
tests/test_logs.pymakes a request throughplugins.http.client()against a mock transport and asserts that no record at INFO or below contains the request's path.