asgi-csrf icon indicating copy to clipboard operation
asgi-csrf copied to clipboard

IndexError thrown on form posts

Open scott2b opened this issue 2 years ago • 1 comments

I'm not quite sure how this part of the code works, so I don't know what I might be doing wrong: https://github.com/simonw/asgi-csrf/blob/f1a7e2889b5e7df7d67dbb7d6f483c22dbffae83/asgi_csrf.py#L210

I am using asgi-csrf with a Starlette app, and rendering out the token as a hidden field in forms. In general, csrf checks appear to be passing, however the middleware is throwing an IndexError due to a pop from empty list. The full stack trace is below.

For what it's worth: the form does get submitted and my view is able to process the form as it normally would. So, these exceptions are happening, but it's not clear to me what the real side effect is, or if it might be possible that a proper csrf check is not really happening.

Perhaps there is something obvious I am doing wrong?

console_1        | ERROR:uvicorn.error:Exception in ASGI application
console_1        | Traceback (most recent call last):
console_1        |   File "/usr/local/lib/python3.10/site-packages/uvicorn/protocols/http/h11_impl.py", line 366, in run_asgi
console_1        |     result = await app(self.scope, self.receive, self.send)
console_1        |   File "/usr/local/lib/python3.10/site-packages/uvicorn/middleware/proxy_headers.py", line 75, in __call__
console_1        |     return await self.app(scope, receive, send)
console_1        |   File "/usr/local/lib/python3.10/site-packages/asgi_csrf.py", line 143, in app_wrapped_with_csrf
console_1        |     await app(scope, replay_receive, wrapped_send)
console_1        |   File "/usr/local/lib/python3.10/site-packages/starlette/applications.py", line 112, in __call__
console_1        |     await self.middleware_stack(scope, receive, send)
console_1        |   File "/usr/local/lib/python3.10/site-packages/starlette/middleware/errors.py", line 181, in __call__
console_1        |     raise exc from None
console_1        |   File "/usr/local/lib/python3.10/site-packages/starlette/middleware/errors.py", line 159, in __call__
console_1        |     await self.app(scope, receive, _send)
console_1        |   File "/usr/local/lib/python3.10/site-packages/starlette/middleware/sessions.py", line 75, in __call__
console_1        |     await self.app(scope, receive, send_wrapper)
console_1        |   File "/usr/local/lib/python3.10/site-packages/starlette/middleware/authentication.py", line 48, in __call__
console_1        |     await self.app(scope, receive, send)
console_1        |   File "/usr/local/lib/python3.10/site-packages/starlette/middleware/base.py", line 26, in __call__
console_1        |     await response(scope, receive, send)
console_1        |   File "/usr/local/lib/python3.10/site-packages/starlette/responses.py", line 224, in __call__
console_1        |     await run_until_first_complete(
console_1        |   File "/usr/local/lib/python3.10/site-packages/starlette/concurrency.py", line 24, in run_until_first_complete
console_1        |     [task.result() for task in done]
console_1        |   File "/usr/local/lib/python3.10/site-packages/starlette/concurrency.py", line 24, in <listcomp>
console_1        |     [task.result() for task in done]
console_1        |   File "/usr/local/lib/python3.10/site-packages/starlette/responses.py", line 204, in listen_for_disconnect
console_1        |     message = await receive()
console_1        |   File "/usr/local/lib/python3.10/site-packages/asgi_csrf.py", line 211, in replay_receive
console_1        |     return messages.pop(0)
console_1        | IndexError: pop from empty list

scott2b avatar Jul 13 '22 14:07 scott2b

I see now that my forms are being submitted as x-www-form-urlencoded rather than as multipart/form-data. I'm not sure why that is this case, I'm guessing this is the browser's call? I'm not sure.

In any case, if I modify the code at line 211, to do the same kind of check that is done in _parse_multipart_form_data on line 249, then it all seems to work:

        if messages:
            return messages.pop(0)
        else:
            return await receive()

scott2b avatar Jul 13 '22 16:07 scott2b