From c01c7488e6251e8c0f62d3620965b9775d931410 Mon Sep 17 00:00:00 2001 From: temrjan Date: Fri, 3 Apr 2026 09:17:44 +0500 Subject: [PATCH] fix: prevent query params leaking into typed session dict Move session name check before `anno is dict` in `_find_p` so that `session: dict` returns the actual session scope instead of the merged data dict (which includes query parameters). Fixes #845 Co-Authored-By: Claude Opus 4.6 (1M context) --- fasthtml/core.py | 1 + nbs/api/00_core.ipynb | 1 + tests/test_toaster.py | 2 ++ 3 files changed, 4 insertions(+) diff --git a/fasthtml/core.py b/fasthtml/core.py index 8f44423b..4bfaea0f 100644 --- a/fasthtml/core.py +++ b/fasthtml/core.py @@ -198,6 +198,7 @@ async def _find_p(conn, data, hdrs, arg:str, p:Parameter): if issubclass(anno, Starlette): return conn.scope['app'] if issubclass(anno, HTTPConnection): return conn if issubclass(anno, State): return conn.scope['app'].state + if 'session'.startswith(arg.lower()) and anno is dict: return conn.scope.get('session', {}) if anno is dict: return data if _is_body(anno): if 'session'.startswith(arg.lower()): return conn.scope.get('session', {}) diff --git a/nbs/api/00_core.ipynb b/nbs/api/00_core.ipynb index 3472b243..34ffec82 100644 --- a/nbs/api/00_core.ipynb +++ b/nbs/api/00_core.ipynb @@ -790,6 +790,7 @@ " if issubclass(anno, Starlette): return conn.scope['app']\n", " if issubclass(anno, HTTPConnection): return conn\n", " if issubclass(anno, State): return conn.scope['app'].state\n", + " if 'session'.startswith(arg.lower()) and anno is dict: return conn.scope.get('session', {})\n", " if anno is dict: return data\n", " if _is_body(anno):\n", " if 'session'.startswith(arg.lower()): return conn.scope.get('session', {})\n", diff --git a/tests/test_toaster.py b/tests/test_toaster.py index 7049fe7d..3ad2b8c2 100644 --- a/tests/test_toaster.py +++ b/tests/test_toaster.py @@ -46,9 +46,11 @@ def test_ft_response(): assert 'Toast FtResponse' in res.text def test_get_toaster_with_typehint(): + cli.get('/set-toast-get', follow_redirects=False) res = cli.get('/see-toast-with-typehint', follow_redirects=False) assert 'Toast get' in res.text + cli.get('/set-toast-get', follow_redirects=False) res = cli.get('/see-toast-with-typehint', follow_redirects=True) assert 'Toast get' in res.text