refactor(ssrf): centralize safe fetch allowlist
This commit is contained in:
@@ -28,7 +28,6 @@ class Messages::Messenger::MessageBuilder
|
||||
def attach_file(attachment, file_url)
|
||||
SafeFetch.fetch(
|
||||
file_url,
|
||||
allowed_content_type_prefixes: %w[image/ video/ audio/],
|
||||
allowed_content_types: Attachment::ACCEPTABLE_FILE_TYPES
|
||||
) do |attachment_file|
|
||||
attachment.file.attach(
|
||||
|
||||
@@ -101,7 +101,6 @@ 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,
|
||||
&
|
||||
)
|
||||
|
||||
@@ -150,7 +150,6 @@ class Telegram::IncomingMessageService
|
||||
|
||||
SafeFetch.fetch(
|
||||
file_download_path,
|
||||
allowed_content_type_prefixes: %w[image/ video/ audio/],
|
||||
allowed_content_types: Attachment::ACCEPTABLE_FILE_TYPES
|
||||
) do |attachment_file|
|
||||
build_file_attachment(attachment_file)
|
||||
|
||||
@@ -175,7 +175,6 @@ 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,
|
||||
&
|
||||
)
|
||||
@@ -185,7 +184,6 @@ 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,7 +3,6 @@ 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,7 +20,6 @@ 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,
|
||||
&
|
||||
)
|
||||
|
||||
+20
-2
@@ -2,8 +2,26 @@ require 'ssrf_filter'
|
||||
|
||||
# rubocop:disable Metrics/ModuleLength
|
||||
module SafeFetch
|
||||
DEFAULT_ALLOWED_CONTENT_TYPE_PREFIXES = %w[image/ video/].freeze
|
||||
DEFAULT_ALLOWED_CONTENT_TYPES = [].freeze
|
||||
DEFAULT_ALLOWED_CONTENT_TYPE_PREFIXES = %w[image/ video/ audio/].freeze
|
||||
DEFAULT_ALLOWED_CONTENT_TYPES = %w[
|
||||
text/csv
|
||||
text/plain
|
||||
text/rtf
|
||||
application/json
|
||||
application/pdf
|
||||
application/zip
|
||||
application/x-7z-compressed
|
||||
application/vnd.rar
|
||||
application/x-tar
|
||||
application/msword
|
||||
application/vnd.ms-excel
|
||||
application/vnd.ms-powerpoint
|
||||
application/rtf
|
||||
application/vnd.oasis.opendocument.text
|
||||
application/vnd.openxmlformats-officedocument.presentationml.presentation
|
||||
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
||||
application/vnd.openxmlformats-officedocument.wordprocessingml.document
|
||||
].freeze
|
||||
DEFAULT_SENSITIVE_HEADERS = %w[authorization cookie].freeze
|
||||
DEFAULT_OPEN_TIMEOUT = 2
|
||||
DEFAULT_READ_TIMEOUT = 20
|
||||
|
||||
@@ -195,6 +195,46 @@ RSpec.describe SafeFetch do
|
||||
expect { described_class.fetch(url) { nil } }.not_to raise_error
|
||||
end
|
||||
|
||||
it 'allows audio/mpeg responses' do
|
||||
stub_request(:get, url).to_return(
|
||||
status: 200,
|
||||
body: File.new(Rails.root.join('spec/assets/sample.mp3')),
|
||||
headers: { 'Content-Type' => 'audio/mpeg' }
|
||||
)
|
||||
|
||||
expect { described_class.fetch(url) { nil } }.not_to raise_error
|
||||
end
|
||||
|
||||
it 'allows application/pdf responses' do
|
||||
stub_request(:get, url).to_return(
|
||||
status: 200,
|
||||
body: File.new(Rails.root.join('spec/assets/sample.pdf')),
|
||||
headers: { 'Content-Type' => 'application/pdf' }
|
||||
)
|
||||
|
||||
expect { described_class.fetch(url) { nil } }.not_to raise_error
|
||||
end
|
||||
|
||||
it 'allows text/plain responses' do
|
||||
stub_request(:get, url).to_return(
|
||||
status: 200,
|
||||
body: 'plain-text',
|
||||
headers: { 'Content-Type' => 'text/plain' }
|
||||
)
|
||||
|
||||
expect { described_class.fetch(url) { nil } }.not_to raise_error
|
||||
end
|
||||
|
||||
it 'allows text/csv responses' do
|
||||
stub_request(:get, url).to_return(
|
||||
status: 200,
|
||||
body: "id,name\n1,Chatwoot\n",
|
||||
headers: { 'Content-Type' => 'text/csv' }
|
||||
)
|
||||
|
||||
expect { described_class.fetch(url) { nil } }.not_to raise_error
|
||||
end
|
||||
|
||||
it 'strips charset/boundary parameters before comparing' do
|
||||
stub_request(:get, url).to_return(
|
||||
status: 200,
|
||||
|
||||
Reference in New Issue
Block a user