Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Respond with proper error responses on unknown paths. #14621

Merged
merged 6 commits into from
Dec 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/14621.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Return spec-compliant JSON errors when unknown endpoints are requested.
6 changes: 2 additions & 4 deletions synapse/api/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,10 +300,8 @@ def __init__(self, session_id: str, result: "JsonDict"):
class UnrecognizedRequestError(SynapseError):
"""An error indicating we don't understand the request you're trying to make"""

def __init__(
self, msg: str = "Unrecognized request", errcode: str = Codes.UNRECOGNIZED
):
super().__init__(400, msg, errcode)
def __init__(self, msg: str = "Unrecognized request", code: int = 400):
super().__init__(code, msg, Codes.UNRECOGNIZED)


class NotFoundError(SynapseError):
Expand Down
19 changes: 18 additions & 1 deletion synapse/http/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,24 @@ def _unrecognised_request_handler(request: Request) -> NoReturn:
Args:
request: Unused, but passed in to match the signature of ServletCallback.
"""
raise UnrecognizedRequestError()
raise UnrecognizedRequestError(code=404)


class UnrecognizedRequestResource(resource.Resource):
"""
Similar to twisted.web.resource.NoResource, but returns a JSON 404 with an
errcode of M_UNRECOGNIZED.
"""

def render(self, request: SynapseRequest) -> int:
f = failure.Failure(UnrecognizedRequestError(code=404))
return_json_error(f, request, None)
# A response has already been sent but Twisted requires either NOT_DONE_YET
# or the response bytes as a return value.
return NOT_DONE_YET

def getChild(self, name: str, request: Request) -> resource.Resource:
return self


class RootRedirect(resource.Resource):
Expand Down
4 changes: 2 additions & 2 deletions synapse/rest/media/v1/media_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import twisted.internet.error
import twisted.web.http
from twisted.internet.defer import Deferred
from twisted.web.resource import Resource

from synapse.api.errors import (
FederationDeniedError,
Expand All @@ -35,6 +34,7 @@
)
from synapse.config._base import ConfigError
from synapse.config.repository import ThumbnailRequirement
from synapse.http.server import UnrecognizedRequestResource
from synapse.http.site import SynapseRequest
from synapse.logging.context import defer_to_thread
from synapse.metrics.background_process_metrics import run_as_background_process
Expand Down Expand Up @@ -1046,7 +1046,7 @@ async def _remove_local_media_from_disk(
return removed_media, len(removed_media)


class MediaRepositoryResource(Resource):
class MediaRepositoryResource(UnrecognizedRequestResource):
"""File uploading and downloading.

Uploads are POSTed to a resource which returns a token which is used to GET
Expand Down
6 changes: 4 additions & 2 deletions synapse/util/httpresourcetree.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
import logging
from typing import Dict

from twisted.web.resource import NoResource, Resource
from twisted.web.resource import Resource

from synapse.http.server import UnrecognizedRequestResource

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -49,7 +51,7 @@ def create_resource_tree(
for path_seg in full_path.split(b"/")[1:-1]:
if path_seg not in last_resource.listNames():
# resource doesn't exist, so make a "dummy resource"
child_resource: Resource = NoResource()
child_resource: Resource = UnrecognizedRequestResource()
last_resource.putChild(path_seg, child_resource)
res_id = _resource_id(last_resource, path_seg)
resource_mappings[res_id] = child_resource
Expand Down
2 changes: 1 addition & 1 deletion tests/rest/admin/test_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -3994,7 +3994,7 @@ def test_user_is_not_local(self, method: str) -> None:
"""
Tests that shadow-banning for a user that is not a local returns a 400
"""
url = "/_synapse/admin/v1/whois/@unknown_person:unknown_domain"
url = "/_synapse/admin/v1/users/@unknown_person:unknown_domain/shadow_ban"
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 think this had essentially been testing garbage. Oops.


channel = self.make_request(method, url, access_token=self.admin_user_tok)
self.assertEqual(400, channel.code, msg=channel.json_body)
Expand Down
4 changes: 2 additions & 2 deletions tests/rest/client/test_login_token_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,13 @@ def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:

def test_disabled(self) -> None:
channel = self.make_request("POST", endpoint, {}, access_token=None)
self.assertEqual(channel.code, 400)
self.assertEqual(channel.code, 404)

self.register_user(self.user, self.password)
token = self.login(self.user, self.password)

channel = self.make_request("POST", endpoint, {}, access_token=token)
self.assertEqual(channel.code, 400)
self.assertEqual(channel.code, 404)

@override_config({"experimental_features": {"msc3882_enabled": True}})
def test_require_auth(self) -> None:
Expand Down
2 changes: 1 addition & 1 deletion tests/rest/client/test_rendezvous.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def make_homeserver(self, reactor: MemoryReactor, clock: Clock) -> HomeServer:

def test_disabled(self) -> None:
channel = self.make_request("POST", endpoint, {}, access_token=None)
self.assertEqual(channel.code, 400)
self.assertEqual(channel.code, 404)

@override_config({"experimental_features": {"msc3886_endpoint": "/asd"}})
def test_redirect(self) -> None:
Expand Down
2 changes: 1 addition & 1 deletion tests/test_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ def _callback(request: SynapseRequest, **kwargs: object) -> None:
self.reactor, FakeSite(res, self.reactor), b"GET", b"/_matrix/foobar"
)

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

Expand Down