Skip to content

fix: pull files with non-ASCII characters in the path - #2871

Open
tonyandrewmeyer wants to merge 4 commits into
canonical:mainfrom
tonyandrewmeyer:fix/pull-non-ascii-path
Open

tonyandrewmeyer wants to merge 4 commits into
canonical:mainfrom
tonyandrewmeyer:fix/pull-non-ascii-path

Conversation

@tonyandrewmeyer

@tonyandrewmeyer tonyandrewmeyer commented Nov 9, 2026 •

Copy link
Copy Markdown
Collaborator

pull() of a path containing non-ASCII characters failed with "path not expected", because the multipart headers were parsed as bytes and the email parser replaces anything outside ASCII in them. The header bytes are now decoded as UTF-8 first.

Pebble's api_files.go uses Go's CreateFormFile, which writes the raw UTF-8 path into the filename parameter.

The multipart headers were parsed as bytes, and the email bytes parser replaces anything outside ASCII in a header value, so the filename no longer matched and pull() failed with "path not expected". Pebble writes the raw UTF-8 path, so the header bytes are now decoded as UTF-8 first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread ops/pebble.py
# The bytes parser replaces anything outside ASCII in a header value,
# which loses a non-ASCII path. Pebble sends the headers as UTF-8.
parser = email.parser.FeedParser()
parser.feed(data.decode('utf-8', 'surrogateescape'))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with the intent here, but I just want to make sure that this will still allow arbitrary binary bytes in the file payload part of the HTTP response.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, only the part headers go through the decode: _MultipartParser splits each part at the first \r\n\r\n, hands the header block to _process_header, and passes the body bytes to _process_body untouched, which writes them straight to the tempfile. I've extended test_pull_binary with some bytes that aren't valid UTF-8 (\xff\xfe\x00\x80) to ensure it stays that way.

tonyandrewmeyer and others added 2 commits November 10, 2026 14:50
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Message.get_filename() strips whitespace from both ends of the filename, so
pulling a path that ends in a space or tab got a name that didn't match the
requested path, and raised ProtocolError('path not expected'). Read the
filename parameter directly instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants