diff options
Diffstat (limited to 'priv')
-rw-r--r-- | priv/repo/migrations/20191118084425_create_user_relationships.exs | 17 | ||||
-rw-r--r-- | priv/repo/migrations/20191118084500_data_migration_populate_user_relationships.exs | 68 | ||||
-rw-r--r-- | priv/scrubbers/default.ex | 93 | ||||
-rw-r--r-- | priv/scrubbers/links_only.ex | 27 | ||||
-rw-r--r-- | priv/scrubbers/media_proxy.ex | 32 | ||||
-rw-r--r-- | priv/scrubbers/twitter_text.ex | 57 |
6 files changed, 294 insertions, 0 deletions
diff --git a/priv/repo/migrations/20191118084425_create_user_relationships.exs b/priv/repo/migrations/20191118084425_create_user_relationships.exs new file mode 100644 index 000000000..c281f887d --- /dev/null +++ b/priv/repo/migrations/20191118084425_create_user_relationships.exs @@ -0,0 +1,17 @@ +defmodule Pleroma.Repo.Migrations.CreateUserRelationships do + use Ecto.Migration + + def change do + create_if_not_exists table(:user_relationships) do + add(:source_id, references(:users, type: :uuid, on_delete: :delete_all)) + add(:target_id, references(:users, type: :uuid, on_delete: :delete_all)) + add(:relationship_type, :integer, null: false) + + timestamps(updated_at: false) + end + + create_if_not_exists( + unique_index(:user_relationships, [:source_id, :relationship_type, :target_id]) + ) + end +end diff --git a/priv/repo/migrations/20191118084500_data_migration_populate_user_relationships.exs b/priv/repo/migrations/20191118084500_data_migration_populate_user_relationships.exs new file mode 100644 index 000000000..990e9f3b8 --- /dev/null +++ b/priv/repo/migrations/20191118084500_data_migration_populate_user_relationships.exs @@ -0,0 +1,68 @@ +defmodule Pleroma.Repo.Migrations.DataMigrationPopulateUserRelationships do + use Ecto.Migration + + alias Ecto.Adapters.SQL + alias Pleroma.Repo + + require Logger + + def up do + Enum.each( + [blocks: 1, mutes: 2, muted_reblogs: 3, muted_notifications: 4, subscribers: 5], + fn {field, relationship_type_code} -> + migrate(field, relationship_type_code) + + if field == :subscribers do + drop_if_exists(index(:users, [:subscribers])) + end + end + ) + end + + def down, do: :noop + + defp migrate(field, relationship_type_code) do + Logger.info("Processing users.#{field}...") + + {:ok, %{rows: field_rows}} = + SQL.query(Repo, "SELECT id, #{field} FROM users WHERE #{field} != '{}'") + + target_ap_ids = + Enum.flat_map( + field_rows, + fn [_, ap_ids] -> ap_ids end + ) + |> Enum.uniq() + + # Selecting ids of all targets at once in order to reduce the number of SELECT queries + {:ok, %{rows: target_ap_id_id}} = + SQL.query(Repo, "SELECT ap_id, id FROM users WHERE ap_id = ANY($1)", [target_ap_ids]) + + target_id_by_ap_id = Enum.into(target_ap_id_id, %{}, fn [k, v] -> {k, v} end) + + Enum.each( + field_rows, + fn [source_id, target_ap_ids] -> + source_uuid = Ecto.UUID.cast!(source_id) + + for target_ap_id <- target_ap_ids do + target_id = target_id_by_ap_id[target_ap_id] + + with {:ok, target_uuid} <- target_id && Ecto.UUID.cast(target_id) do + execute(""" + INSERT INTO user_relationships( + source_id, target_id, relationship_type, inserted_at + ) + VALUES( + '#{source_uuid}'::uuid, '#{target_uuid}'::uuid, #{relationship_type_code}, now() + ) + ON CONFLICT (source_id, relationship_type, target_id) DO NOTHING + """) + else + _ -> Logger.warn("Unresolved #{field} reference: (#{source_uuid}, #{target_id})") + end + end + end + ) + end +end diff --git a/priv/scrubbers/default.ex b/priv/scrubbers/default.ex new file mode 100644 index 000000000..ea0480dcd --- /dev/null +++ b/priv/scrubbers/default.ex @@ -0,0 +1,93 @@ +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 diff --git a/priv/scrubbers/links_only.ex b/priv/scrubbers/links_only.ex new file mode 100644 index 000000000..b30a00589 --- /dev/null +++ b/priv/scrubbers/links_only.ex @@ -0,0 +1,27 @@ +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 diff --git a/priv/scrubbers/media_proxy.ex b/priv/scrubbers/media_proxy.ex new file mode 100644 index 000000000..5dbe57666 --- /dev/null +++ b/priv/scrubbers/media_proxy.ex @@ -0,0 +1,32 @@ +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 diff --git a/priv/scrubbers/twitter_text.ex b/priv/scrubbers/twitter_text.ex new file mode 100644 index 000000000..c4e796cad --- /dev/null +++ b/priv/scrubbers/twitter_text.ex @@ -0,0 +1,57 @@ +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 |