curl and httpie export change the body when it has % or a trailing newline
Problem Description
The curl and httpie export changes the request body when the body has a control character (for example a newline) together with %, a backslash, or a newline at the end. The exported command sends different bytes than the original request.
The body is passed as "$(printf '...')". printf treats % as a format directive and \n, \t and other backslash sequences as escapes. $(...) also removes newlines at the end of the output.
I ran the exported commands in bash against a local server and compared the bytes:
| body in mitmproxy | exported | server received |
|---|---|---|
{"a":1}\n |
-d "$(printf '{"a":1}\x0a')" |
{"a":1} (newline lost) |
a=100%\nb=2 |
-d "$(printf 'a=100%\x0ab=2')" |
a=100 and printf: '\': invalid format character |
C:\new\temp\nx (two backslashes) |
-d "$(printf 'C:\new\temp\x0ax')" |
C:<LF>ew<TAB>emp<LF>x |
A JSON body that ends with a newline is very common, so the first case happens a lot.
Steps to reproduce the behavior:
- Capture a POST request whose body is
{"a":1}followed by a newline. - Run
export.clip curl @focus(or:export.clip curl @focusin the console) and paste the command into bash. - The server gets
{"a":1}without the newline. With a body likea=100%followed by a newline the server gets onlya=100.
Bodies without control characters are fine, they go through shlex.quote.
A fix is to use ANSI-C quoting ($'...') instead of printf. It expands \\\xNN byte for byte and does not touch %. I tested it in bash and zsh, all bodies above arrive unchanged. I will open a pull request.
System Information
Mitmproxy: 13.0.0.dev (+79, commit 2ac5b08)
Python: 3.14.6
OpenSSL: OpenSSL 3.5.5 27 Jan 2026
Platform: macOS-26.5.2-arm64-arm-64bit-Mach-O
The printf code is unchanged since 11.1.0 (#7520), so the latest release is affected too.
Checklist
- This bug affects the latest mitmproxy release.
Source: mitmproxy/mitmproxy