Skip to content

Commit

Permalink
Backport #2104 to 3-0-stable
Browse files Browse the repository at this point in the history
Return empty when parsing a multi-part POST with only one end delimiter.

Fixed: #2103

Sending the following request in a browser generates a request with
with only one end delimiter.

```javascript
const formData = new FormData();
const request = new Request('http://127.0.0.1:8080/', {
  method: 'POST',
  body: formData,
});
const response = fetch(request);
```

```
curl 'http://127.0.0.1:8080/' \
  -H 'Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryR1LC4tR6ayskIXJm' \
  --data-raw $'------WebKitFormBoundaryR1LC4tR6ayskIXJm--\r\n'
```

This request is not compliant RFC7578, but is generated by major browsers such as
FireFox and Chrome.
Supporting this request will cause the multipart parser to return an empty value.
  • Loading branch information
alpaca-tc authored and jeremyevans committed Mar 20, 2024
1 parent a4bc5e0 commit f856dbd
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 1 deletion.
10 changes: 9 additions & 1 deletion lib/rack/multipart/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ def initialize(boundary, tempfile, bufsize, query_parser)

@sbuf = StringScanner.new("".dup)
@body_regex = /(?:#{EOL}|\A)--#{Regexp.quote(boundary)}(?:#{EOL}|--)/m
@end_boundary_size = boundary.bytesize + 4 # (-- at start, -- at finish)
@rx_max_size = boundary.bytesize + 6 # (\r\n-- at start, either \r\n or -- at finish)
@head_regex = /(.*?#{EOL})#{EOL}/m
end
Expand Down Expand Up @@ -279,7 +280,14 @@ def handle_fast_forward
@state = :MIME_HEAD
return
when :END_BOUNDARY
# invalid multipart upload, but retry for opening boundary
# invalid multipart upload
if @sbuf.pos == @end_boundary_size && @sbuf.rest == EOL
# stop parsing a buffer if a buffer is only an end boundary.
@state = :DONE
return
end

# retry for opening boundary
else
# no boundary found, keep reading data
return :want_read
Expand Down
18 changes: 18 additions & 0 deletions test/spec_request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1333,6 +1333,24 @@ def initialize(*)
f[:tempfile].size.must_equal 76
end

it "parse multipart delimiter-only boundary" do
input = <<EOF
--AaB03x--\r
EOF
mr = Rack::MockRequest.env_for(
"/",
"CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
"CONTENT_LENGTH" => input.size,
:input => input
)

req = make_request mr
req.query_string.must_equal ""
req.GET.must_be :empty?
req.POST.must_be :empty?
req.params.must_equal({})
end

it "MultipartPartLimitError when request has too many multipart file parts if limit set" do
begin
data = 10000.times.map { "--AaB03x\r\ncontent-type: text/plain\r\ncontent-disposition: attachment; name=#{SecureRandom.hex(10)}; filename=#{SecureRandom.hex(10)}\r\n\r\ncontents\r\n" }.join("\r\n")
Expand Down

0 comments on commit f856dbd

Please sign in to comment.