Repository navigation
0.11
Security-relevant release. If you render incoming requests server-side — aiohttp.web or starlette / fastapi — upgrading is worth doing now.
A single quote in any value could execute arbitrary commands
sh output wrapped values in '…' without escaping, so a quote inside a header, cookie, url or body closed the literal and the remainder was parsed by the shell as code. Through the server-side adapters this is reachable from the wire, since the request path, cookies and headers are all chosen by the caller:
# an incoming GET /x;id, rendered inside a handler
curl -b n=O'Brien -H 'host: example.com' http://example.com/x;id
# ^ unterminated quote ^ runs when pasted
A developer copying that out of a log runs id. Every value is quoted now, and the url and cookie header are quoted whenever they hold anything outside the safe character class shlex.quote() uses.
Also fixed
- A url with one query parameter did not run at all. It has no
&, so it was emitted bare, andzsh— the default shell on macOS — rejects the bare?as an unmatched glob:no matches found. - A non-UTF-8 body emitted a Python
repr.-d 'b'\xff\xfe''could never run. It renders as--data-raw $'caf\xe9'now, escaping only the bytes that must be escaped so a mis-encoded text body stays readable.--data-rawrather than-d, because both-dand--data-binaryread a leading@as a filename to load the body from, and@is an ordinary byte in a binary payload. Note$'…'is bash/zsh/ksh ANSI-C quoting and not POSIX —dashleaves it literal. - Raw bytes with
shell="powershell", and any body holding a NUL, now raiseValueErrorinstead of rendering a command that runs and sends the wrong thing. Nothing can be spelled behind the--%token, and a command-line argument is NUL-terminated so no quoting can carry a NUL. - The
Content-Typefallback for a body sent without one istext/plain, not the reversedplain/text.
Packaging
The package shipped without a license from 0.1 through 0.10. It now carries LICENSE (MIT) and trove classifiers.
Full detail in CHANGELOG.md and #13.