Description
GET /_matrix/client/v1/media/preview_url accepts any URI scheme and hands it straight to the HTTP client. For a scheme that cannot be fetched over HTTP, the request fails deep inside the client and Synapse answers 500 with an internal TypeError in the body, instead of rejecting the input with a 4xx.
url_previewer.py never checks the scheme. urlsplit is used only in _is_url_blocked for blocklist matching (v1.159.0, synapse/media/url_previewer.py:408), and the only scheme comparison in the file is url_parts.scheme != "data" at line 682, for images inside an already fetched page. So mailto:someone@example.com, tel:+3112345678 or a bare word: all reach the fetch path.
This is reachable without a malicious client. Element Web treats a plain word followed by a colon as a link and asks the homeserver to preview it, which is how we hit it: a message containing the Dutch word ontvangen: produced the 500 below.
Steps to reproduce
As a logged-in user:
GET /_matrix/client/v1/media/preview_url?url=ontvangen%3A
mailto:someone@example.com fails the same way.
Expected
400 or 422 with a message about an unsupported or malformed URL, and no traceback in the response body. A scheme allowlist of http and https at the entry point would cover it.
Actual
HTTP 500
Failed to download content: ["TypeError: argument of type 'NoneType' is not iterable\n"]
Server log:
synapse.http.client - 465 - INFO - Error sending request to GET ontvangen:: TypeError argument of type 'NoneType' is not iterable
synapse.media.url_previewer - 498 - WARNING - Error downloading ontvangen:: TypeError("argument of type 'NoneType' is not iterable")
Both log lines come from generic except Exception handlers (synapse/http/client.py:459 and synapse/media/url_previewer.py:496), so the TypeError is raised further down, where the agent works with a URI whose host is None.
Impact
Low. It is a wrong status code plus log noise, not a crash. It does fill the logs: on our homeserver these previews are a steady source of tracebacks, which makes log-based alerting on exceptions harder to use.
Version information
- Synapse: 1.159.0
- Database: PostgreSQL
- Client: Element Web 1.12.26
- Installation: Docker image, Kubernetes
Description
GET /_matrix/client/v1/media/preview_urlaccepts any URI scheme and hands it straight to the HTTP client. For a scheme that cannot be fetched over HTTP, the request fails deep inside the client and Synapse answers500with an internalTypeErrorin the body, instead of rejecting the input with a 4xx.url_previewer.pynever checks the scheme.urlsplitis used only in_is_url_blockedfor blocklist matching (v1.159.0,synapse/media/url_previewer.py:408), and the only scheme comparison in the file isurl_parts.scheme != "data"at line 682, for images inside an already fetched page. Somailto:someone@example.com,tel:+3112345678or a bareword:all reach the fetch path.This is reachable without a malicious client. Element Web treats a plain word followed by a colon as a link and asks the homeserver to preview it, which is how we hit it: a message containing the Dutch word
ontvangen:produced the 500 below.Steps to reproduce
As a logged-in user:
mailto:someone@example.comfails the same way.Expected
400or422with a message about an unsupported or malformed URL, and no traceback in the response body. A scheme allowlist ofhttpandhttpsat the entry point would cover it.Actual
Server log:
Both log lines come from generic
except Exceptionhandlers (synapse/http/client.py:459andsynapse/media/url_previewer.py:496), so theTypeErroris raised further down, where the agent works with a URI whose host isNone.Impact
Low. It is a wrong status code plus log noise, not a crash. It does fill the logs: on our homeserver these previews are a steady source of tracebacks, which makes log-based alerting on exceptions harder to use.
Version information