handle dangerous extensions
This commit is contained in:
@@ -11,6 +11,7 @@ module Api
|
|||||||
before_action :set_noindex_headers
|
before_action :set_noindex_headers
|
||||||
before_action :set_security_headers
|
before_action :set_security_headers
|
||||||
|
|
||||||
|
# rubocop:disable Metrics
|
||||||
def show
|
def show
|
||||||
blob_uuid, purp, exp = ApplicationRecord.signed_id_verifier.verified(params[:signed_uuid])
|
blob_uuid, purp, exp = ApplicationRecord.signed_id_verifier.verified(params[:signed_uuid])
|
||||||
|
|
||||||
@@ -22,6 +23,12 @@ module Api
|
|||||||
|
|
||||||
blob = ActiveStorage::Blob.find_by!(uuid: blob_uuid)
|
blob = ActiveStorage::Blob.find_by!(uuid: blob_uuid)
|
||||||
|
|
||||||
|
if Submitters::DANGEROUS_EXTENSIONS.include?(blob.filename.extension.to_s.downcase)
|
||||||
|
Rollbar.error('Dangerous extension') if defined?(Rollbar)
|
||||||
|
|
||||||
|
return head :unprocessable_content
|
||||||
|
end
|
||||||
|
|
||||||
attachment = blob.attachments.take
|
attachment = blob.attachments.take
|
||||||
|
|
||||||
@record = attachment.record
|
@record = attachment.record
|
||||||
@@ -46,6 +53,7 @@ module Api
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
# rubocop:enable Metrics
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,12 @@ module Api
|
|||||||
|
|
||||||
return head :not_found unless blob
|
return head :not_found unless blob
|
||||||
|
|
||||||
|
if Submitters::DANGEROUS_EXTENSIONS.include?(blob.filename.extension.to_s.downcase)
|
||||||
|
Rollbar.error('Dangerous extension') if defined?(Rollbar)
|
||||||
|
|
||||||
|
return head :unprocessable_content
|
||||||
|
end
|
||||||
|
|
||||||
is_permitted = blob.attachments.any? do |a|
|
is_permitted = blob.attachments.any? do |a|
|
||||||
(current_user && a.record.account.id == current_user.account_id) ||
|
(current_user && a.record.account.id == current_user.account_id) ||
|
||||||
a.record.account.account_configs.any? { |e| e.key == 'legacy_blob_proxy' } ||
|
a.record.account.account_configs.any? { |e| e.key == 'legacy_blob_proxy' } ||
|
||||||
|
|||||||
@@ -11,6 +11,12 @@ class UserInitialsController < ApplicationController
|
|||||||
|
|
||||||
return redirect_to settings_profile_index_path, notice: I18n.t('unable_to_save_initials') if file.blank?
|
return redirect_to settings_profile_index_path, notice: I18n.t('unable_to_save_initials') if file.blank?
|
||||||
|
|
||||||
|
extension = File.extname(file.original_filename).delete_prefix('.').downcase
|
||||||
|
|
||||||
|
if Submitters::DANGEROUS_EXTENSIONS.include?(extension)
|
||||||
|
raise Submitters::MaliciousFileExtension, "File type '.#{extension}' is not allowed."
|
||||||
|
end
|
||||||
|
|
||||||
blob = ActiveStorage::Blob.create_and_upload!(io: file.open,
|
blob = ActiveStorage::Blob.create_and_upload!(io: file.open,
|
||||||
filename: file.original_filename,
|
filename: file.original_filename,
|
||||||
content_type: file.content_type)
|
content_type: file.content_type)
|
||||||
|
|||||||
@@ -11,6 +11,12 @@ class UserSignaturesController < ApplicationController
|
|||||||
|
|
||||||
return redirect_to settings_profile_index_path, notice: I18n.t('unable_to_save_signature') if file.blank?
|
return redirect_to settings_profile_index_path, notice: I18n.t('unable_to_save_signature') if file.blank?
|
||||||
|
|
||||||
|
extension = File.extname(file.original_filename).delete_prefix('.').downcase
|
||||||
|
|
||||||
|
if Submitters::DANGEROUS_EXTENSIONS.include?(extension)
|
||||||
|
raise Submitters::MaliciousFileExtension, "File type '.#{extension}' is not allowed."
|
||||||
|
end
|
||||||
|
|
||||||
blob = ActiveStorage::Blob.create_and_upload!(io: file.open,
|
blob = ActiveStorage::Blob.create_and_upload!(io: file.open,
|
||||||
filename: file.original_filename,
|
filename: file.original_filename,
|
||||||
content_type: file.content_type)
|
content_type: file.content_type)
|
||||||
|
|||||||
@@ -212,8 +212,8 @@ module Submitters
|
|||||||
elsif type.in?(%w[signature initials]) && value.length < 60
|
elsif type.in?(%w[signature initials]) && value.length < 60
|
||||||
find_or_create_blob_from_text(account, value, type)
|
find_or_create_blob_from_text(account, value, type)
|
||||||
elsif (data = Base64.decode64(value.sub(BASE64_PREFIX_REGEXP, ''))) &&
|
elsif (data = Base64.decode64(value.sub(BASE64_PREFIX_REGEXP, ''))) &&
|
||||||
Marcel::MimeType.for(data).exclude?('octet-stream')
|
(mime_type = Marcel::MimeType.for(data)).exclude?('octet-stream')
|
||||||
find_or_create_blob_from_base64(account, data, type)
|
find_or_create_blob_from_base64(account, data, type, mime_type:)
|
||||||
elsif type == 'image' && (value.starts_with?('<html>') || value.starts_with?('<!DOCTYPE'))
|
elsif type == 'image' && (value.starts_with?('<html>') || value.starts_with?('<!DOCTYPE'))
|
||||||
raise InvalidDefaultValue, "Invalid #{type} value" unless purpose == :api
|
raise InvalidDefaultValue, "Invalid #{type} value" unless purpose == :api
|
||||||
|
|
||||||
@@ -236,15 +236,27 @@ module Submitters
|
|||||||
raise InvalidDefaultValue, "HTML content is not allowed: #{value.first(200)}..."
|
raise InvalidDefaultValue, "HTML content is not allowed: #{value.first(200)}..."
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_or_create_blob_from_base64(account, data, type)
|
def find_or_create_blob_from_base64(account, data, type, mime_type: nil)
|
||||||
checksum = Digest::MD5.base64digest(data)
|
checksum = Digest::MD5.base64digest(data)
|
||||||
|
|
||||||
blob = find_blob_by_checksum(checksum, account)
|
blob = find_blob_by_checksum(checksum, account)
|
||||||
|
|
||||||
blob || ActiveStorage::Blob.create_and_upload!(
|
return blob if blob
|
||||||
io: StringIO.new(data),
|
|
||||||
filename: "#{type}.png"
|
mime_type ||= Marcel::MimeType.for(data)
|
||||||
)
|
|
||||||
|
detected_extensions = Marcel::TYPE_EXTS[mime_type].to_a.map(&:downcase)
|
||||||
|
|
||||||
|
if detected_extensions.any? { |e| Submitters::DANGEROUS_EXTENSIONS.include?(e) }
|
||||||
|
raise InvalidDefaultValue, "File type '.#{detected_extensions.first}' is not allowed."
|
||||||
|
end
|
||||||
|
|
||||||
|
extension = detected_extensions.first
|
||||||
|
extension = 'png' if extension.blank? && type.in?(%w[signature initials stamp image])
|
||||||
|
|
||||||
|
filename = extension.present? ? "#{type}.#{extension}" : type
|
||||||
|
|
||||||
|
ActiveStorage::Blob.create_and_upload!(io: StringIO.new(data), filename:)
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_or_create_blob_from_text(account, text, type)
|
def find_or_create_blob_from_text(account, text, type)
|
||||||
@@ -261,6 +273,13 @@ module Submitters
|
|||||||
end
|
end
|
||||||
|
|
||||||
def find_or_create_blob_from_url(account, url)
|
def find_or_create_blob_from_url(account, url)
|
||||||
|
filename = Addressable::URI.parse(url).path.split('/').last.to_s
|
||||||
|
extension = File.extname(filename).delete_prefix('.').downcase
|
||||||
|
|
||||||
|
if Submitters::DANGEROUS_EXTENSIONS.include?(extension)
|
||||||
|
raise InvalidDefaultValue, "File type '.#{extension}' is not allowed."
|
||||||
|
end
|
||||||
|
|
||||||
cache_key = [account.id, url].join(':')
|
cache_key = [account.id, url].join(':')
|
||||||
checksum = CHECKSUM_CACHE_STORE.fetch(cache_key)
|
checksum = CHECKSUM_CACHE_STORE.fetch(cache_key)
|
||||||
|
|
||||||
@@ -276,10 +295,7 @@ module Submitters
|
|||||||
|
|
||||||
blob = find_blob_by_checksum(checksum, account)
|
blob = find_blob_by_checksum(checksum, account)
|
||||||
|
|
||||||
blob || ActiveStorage::Blob.create_and_upload!(
|
blob || ActiveStorage::Blob.create_and_upload!(io: StringIO.new(data), filename:)
|
||||||
io: StringIO.new(data),
|
|
||||||
filename: Addressable::URI.parse(url).path.split('/').last
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_blob_by_checksum(checksum, account)
|
def find_blob_by_checksum(checksum, account)
|
||||||
|
|||||||
Reference in New Issue
Block a user