From d8b6b4f7b6eb14a578eeb5977dbf57bdf1986834 Mon Sep 17 00:00:00 2001 From: Asad Ali <42726454+dmp990@users.noreply.github.com> Date: Thu, 29 Oct 2026 14:40:42 +0000 Subject: [PATCH 1/2] Fix session:dict injection and 'session'-prefixed body params in _find_p --- fasthtml/core.py | 6 +-- nbs/api/00_core.ipynb | 98 +++++++++++++++++++++++++++---------------- 2 files changed, 64 insertions(+), 40 deletions(-) diff --git a/fasthtml/core.py b/fasthtml/core.py index 09c2acb4..53414dc6 100644 --- a/fasthtml/core.py +++ b/fasthtml/core.py @@ -274,10 +274,8 @@ 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 anno is dict: return data - if _is_body(anno): - if 'session'.startswith(arg.lower()): return conn.scope.get('session', {}) - return await _from_body(conn, p, data) + if anno is dict: return conn.scope.get('session', {}) if 'session'.startswith(arg.lower()) else data + if _is_body(anno): return await _from_body(conn, p, data) if (msg := _check_anno(arg, anno)): return warn(msg) # Special param names with no annotations if anno is empty: diff --git a/nbs/api/00_core.ipynb b/nbs/api/00_core.ipynb index a5ace4a8..60b6acb2 100644 --- a/nbs/api/00_core.ipynb +++ b/nbs/api/00_core.ipynb @@ -182,7 +182,7 @@ { "data": { "text/plain": [ - "datetime.datetime(2026, 3, 30, 14, 0)" + "datetime.datetime(2026, 10, 29, 14, 0)" ] }, "execution_count": null, @@ -842,7 +842,25 @@ "execution_count": null, "id": "28bfbbc1", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Did not find CR at end of boundary (5)\n" + ] + }, + { + "data": { + "text/plain": [ + "'Invalid multipart data.'" + ] + }, + "execution_count": null, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "bad = b'--xyzXX\\r\\nContent-Disposition: form-data; name=\"a\"\\r\\n\\r\\n1\\r\\n--xyz--\\r\\n'\n", "res = client.post('/', content=bad, headers={'content-type':'multipart/form-data; boundary=xyz'})\n", @@ -863,7 +881,18 @@ "execution_count": null, "id": "2aaa8fbe", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "'Invalid JSON body'" + ] + }, + "execution_count": null, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "res = client.post('/', json=[1,2])\n", "test_eq(res.status_code, 400)\n", @@ -912,10 +941,8 @@ " 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 anno is dict: return data\n", - " if _is_body(anno):\n", - " if 'session'.startswith(arg.lower()): return conn.scope.get('session', {})\n", - " return await _from_body(conn, p, data)\n", + " if anno is dict: return conn.scope.get('session', {}) if 'session'.startswith(arg.lower()) else data\n", + " if _is_body(anno): return await _from_body(conn, p, data)\n", " if (msg := _check_anno(arg, anno)): return warn(msg)\n", " # Special param names with no annotations\n", " if anno is empty:\n", @@ -1739,7 +1766,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "morph head-support preload class-tools loading-states multi-swap path-deps remove-me debug ws chunked-transfer\n" + "morph head-support preload class-tools loading-states multi-swap path-deps remove-me debug ws ws4 chunked-transfer sse4 live multipart\n" ] } ], @@ -2920,13 +2947,7 @@ "\n", "\n", "
Text.
\n", - "\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ + "\n", "\n" ] } @@ -2952,13 +2973,7 @@ " \n", "
Text.
\n", " \n", - "\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ + "\n", "\n" ] } @@ -2982,13 +2997,7 @@ "\n", "\n", "
Text.
\n", - "\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ + "\n", "\n" ] } @@ -3035,13 +3044,7 @@ "\n", "\n", "
Text.
\n", - "\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ + "\n", "\n" ] } @@ -3514,6 +3517,29 @@ "test_r(cli, '/bodietd/', '{\"a\":1,\"b\":\"foo\"}', 'post', data=d)" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "4b57a83f", + "metadata": {}, + "outputs": [], + "source": [ + "@app.get(\"/set_sess_dict\")\n", + "def _(session:dict):\n", + " session['foo'] = 'bar'\n", + " return 'ok'\n", + "\n", + "@app.get(\"/sess_dict\")\n", + "def _(session:dict): return session.get('foo')\n", + "\n", + "@app.post(\"/s_bodie\")\n", + "def _(s:Bodie): return asdict(s)\n", + "\n", + "cli.get('/set_sess_dict')\n", + "test_r(cli, '/sess_dict?foo=evil', 'bar')\n", + "test_r(cli, '/s_bodie', '{\"a\":1,\"b\":\"foo\"}', 'post', data=d)" + ] + }, { "cell_type": "code", "execution_count": null, From aabd56add1114ba8e365845eaf9011f4cacc72cf Mon Sep 17 00:00:00 2001 From: Asad Ali <42726454+dmp990@users.noreply.github.com> Date: Thu, 29 Oct 2026 16:26:18 +0000 Subject: [PATCH 2/2] Revert unwanted notebook noise --- nbs/api/00_core.ipynb | 69 ++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 37 deletions(-) diff --git a/nbs/api/00_core.ipynb b/nbs/api/00_core.ipynb index 60b6acb2..8aa79aae 100644 --- a/nbs/api/00_core.ipynb +++ b/nbs/api/00_core.ipynb @@ -182,7 +182,7 @@ { "data": { "text/plain": [ - "datetime.datetime(2026, 10, 29, 14, 0)" + "datetime.datetime(2026, 3, 30, 14, 0)" ] }, "execution_count": null, @@ -842,25 +842,7 @@ "execution_count": null, "id": "28bfbbc1", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Did not find CR at end of boundary (5)\n" - ] - }, - { - "data": { - "text/plain": [ - "'Invalid multipart data.'" - ] - }, - "execution_count": null, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "bad = b'--xyzXX\\r\\nContent-Disposition: form-data; name=\"a\"\\r\\n\\r\\n1\\r\\n--xyz--\\r\\n'\n", "res = client.post('/', content=bad, headers={'content-type':'multipart/form-data; boundary=xyz'})\n", @@ -881,18 +863,7 @@ "execution_count": null, "id": "2aaa8fbe", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'Invalid JSON body'" - ] - }, - "execution_count": null, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "res = client.post('/', json=[1,2])\n", "test_eq(res.status_code, 400)\n", @@ -1766,7 +1737,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "morph head-support preload class-tools loading-states multi-swap path-deps remove-me debug ws ws4 chunked-transfer sse4 live multipart\n" + "morph head-support preload class-tools loading-states multi-swap path-deps remove-me debug ws chunked-transfer\n" ] } ], @@ -2947,7 +2918,13 @@ "\n", "\n", "
Text.
\n", - "\n", + "\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ "\n" ] } @@ -2973,7 +2950,13 @@ " \n", "
Text.
\n", " \n", - "\n", + "\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ "\n" ] } @@ -2997,7 +2980,13 @@ "\n", "\n", "
Text.
\n", - "\n", + "\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ "\n" ] } @@ -3044,7 +3033,13 @@ "\n", "\n", "
Text.
\n", - "\n", + "\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ "\n" ] }