From 1808bcdf3424eab0c659ef2d0e85579aab977a1a Mon Sep 17 00:00:00 2001 From: namusyaka Date: Wed, 23 Nov 2022 22:24:02 +0900 Subject: [PATCH] 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 Origin: https://github.com/sinatra/sinatra/commit/1808bcdf3424eab0c659ef2d0e85579aab977a1a --- lib/sinatra/base.rb | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index f5d7729..b20a1f7 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -357,16 +357,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. -- 2.47.0