Skip to content

Limit health endpoint to /health$#19405

Merged
anoadragon453 merged 4 commits intodevelopfrom
anoa/health_prevent_traversal
Jan 26, 2026
Merged

Limit health endpoint to /health$#19405
anoadragon453 merged 4 commits intodevelopfrom
anoa/health_prevent_traversal

Conversation

@anoadragon453
Copy link
Member

@anoadragon453 anoadragon453 commented Jan 23, 2026

Fixes #19395 by checking for /health exactly (Twisted didn't appear to make it easy to use a regex here - and an exact str check is quicker than a regex match anyhow).

Pull Request Checklist

  • Pull request is based on the develop branch
  • Pull request includes a changelog file. The entry should:
    • Be a short description of your change which makes sense to users. "Fixed a bug that prevented receiving messages from other servers." instead of "Moved X method from EventStore to EventWorkerStore.".
    • Use markdown where necessary, mostly for code blocks.
    • End with either a period (.) or an exclamation mark (!).
    • Start with a capital letter.
    • Feel free to credit yourself, by adding a sentence "Contributed by @github_username." or "Contributed by [Your Name]." to the end of the entry.
  • Code style is correct (run the linters)

Comment on lines +41 to +46
request.setResponseCode(404)
body = (
'{"errcode":"'
+ Codes.UNRECOGNIZED
+ '","error":"Unrecognized request"}'
)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't able to use a UnrecognizedRequestError here directly as no calling function would catch and convert it. And I didn't want to hardcode M_UNRECOGNIZED in case it ever changed.

I also didn't want to construct an object and then convert that to JSON, as the health endpoint is supposed to be fast.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a good approach to keep the /health endpoint logic as simple as possible and speedy.

@anoadragon453 anoadragon453 marked this pull request as ready for review January 23, 2026 15:59
@anoadragon453 anoadragon453 requested a review from a team as a code owner January 23, 2026 15:59
Copy link
Member

@devonh devonh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for taking this on so quickly and just getting it out of the way :)

Comment on lines +41 to +46
request.setResponseCode(404)
body = (
'{"errcode":"'
+ Codes.UNRECOGNIZED
+ '","error":"Unrecognized request"}'
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a good approach to keep the /health endpoint logic as simple as possible and speedy.

"""
channel = self.make_request("GET", "/health/extra/path", shorthand=False)

self.assertEqual(channel.code, 404)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also test the error body for completeness sake?
Similar to what we do for testing generic server requests

Suggested change
self.assertEqual(channel.code, 404)
self.assertEqual(channel.code, 404)
self.assertEqual(channel.json_body["error"], "Unrecognized request")
self.assertEqual(channel.json_body["errcode"], "M_UNRECOGNIZED")

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typically I'd shy away from testing error widely, as clients aren't meant to programmatically interpret it. And thus, say, updating "Unrecognized" to "Unrecognised" shouldn't cause widespread test failures (whereas M_UNRECOGNIZED should).

But I agree that we should test errcode, thus I've done so in dda2dcd. Along with testing that error is present.

Consciously avoid testing `error`s contents as they're not meant to be
machine-read.
@anoadragon453 anoadragon453 enabled auto-merge (squash) January 26, 2026 11:55
@anoadragon453 anoadragon453 merged commit 24df0ed into develop Jan 26, 2026
45 checks passed
@anoadragon453 anoadragon453 deleted the anoa/health_prevent_traversal branch January 26, 2026 12:27
alexlebens pushed a commit to alexlebens/infrastructure that referenced this pull request Feb 10, 2026
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [element-hq/synapse](https://github.com/element-hq/synapse) | minor | `1.146.0` → `1.147.0` |

---

### Release Notes

<details>
<summary>element-hq/synapse (element-hq/synapse)</summary>

### [`v1.147.0`](https://github.com/element-hq/synapse/releases/tag/v1.147.0)

[Compare Source](element-hq/synapse@v1.146.0...v1.147.0)

### Synapse 1.147.0 (2026-02-10)

No significant changes since 1.147.0rc1.

### Synapse 1.147.0rc1 (2026-02-03)

#### Bugfixes

- Fix memory leak caused by not cleaning up stopped looping calls. Introduced in v1.140.0. ([#&#8203;19416](element-hq/synapse#19416))
- Fix a typo that incorrectly made `setuptools_rust` a runtime dependency. ([#&#8203;19417](element-hq/synapse#19417))

#### Internal Changes

- Prune stale entries from `sliding_sync_connection_required_state` table. ([#&#8203;19306](element-hq/synapse#19306))
- Update "Event Send Time Quantiles" graph to only use dots for the event persistence rate (Grafana dashboard). ([#&#8203;19399](element-hq/synapse#19399))
- Update and align Grafana dashboard to use regex matching for `job` selectors (`job=~"$job"`) so the "all" value works correctly across all panels. ([#&#8203;19400](element-hq/synapse#19400))
- Don't retry joining partial state rooms all at once on startup. ([#&#8203;19402](element-hq/synapse#19402))
- Disallow requests to the health endpoint from containing trailing path characters. ([#&#8203;19405](element-hq/synapse#19405))
- Add notes that new experimental features should have associated tracking issues. ([#&#8203;19410](element-hq/synapse#19410))
- Bump `pyo3` from 0.26.0 to 0.27.2 and `pythonize` from 0.26.0 to 0.27.0. Contributed by [@&#8203;razvp](https://github.com/razvp) @&#8203; ERCOM. ([#&#8203;19412](element-hq/synapse#19412))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4zLjYiLCJ1cGRhdGVkSW5WZXIiOiI0My4zLjYiLCJ0YXJnZXRCcmFuY2giOiJtYWluIiwibGFiZWxzIjpbImltYWdlIl19-->

Reviewed-on: https://gitea.alexlebens.dev/alexlebens/infrastructure/pulls/3877
Co-authored-by: Renovate Bot <renovate-bot@alexlebens.net>
Co-committed-by: Renovate Bot <renovate-bot@alexlebens.net>
github-merge-queue bot pushed a commit to famedly/synapse that referenced this pull request Feb 18, 2026
# Famedly Synapse Release v1.147.1_1

depends on: famedly/complement#11

## Famedly additions for v1.146.0_1

None

### Notes for Famedly:

- Disallow requests to the health endpoint from containing trailing path
characters.
([\#19405](element-hq/synapse#19405))
- Block federation requests and events authenticated using a known
insecure signing key. See
[CVE-2026-24044](https://www.cve.org/CVERecord?id=CVE-2026-24044) /
[ELEMENTSEC-2025-1670](GHSA-qwcj-h6m8-vp6q).
([\#19459](element-hq/synapse#19459))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The /health resource allows for path traversal

2 participants