escape filename in the Content-Disposition header

According the multipart form data spec in WHATWG living standard.
Ref: https://html.spec.whatwg.org/#multipart-form-data
This commit is contained in:
namusyaka 2022-11-23 22:24:02 +09:00 committed by Jordan Owens
parent ee12b185f6
commit 1808bcdf34
2 changed files with 25 additions and 6 deletions

View File

@ -381,16 +381,23 @@ module Sinatra
response['Content-Type'] = mime_type
end
# https://html.spec.whatwg.org/#multipart-form-data
MULTIPART_FORM_DATA_REPLACEMENT_TABLE = {
'"' => '%22',
"\r" => '%0D',
"\n" => '%0A'
}.freeze
# Set the Content-Disposition to "attachment" with the specified filename,
# instructing the user agents to prompt to save.
def attachment(filename = nil, disposition = :attachment)
response['Content-Disposition'] = disposition.to_s.dup
if filename
params = '; filename="%s"' % File.basename(filename)
response['Content-Disposition'] << params
ext = File.extname(filename)
content_type(ext) unless response['Content-Type'] or ext.empty?
end
return unless filename
params = format('; filename="%s"', File.basename(filename).gsub(/["\r\n]/, MULTIPART_FORM_DATA_REPLACEMENT_TABLE))
response['Content-Disposition'] << params
ext = File.extname(filename)
content_type(ext) unless response['Content-Type'] || ext.empty?
end
# Use the contents of the file at +path+ as the response body.

View File

@ -781,6 +781,18 @@ class HelpersTest < Minitest::Test
assert_equal '<sinatra></sinatra>', body
end
it 'escapes filename in the Content-Disposition header according to the multipart form data spec in WHATWG living standard' do
mock_app do
get('/attachment') do
attachment "test.xml\";\r\next=.txt"
response.write("<sinatra></sinatra>")
end
end
get '/attachment'
assert_equal 'attachment; filename="test.xml%22;%0D%0Aext=.txt"', response['Content-Disposition']
assert_equal '<sinatra></sinatra>', body
end
end
describe 'send_file' do