Summary
The ASGI adapter parses the query string with k, v = kv.split("=", 1), which raises ValueError when a parameter has no = (e.g. ?debug). The exception is uncaught inside the adapter, so the request dies before the handler runs. Query and form values are also never percent-decoded.
Where
src/htealeaf/server/adapter/asgi.py:75-79:
args_kv = scope["query_string"].decode().split("&") if scope["query_string"] else []
args = {}
for kv in args_kv:
k, v = kv.split("=", 1) # ValueError on a valueless param
args[k] = v
src/htealeaf/server/http/request.py:87 form() — same non-decoding split pattern.
Reproduction
curl "http://127.0.0.1:8000/?debug" # -> 500 / dropped request
curl "http://127.0.0.1:8000/?q=a%20b" # -> value stays "a%20b", not "a b"
Suggested fix
Reuse urllib.parse.parse_qsl (already used by the CGI adapter), which handles valueless params and percent-decoding:
from urllib.parse import parse_qsl
args = dict(parse_qsl(scope["query_string"].decode(), keep_blank_values=True))
Why it matters
A valueless query param is common and lets any client trigger a 500 on the ASGI adapter; missing percent-decoding silently corrupts input. The CGI adapter already does this correctly — the ASGI/form() paths should match. Found during a security review of develop.
Summary
The ASGI adapter parses the query string with
k, v = kv.split("=", 1), which raisesValueErrorwhen a parameter has no=(e.g.?debug). The exception is uncaught inside the adapter, so the request dies before the handler runs. Query and form values are also never percent-decoded.Where
src/htealeaf/server/adapter/asgi.py:75-79:src/htealeaf/server/http/request.py:87form()— same non-decoding split pattern.Reproduction
Suggested fix
Reuse
urllib.parse.parse_qsl(already used by the CGI adapter), which handles valueless params and percent-decoding:Why it matters
A valueless query param is common and lets any client trigger a 500 on the ASGI adapter; missing percent-decoding silently corrupts input. The CGI adapter already does this correctly — the ASGI/
form()paths should match. Found during a security review ofdevelop.