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:
		
							parent
							
								
									c27adec455
								
							
						
					
					
						commit
						051c181ef4
					
				
					 2 changed files with 40 additions and 1 deletions
				
			
		| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue