diff --git a/packages/reflex-components-core/src/reflex_components_core/core/_upload.py b/packages/reflex-components-core/src/reflex_components_core/core/_upload.py index 423bc83403c..11be8771a9a 100644 --- a/packages/reflex-components-core/src/reflex_components_core/core/_upload.py +++ b/packages/reflex-components-core/src/reflex_components_core/core/_upload.py @@ -796,7 +796,8 @@ async def upload_file(request: Request): Raises: UploadValueError: If the handler does not have a supported annotation. UploadTypeError: If a non-streaming upload is wired to a background task. - HTTPException: when the request does not include token / handler headers. + HTTPException: when the request does not include token / handler headers, + or when the handler header does not name a registered event handler. """ from reflex_base.event import ( resolve_upload_chunk_handler_param, @@ -805,9 +806,14 @@ async def upload_file(request: Request): from reflex_base.registry import RegistrationContext token, handler_name = _require_upload_headers(request) - registered_event_handler = RegistrationContext.get().event_handlers[ + registered_event_handler = RegistrationContext.get().event_handlers.get([ handler_name - ] + ]) + if registered_event_handler is None: + raise HTTPException( + status_code=400, + detail=f"Unknown upload event handler: {handler_name!r}.", + ) event_handler = registered_event_handler.handler if event_handler.is_background: diff --git a/tests/units/test_app.py b/tests/units/test_app.py index c50af0271db..2d9953a0f94 100644 --- a/tests/units/test_app.py +++ b/tests/units/test_app.py @@ -36,6 +36,7 @@ from reflex_components_radix.themes.typography.text import Text from starlette.applications import Starlette from starlette.datastructures import FormData, Headers, UploadFile +from starlette.exceptions import HTTPException from starlette.requests import ClientDisconnect from starlette.responses import StreamingResponse from starlette_admin.auth import AuthProvider @@ -1434,6 +1435,37 @@ async def form(): # noqa: RUF029 await app.state_manager.close() +@pytest.mark.asyncio +async def test_upload_file_unknown_handler_returns_400( + token: str, +): + """Test that an unregistered upload event handler raises a controlled 400. + + A stale, misspelled, or since-removed handler name in the + ``reflex-event-handler`` header must not fall through to an unhandled + ``KeyError`` (which Starlette would surface as a 500); it should raise a + ``HTTPException`` before any form parsing or event dispatch happens. + + Args: + token: a Token. + """ + app = App(_state=State) + + request_mock = unittest.mock.Mock() + request_mock.headers = { + "reflex-client-token": token, + "reflex-event-handler": "no.such.State.handler", + } + + fn = upload(app) + with pytest.raises(HTTPException) as err: + await fn(request_mock) + assert err.value.status_code == 400 + assert err.value.detail == "Unknown upload event handler: 'no.such.State.handler'." + # The form should never have been read: the handler lookup fails first. + request_mock.form.assert_not_called() + await app.state_manager.close() + @pytest.mark.asyncio @pytest.mark.parametrize(