mirror of
https://github.com/discourse/discourse.git
synced 2025-05-22 22:43:33 +08:00
FEATURE: Allow hotlinked media to be blocked (#16940)
This commit introduces a new site setting: `block_hotlinked_media`. When enabled, all attempts to hotlink media (images, videos, and audio) will fail, and be replaced with a linked placeholder. Exceptions to the rule can be added via `block_hotlinked_media_exceptions`. `download_remote_image_to_local` can be used alongside this feature. In that case, hotlinked images will be blocked immediately when the post is created, but will then be replaced with the downloaded version a few seconds later. This implementation is purely server-side, and does not impact the composer preview. Technically, there are two stages to this feature: 1. `PrettyText.sanitize_hotlinked_media` is called during `PrettyText.cook`, and whenever new images are introduced by Onebox. It will iterate over all src/srcset attributes in the post HTML and check if they're allowed. If not, the attributes will be removed and replaced with a `data-blocked-hotlinked-src(set)` attribute 2. In the `CookedPostProcessor`, we iterate over all `data-blocked-hotlinked-src(set)` attributes and check whether we have a downloaded version of the media. If yes, we update the src to use the downloaded version. If not, the entire media element is replaced with a placeholder. The placeholder is labelled 'external media', and is a link to the offsite media.
This commit is contained in:
@ -42,6 +42,7 @@ class CookedPostProcessor
|
||||
remove_full_quote_on_direct_reply if new_post
|
||||
post_process_oneboxes
|
||||
post_process_images
|
||||
add_blocked_hotlinked_media_placeholders
|
||||
post_process_quotes
|
||||
optimize_urls
|
||||
remove_user_ids
|
||||
@ -120,7 +121,7 @@ class CookedPostProcessor
|
||||
|
||||
def extract_images
|
||||
# all images with a src attribute
|
||||
@doc.css("img[src]") -
|
||||
@doc.css("img[src], img[#{PrettyText::BLOCKED_HOTLINKED_SRC_ATTR}]") -
|
||||
# minus data images
|
||||
@doc.css("img[src^='data']") -
|
||||
# minus emojis
|
||||
@ -371,7 +372,7 @@ class CookedPostProcessor
|
||||
|
||||
def process_hotlinked_image(img)
|
||||
@hotlinked_map ||= @post.post_hotlinked_media.preload(:upload).map { |r| [r.url, r] }.to_h
|
||||
normalized_src = PostHotlinkedMedia.normalize_src(img["src"])
|
||||
normalized_src = PostHotlinkedMedia.normalize_src(img["src"] || img[PrettyText::BLOCKED_HOTLINKED_SRC_ATTR])
|
||||
info = @hotlinked_map[normalized_src]
|
||||
|
||||
still_an_image = true
|
||||
@ -384,11 +385,37 @@ class CookedPostProcessor
|
||||
still_an_image = false
|
||||
elsif info&.downloaded? && upload = info&.upload
|
||||
img["src"] = UrlHelper.cook_url(upload.url, secure: @with_secure_media)
|
||||
img.delete(PrettyText::BLOCKED_HOTLINKED_SRC_ATTR)
|
||||
end
|
||||
|
||||
still_an_image
|
||||
end
|
||||
|
||||
def add_blocked_hotlinked_media_placeholders
|
||||
@doc.css([
|
||||
"[#{PrettyText::BLOCKED_HOTLINKED_SRC_ATTR}]",
|
||||
"[#{PrettyText::BLOCKED_HOTLINKED_SRCSET_ATTR}]",
|
||||
].join(',')).each do |el|
|
||||
src = el[PrettyText::BLOCKED_HOTLINKED_SRC_ATTR] ||
|
||||
el[PrettyText::BLOCKED_HOTLINKED_SRCSET_ATTR]&.split(',')&.first&.split(' ')&.first
|
||||
|
||||
if el.name == "img"
|
||||
add_blocked_hotlinked_image_placeholder!(el)
|
||||
next
|
||||
end
|
||||
|
||||
if ["video", "audio"].include?(el.parent.name)
|
||||
el = el.parent
|
||||
end
|
||||
|
||||
if el.parent.classes.include?("video-container")
|
||||
el = el.parent
|
||||
end
|
||||
|
||||
add_blocked_hotlinked_media_placeholder!(el, src)
|
||||
end
|
||||
end
|
||||
|
||||
def is_svg?(img)
|
||||
path =
|
||||
begin
|
||||
|
Reference in New Issue
Block a user