1
0
Fork 0
mirror of https://github.com/jnunemaker/httparty synced 2023-03-27 23:23:07 -04:00

escape filename in the multipart/form-data Content-Disposition header

This commit is contained in:
motoyasu-saburi 2022-11-27 14:04:19 +09:00
parent c27adec455
commit 051c181ef4
2 changed files with 40 additions and 1 deletions

View file

@ -32,6 +32,13 @@ module HTTParty
private
# https://html.spec.whatwg.org/#multipart-form-data
MULTIPART_FORM_DATA_REPLACEMENT_TABLE = {
'"' => '%22',
"\r" => '%0D',
"\n" => '%0A'
}.freeze
def generate_multipart
normalized_params = params.flat_map { |key, value| HashConversions.normalize_keys(key, value) }
@ -40,7 +47,7 @@ module HTTParty
memo << %(Content-Disposition: form-data; name="#{key}")
# value.path is used to support ActionDispatch::Http::UploadedFile
# https://github.com/jnunemaker/httparty/pull/585
memo << %(; filename="#{file_name(value)}") if file?(value)
memo << %(; filename="#{file_name(value).gsub(/["\r\n]/, MULTIPART_FORM_DATA_REPLACEMENT_TABLE)}") if file?(value)
memo << NEWLINE
memo << "Content-Type: #{content_type(value)}#{NEWLINE}" if file?(value)
memo << NEWLINE

View file

@ -105,6 +105,38 @@ RSpec.describe HTTParty::Request::Body do
it { is_expected.to eq multipart_params }
end
context 'when file name contains [ " \r \n ]' do
let(:options) { { force_multipart: true } }
let(:some_temp_file) { Tempfile.new(['basefile', '.txt']) }
let(:file_content) { 'test' }
let(:raw_filename) { "dummy=tampering.sh\"; \r\ndummy=a.txt" }
let(:expected_file_name) { 'dummy=tampering.sh%22; %0D%0Adummy=a.txt' }
let(:file) { double(:mocked_action_dispatch, path: some_temp_file.path, original_filename: raw_filename, read: file_content) }
let(:params) do
{
user: {
attachment_file: file,
enabled: true
}
}
end
let(:multipart_params) do
"--------------------------c772861a5109d5ef\r\n" \
"Content-Disposition: form-data; name=\"user[attachment_file]\"; filename=\"#{expected_file_name}\"\r\n" \
"Content-Type: text/plain\r\n" \
"\r\n" \
"test\r\n" \
"--------------------------c772861a5109d5ef\r\n" \
"Content-Disposition: form-data; name=\"user[enabled]\"\r\n" \
"\r\n" \
"true\r\n" \
"--------------------------c772861a5109d5ef--\r\n"
end
it { is_expected.to eq multipart_params }
end
end
end
end