#8425·mitmproxy

curl and httpie export change the body when it has % or a trailing newline

Author: monascoCreated Sep 7, 2026Updated Sep 7, 2026

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:

  1. Capture a POST request whose body is {"a":1} followed by a newline.
  2. Run export.clip curl @focus (or :export.clip curl @focus in the console) and paste the command into bash.
  3. The server gets {"a":1} without the newline. With a body like a=100% followed by a newline the server gets only a=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