Skip to content

Commit

Permalink
backport #2104 (#2121)
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 committed Sep 13, 2023
1 parent 99057e6 commit fdb12cb
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 1 deletion.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

All notable changes to this project will be documented in this file. For info on how to format all future additions to this file please reference [Keep A Changelog](https://keepachangelog.com/en/1.0.0/).

## Unreleased

- Return empty when parsing a multi-part POST with only one end delimiter. ([#2104](https://github.com/rack/rack/pull/2104), [@alpaca-tc])

## [2.2.8] - 2023-07-31

- Regenerate SPEC ([#2102](https://github.com/rack/rack/pull/2102), [@skipkayhil](https://github.com/skipkayhil))
Expand Down
8 changes: 7 additions & 1 deletion lib/rack/multipart/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ def initialize(boundary, tempfile, bufsize, query_parser)

@sbuf = StringScanner.new("".dup)
@body_regex = /(?:#{EOL})?#{Regexp.quote(@boundary)}(?:#{EOL}|--)/m
@end_boundary_size = boundary.bytesize + 6 # (-- at start, -- at finish, EOL at end)
@rx_max_size = EOL.size + @boundary.bytesize + [EOL.size, '--'.size].max
@head_regex = /(.*?#{EOL})#{EOL}/m
end
Expand Down Expand Up @@ -231,7 +232,12 @@ def run_parser
end

def handle_fast_forward
if consume_boundary
tok = consume_boundary

if tok == :END_BOUNDARY && @sbuf.pos == @end_boundary_size && @sbuf.eos?
# stop parsing a buffer if a buffer is only an end boundary.
@state = :DONE
elsif tok
@state = :MIME_HEAD
else
raise EOFError, "bad content body" if @sbuf.rest_size >= @bufsize
Expand Down
18 changes: 18 additions & 0 deletions test/spec_request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1009,6 +1009,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 fdb12cb

Please sign in to comment.