element-hq / element-hq/synapse
URL preview accepts any URI scheme and returns 500 with an internal TypeError
- Dominant language
- Python
- Stars
- 4.6k
- Forks
- 600
- Avg merge
- 5d 22h
- Merged PRs (30d)
- 51
Description
### 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
Contributor guide
Research direction
Start in synapse/media/url_previewer.py at the preview entry point and the URL handling around lines 408 and 682; compare it with the GET /_matrix/client/v1/media/preview_url request path. Reproduce with url=ontvangen: and mailto:someone@example.com. Done means unsupported schemes receive a 4xx response with no traceback or internal TypeError in the response body.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100