|
|
|
@ -3,6 +3,25 @@ |
|
|
|
|
# SPDX-License-Identifier: AGPL-3.0-only |
|
|
|
|
|
|
|
|
|
defmodule Pleroma.HTML do |
|
|
|
|
# Scrubbers are compiled on boot so they can be configured in OTP releases |
|
|
|
|
# @on_load :compile_scrubbers |
|
|
|
|
|
|
|
|
|
def compile_scrubbers do |
|
|
|
|
dir = Path.join(:code.priv_dir(:pleroma), "scrubbers") |
|
|
|
|
|
|
|
|
|
dir |
|
|
|
|
|> File.ls!() |
|
|
|
|
|> Enum.map(&Path.join(dir, &1)) |
|
|
|
|
|> Kernel.ParallelCompiler.compile() |
|
|
|
|
|> case do |
|
|
|
|
{:error, _errors, _warnings} -> |
|
|
|
|
raise "Compiling scrubbers failed" |
|
|
|
|
|
|
|
|
|
{:ok, _modules, _warnings} -> |
|
|
|
|
:ok |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
defp get_scrubbers(scrubber) when is_atom(scrubber), do: [scrubber] |
|
|
|
|
defp get_scrubbers(scrubbers) when is_list(scrubbers), do: scrubbers |
|
|
|
|
defp get_scrubbers(_), do: [Pleroma.HTML.Scrubber.Default] |
|
|
|
@ -99,216 +118,3 @@ defmodule Pleroma.HTML do |
|
|
|
|
end) |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
defmodule Pleroma.HTML.Scrubber.TwitterText do |
|
|
|
|
@moduledoc """ |
|
|
|
|
An HTML scrubbing policy which limits to twitter-style text. Only |
|
|
|
|
paragraphs, breaks and links are allowed through the filter. |
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
@valid_schemes Pleroma.Config.get([:uri_schemes, :valid_schemes], []) |
|
|
|
|
|
|
|
|
|
require FastSanitize.Sanitizer.Meta |
|
|
|
|
alias FastSanitize.Sanitizer.Meta |
|
|
|
|
|
|
|
|
|
Meta.strip_comments() |
|
|
|
|
|
|
|
|
|
# links |
|
|
|
|
Meta.allow_tag_with_uri_attributes(:a, ["href", "data-user", "data-tag"], @valid_schemes) |
|
|
|
|
|
|
|
|
|
Meta.allow_tag_with_this_attribute_values(:a, "class", [ |
|
|
|
|
"hashtag", |
|
|
|
|
"u-url", |
|
|
|
|
"mention", |
|
|
|
|
"u-url mention", |
|
|
|
|
"mention u-url" |
|
|
|
|
]) |
|
|
|
|
|
|
|
|
|
Meta.allow_tag_with_this_attribute_values(:a, "rel", [ |
|
|
|
|
"tag", |
|
|
|
|
"nofollow", |
|
|
|
|
"noopener", |
|
|
|
|
"noreferrer" |
|
|
|
|
]) |
|
|
|
|
|
|
|
|
|
Meta.allow_tag_with_these_attributes(:a, ["name", "title"]) |
|
|
|
|
|
|
|
|
|
# paragraphs and linebreaks |
|
|
|
|
Meta.allow_tag_with_these_attributes(:br, []) |
|
|
|
|
Meta.allow_tag_with_these_attributes(:p, []) |
|
|
|
|
|
|
|
|
|
# microformats |
|
|
|
|
Meta.allow_tag_with_this_attribute_values(:span, "class", ["h-card"]) |
|
|
|
|
Meta.allow_tag_with_these_attributes(:span, []) |
|
|
|
|
|
|
|
|
|
# allow inline images for custom emoji |
|
|
|
|
if Pleroma.Config.get([:markup, :allow_inline_images]) do |
|
|
|
|
# restrict img tags to http/https only, because of MediaProxy. |
|
|
|
|
Meta.allow_tag_with_uri_attributes(:img, ["src"], ["http", "https"]) |
|
|
|
|
|
|
|
|
|
Meta.allow_tag_with_these_attributes(:img, [ |
|
|
|
|
"width", |
|
|
|
|
"height", |
|
|
|
|
"class", |
|
|
|
|
"title", |
|
|
|
|
"alt" |
|
|
|
|
]) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
Meta.strip_everything_not_covered() |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
defmodule Pleroma.HTML.Scrubber.Default do |
|
|
|
|
@doc "The default HTML scrubbing policy: no " |
|
|
|
|
|
|
|
|
|
require FastSanitize.Sanitizer.Meta |
|
|
|
|
alias FastSanitize.Sanitizer.Meta |
|
|
|
|
|
|
|
|
|
# credo:disable-for-previous-line |
|
|
|
|
# No idea how to fix this one… |
|
|
|
|
|
|
|
|
|
@valid_schemes Pleroma.Config.get([:uri_schemes, :valid_schemes], []) |
|
|
|
|
|
|
|
|
|
Meta.strip_comments() |
|
|
|
|
|
|
|
|
|
Meta.allow_tag_with_uri_attributes(:a, ["href", "data-user", "data-tag"], @valid_schemes) |
|
|
|
|
|
|
|
|
|
Meta.allow_tag_with_this_attribute_values(:a, "class", [ |
|
|
|
|
"hashtag", |
|
|
|
|
"u-url", |
|
|
|
|
"mention", |
|
|
|
|
"u-url mention", |
|
|
|
|
"mention u-url" |
|
|
|
|
]) |
|
|
|
|
|
|
|
|
|
Meta.allow_tag_with_this_attribute_values(:a, "rel", [ |
|
|
|
|
"tag", |
|
|
|
|
"nofollow", |
|
|
|
|
"noopener", |
|
|
|
|
"noreferrer", |
|
|
|
|
"ugc" |
|
|
|
|
]) |
|
|
|
|
|
|
|
|
|
Meta.allow_tag_with_these_attributes(:a, ["name", "title"]) |
|
|
|
|
|
|
|
|
|
Meta.allow_tag_with_these_attributes(:abbr, ["title"]) |
|
|
|
|
|
|
|
|
|
Meta.allow_tag_with_these_attributes(:b, []) |
|
|
|
|
Meta.allow_tag_with_these_attributes(:blockquote, []) |
|
|
|
|
Meta.allow_tag_with_these_attributes(:br, []) |
|
|
|
|
Meta.allow_tag_with_these_attributes(:code, []) |
|
|
|
|
Meta.allow_tag_with_these_attributes(:del, []) |
|
|
|
|
Meta.allow_tag_with_these_attributes(:em, []) |
|
|
|
|
Meta.allow_tag_with_these_attributes(:i, []) |
|
|
|
|
Meta.allow_tag_with_these_attributes(:li, []) |
|
|
|
|
Meta.allow_tag_with_these_attributes(:ol, []) |
|
|
|
|
Meta.allow_tag_with_these_attributes(:p, []) |
|
|
|
|
Meta.allow_tag_with_these_attributes(:pre, []) |
|
|
|
|
Meta.allow_tag_with_these_attributes(:strong, []) |
|
|
|
|
Meta.allow_tag_with_these_attributes(:sub, []) |
|
|
|
|
Meta.allow_tag_with_these_attributes(:sup, []) |
|
|
|
|
Meta.allow_tag_with_these_attributes(:u, []) |
|
|
|
|
Meta.allow_tag_with_these_attributes(:ul, []) |
|
|
|
|
|
|
|
|
|
Meta.allow_tag_with_this_attribute_values(:span, "class", ["h-card"]) |
|
|
|
|
Meta.allow_tag_with_these_attributes(:span, []) |
|
|
|
|
|
|
|
|
|
@allow_inline_images Pleroma.Config.get([:markup, :allow_inline_images]) |
|
|
|
|
|
|
|
|
|
if @allow_inline_images do |
|
|
|
|
# restrict img tags to http/https only, because of MediaProxy. |
|
|
|
|
Meta.allow_tag_with_uri_attributes(:img, ["src"], ["http", "https"]) |
|
|
|
|
|
|
|
|
|
Meta.allow_tag_with_these_attributes(:img, [ |
|
|
|
|
"width", |
|
|
|
|
"height", |
|
|
|
|
"class", |
|
|
|
|
"title", |
|
|
|
|
"alt" |
|
|
|
|
]) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
if Pleroma.Config.get([:markup, :allow_tables]) do |
|
|
|
|
Meta.allow_tag_with_these_attributes(:table, []) |
|
|
|
|
Meta.allow_tag_with_these_attributes(:tbody, []) |
|
|
|
|
Meta.allow_tag_with_these_attributes(:td, []) |
|
|
|
|
Meta.allow_tag_with_these_attributes(:th, []) |
|
|
|
|
Meta.allow_tag_with_these_attributes(:thead, []) |
|
|
|
|
Meta.allow_tag_with_these_attributes(:tr, []) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
if Pleroma.Config.get([:markup, :allow_headings]) do |
|
|
|
|
Meta.allow_tag_with_these_attributes(:h1, []) |
|
|
|
|
Meta.allow_tag_with_these_attributes(:h2, []) |
|
|
|
|
Meta.allow_tag_with_these_attributes(:h3, []) |
|
|
|
|
Meta.allow_tag_with_these_attributes(:h4, []) |
|
|
|
|
Meta.allow_tag_with_these_attributes(:h5, []) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
if Pleroma.Config.get([:markup, :allow_fonts]) do |
|
|
|
|
Meta.allow_tag_with_these_attributes(:font, ["face"]) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
Meta.strip_everything_not_covered() |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
defmodule Pleroma.HTML.Transform.MediaProxy do |
|
|
|
|
@moduledoc "Transforms inline image URIs to use MediaProxy." |
|
|
|
|
|
|
|
|
|
alias Pleroma.Web.MediaProxy |
|
|
|
|
|
|
|
|
|
def before_scrub(html), do: html |
|
|
|
|
|
|
|
|
|
def scrub_attribute(:img, {"src", "http" <> target}) do |
|
|
|
|
media_url = |
|
|
|
|
("http" <> target) |
|
|
|
|
|> MediaProxy.url() |
|
|
|
|
|
|
|
|
|
{"src", media_url} |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
def scrub_attribute(_tag, attribute), do: attribute |
|
|
|
|
|
|
|
|
|
def scrub({:img, attributes, children}) do |
|
|
|
|
attributes = |
|
|
|
|
attributes |
|
|
|
|
|> Enum.map(fn attr -> scrub_attribute(:img, attr) end) |
|
|
|
|
|> Enum.reject(&is_nil(&1)) |
|
|
|
|
|
|
|
|
|
{:img, attributes, children} |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
def scrub({:comment, _text, _children}), do: "" |
|
|
|
|
|
|
|
|
|
def scrub({tag, attributes, children}), do: {tag, attributes, children} |
|
|
|
|
def scrub({_tag, children}), do: children |
|
|
|
|
def scrub(text), do: text |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
defmodule Pleroma.HTML.Scrubber.LinksOnly do |
|
|
|
|
@moduledoc """ |
|
|
|
|
An HTML scrubbing policy which limits to links only. |
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
@valid_schemes Pleroma.Config.get([:uri_schemes, :valid_schemes], []) |
|
|
|
|
|
|
|
|
|
require FastSanitize.Sanitizer.Meta |
|
|
|
|
alias FastSanitize.Sanitizer.Meta |
|
|
|
|
|
|
|
|
|
Meta.strip_comments() |
|
|
|
|
|
|
|
|
|
# links |
|
|
|
|
Meta.allow_tag_with_uri_attributes(:a, ["href"], @valid_schemes) |
|
|
|
|
|
|
|
|
|
Meta.allow_tag_with_this_attribute_values(:a, "rel", [ |
|
|
|
|
"tag", |
|
|
|
|
"nofollow", |
|
|
|
|
"noopener", |
|
|
|
|
"noreferrer", |
|
|
|
|
"me", |
|
|
|
|
"ugc" |
|
|
|
|
]) |
|
|
|
|
|
|
|
|
|
Meta.allow_tag_with_these_attributes(:a, ["name", "title"]) |
|
|
|
|
Meta.strip_everything_not_covered() |
|
|
|
|
end |
|
|
|
|