fix(ssrf): allow provider audio downloads

This commit is contained in:
Sony Mathew
2026-04-23 12:23:58 +05:30
parent 0506c477eb
commit 9c6b20f11e
8 changed files with 76 additions and 0 deletions
@@ -101,6 +101,7 @@ class Sms::IncomingMessageService
SafeFetch.fetch(
media_url,
http_basic_authentication: [channel.provider_config['api_key'], channel.provider_config['api_secret']],
allowed_content_type_prefixes: %w[image/ video/ audio/],
allowed_content_types: Attachment::ACCEPTABLE_FILE_TYPES,
&
)
@@ -175,6 +175,7 @@ class Twilio::IncomingMessageService
SafeFetch.fetch(
media_url,
http_basic_authentication: auth_credentials,
allowed_content_type_prefixes: %w[image/ video/ audio/],
allowed_content_types: Attachment::ACCEPTABLE_FILE_TYPES,
&
)
@@ -184,6 +185,7 @@ class Twilio::IncomingMessageService
Rails.logger.info "Error downloading attachment from Twilio: #{error.message}: Retrying without auth"
SafeFetch.fetch(
media_url,
allowed_content_type_prefixes: %w[image/ video/ audio/],
allowed_content_types: Attachment::ACCEPTABLE_FILE_TYPES,
&
)
@@ -3,6 +3,7 @@ module Whatsapp::IncomingMessageServiceHelpers
SafeFetch.fetch(
inbox.channel.media_url(attachment_payload[:id]),
headers: inbox.channel.api_headers,
allowed_content_type_prefixes: %w[image/ video/ audio/],
allowed_content_types: Attachment::ACCEPTABLE_FILE_TYPES,
&
)
@@ -20,6 +20,7 @@ class Whatsapp::IncomingMessageWhatsappCloudService < Whatsapp::IncomingMessageB
SafeFetch.fetch(
url_response.parsed_response['url'],
headers: inbox.channel.api_headers,
allowed_content_type_prefixes: %w[image/ video/ audio/],
allowed_content_types: Attachment::ACCEPTABLE_FILE_TYPES,
&
)
@@ -100,6 +100,18 @@ describe Sms::IncomingMessageService do
expect(sms_channel.inbox.messages.first.attachments.present?).to be true
end
it 'creates audio attachment messages' do
stub_request(:get, 'http://test.com/test.mp3')
.to_return(status: 200, body: File.read('spec/assets/sample.mp3'), headers: { 'Content-Type' => 'audio/mpeg' })
media_params = { 'media': ['http://test.com/test.mp3'] }.with_indifferent_access
described_class.new(inbox: sms_channel.inbox, params: params.merge(media_params)).perform
attachment = sms_channel.inbox.messages.first.attachments.first
expect(attachment.file_type).to eq('audio')
end
it 'skips blocked attachment URLs' do
media_params = { 'media': ['http://127.0.0.1/blocked.png'] }.with_indifferent_access
@@ -179,6 +179,8 @@ describe Twilio::IncomingMessageService do
before do
stub_request(:get, 'https://chatwoot-assets.local/sample.png')
.to_return(status: 200, body: 'image data', headers: { 'Content-Type' => 'image/png' })
stub_request(:get, 'https://chatwoot-assets.local/sample.mp3')
.to_return(status: 200, body: File.read('spec/assets/sample.mp3'), headers: { 'Content-Type' => 'audio/mpeg' })
end
let(:params_with_attachment) do
@@ -207,6 +209,18 @@ describe Twilio::IncomingMessageService do
expect(WebMock).to(have_requested(:get, 'https://chatwoot-assets.local/sample.png')
.with { |request| request.headers['Authorization']&.start_with?('Basic ') })
end
it 'creates an audio attachment when the provider serves audio media' do
params_with_audio_attachment = params_with_attachment.merge(
MediaContentType0: 'audio/mpeg',
MediaUrl0: 'https://chatwoot-assets.local/sample.mp3'
)
described_class.new(params: params_with_audio_attachment).perform
attachment = conversation.reload.messages.last.attachments.first
expect(attachment.file_type).to eq('audio')
end
end
context 'when there is an error downloading the attachment' do
@@ -234,6 +234,27 @@ describe Whatsapp::IncomingMessageService do
expect(whatsapp_channel.inbox.messages.first.attachments.present?).to be true
end
it 'creates audio attachments when the provider serves audio media' do
stub_request(:get, whatsapp_channel.media_url('b1c68f38-8734-4ad3-b4a1-ef0c10d683')).to_return(
status: 200,
body: File.read('spec/assets/sample.mp3'),
headers: { 'Content-Type' => 'audio/mpeg' }
)
params = {
'contacts' => [{ 'profile' => { 'name' => 'Sojan Jose' }, 'wa_id' => '2423423243' }],
'messages' => [{ 'from' => '2423423243', 'id' => 'SDFADSf23sfasdafasdfa',
'audio' => { 'id' => 'b1c68f38-8734-4ad3-b4a1-ef0c10d683',
'mime_type' => 'audio/mpeg',
'sha256' => '29ed500fa64eb55fc19dc4124acb300e5dcca0f822a301ae99944db' },
'timestamp' => '1633034394', 'type' => 'audio' }]
}.with_indifferent_access
described_class.new(inbox: whatsapp_channel.inbox, params: params).perform
attachment = whatsapp_channel.inbox.messages.first.attachments.first
expect(attachment.file_type).to eq('audio')
end
it 'skips blocked attachment URLs' do
allow(whatsapp_channel).to receive(:media_url).and_return('http://127.0.0.1/blocked.png')
params = {
@@ -47,6 +47,30 @@ describe Whatsapp::IncomingMessageWhatsappCloudService do
expect_message_has_attachment
end
it 'creates audio attachments when the provider serves audio media' do
params[:entry][0][:changes][0][:value][:messages][0] = {
from: '2423423243',
audio: {
id: 'b1c68f38-8734-4ad3-b4a1-ef0c10d683',
mime_type: 'audio/mpeg',
sha256: '29ed500fa64eb55fc19dc4124acb300e5dcca0f822a301ae99944db'
},
timestamp: '1664799904',
type: 'audio'
}
stub_media_url_request(url: 'https://chatwoot-assets.local/sample.mp3')
stub_request(:get, 'https://chatwoot-assets.local/sample.mp3').to_return(
status: 200,
body: File.read('spec/assets/sample.mp3'),
headers: { 'Content-Type' => 'audio/mpeg' }
)
described_class.new(inbox: whatsapp_channel.inbox, params: params).perform
attachment = whatsapp_channel.inbox.messages.first.attachments.first
expect(attachment.file_type).to eq('audio')
end
it 'sends the provider headers when downloading the resolved media URL' do
stub_media_url_request
stub_sample_png_request