mirror of
https://github.com/discourse/discourse.git
synced 2024-11-22 11:23:25 +08:00
e8efdd60d4
Further on from my earlier PR #8973 also reject upload as secure if its origin URL contains images/emoji. We still check Emoji.all first to try and be canonical. This may be a little heavy handed (e.g. if an external URL followed this same path it would be a false positive), but there are a lot of emoji aliases where the actual Emoji url is something, but you can have another image that should not be secure that that thing is an alias for. For example slight_smile.png does not show up in Emoji.all BUT slightly_smiling_face does, and it aliases slight_smile e.g. /images/emoji/twitter/slight_smile.png?v=9 and /images/emoji/twitter/slightly_smiling_face.png?v=9 are equivalent.
85 lines
2.9 KiB
Ruby
85 lines
2.9 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
##
|
|
# A note on determining whether an upload should be marked as secure:
|
|
#
|
|
# Some of these flags checked (e.g. all of the for_X flags and the opts[:type])
|
|
# are only set when _initially uploading_ via UploadCreator and are not present
|
|
# when an upload already exists.
|
|
#
|
|
# If the upload already exists the best way to figure out whether it should be
|
|
# secure alongside the site settings is the access_control_post_id, because the
|
|
# original post the upload is linked to has far more bearing on its security context
|
|
# post-upload. If the access_control_post_id does not exist then we just rely
|
|
# on the current secure? status, otherwise there would be a lot of additional
|
|
# complex queries and joins to perform.
|
|
class UploadSecurity
|
|
def initialize(upload, opts = {})
|
|
@upload = upload
|
|
@opts = opts
|
|
@upload_type = @opts[:type]
|
|
end
|
|
|
|
def should_be_secure?
|
|
return false if uploading_in_public_context?
|
|
secure_attachment? || secure_media?
|
|
end
|
|
|
|
private
|
|
|
|
def uploading_in_public_context?
|
|
@upload.for_theme || @upload.for_site_setting || @upload.for_gravatar || public_type? || used_for_custom_emoji? || based_on_regular_emoji?
|
|
end
|
|
|
|
def supported_media?
|
|
FileHelper.is_supported_media?(@upload.original_filename)
|
|
end
|
|
|
|
def secure_attachment?
|
|
!supported_media? && SiteSetting.prevent_anons_from_downloading_files
|
|
end
|
|
|
|
def secure_media?
|
|
SiteSetting.secure_media? && supported_media? && uploading_in_secure_context?
|
|
end
|
|
|
|
def uploading_in_secure_context?
|
|
return true if SiteSetting.login_required?
|
|
if @upload.access_control_post_id.present?
|
|
return access_control_post_has_secure_media?
|
|
end
|
|
uploading_in_composer? || @upload.for_private_message || @upload.for_group_message || @upload.secure?
|
|
end
|
|
|
|
# whether the upload should remain secure or not after posting depends on its context,
|
|
# which is based on the post it is linked to via access_control_post_id.
|
|
# if that post is with_secure_media? then the upload should also be secure.
|
|
# this may change to false if the upload was set to secure on upload e.g. in
|
|
# a post composer then it turned out that the post itself was not in a secure context
|
|
#
|
|
# if there is no access control post id and the upload is currently secure, we
|
|
# do not want to make it un-secure to avoid unintentionally exposing it
|
|
def access_control_post_has_secure_media?
|
|
@upload.access_control_post.with_secure_media?
|
|
end
|
|
|
|
def public_type?
|
|
%w[avatar custom_emoji profile_background card_background].include?(@upload_type)
|
|
end
|
|
|
|
def uploading_in_composer?
|
|
@upload_type == "composer"
|
|
end
|
|
|
|
def used_for_custom_emoji?
|
|
@upload.id.present? && CustomEmoji.exists?(upload_id: @upload.id)
|
|
end
|
|
|
|
def based_on_regular_emoji?
|
|
return false if @upload.origin.blank?
|
|
uri = URI.parse(@upload.origin)
|
|
return true if Emoji.all.map(&:url).include?("#{uri.path}?#{uri.query}")
|
|
uri.path.include?("images/emoji")
|
|
end
|
|
end
|