Skip to content

Commit

Permalink
Return empty when parsing a multi-part POST with only one end delimit…
Browse files Browse the repository at this point in the history
…er. (#2104)

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 committed Aug 3, 2023
1 parent 1939a54 commit da03bfa
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
Expand Up @@ -209,6 +209,7 @@ def initialize(boundary, tempfile, bufsize, query_parser)
@sbuf = StringScanner.new("".dup)
@body_regex = /(?:#{EOL}|\A)--#{Regexp.quote(boundary)}(?:#{EOL}|--)/m
@body_regex_at_end = /#{@body_regex}\z/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 @@ -275,7 +276,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
Expand Up @@ -1363,6 +1363,24 @@ def initialize(*)
req.env['rack.request.form_pairs'].must_equal [["reply", "yes"], ["fileupload", f]]
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 da03bfa

Please sign in to comment.