Skip to content

0.11

Choose a tag to compare

@shpaker shpaker released this 17 Aug 14:28
· 36 commits to main since this release
e166cb2

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, and zsh — 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-raw rather than -d, because both -d and --data-binary read 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 — dash leaves it literal.
  • Raw bytes with shell="powershell", and any body holding a NUL, now raise ValueError instead 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-Type fallback for a body sent without one is text/plain, not the reversed plain/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.