Verified Commit 3bc7d122 authored by Haelwenn's avatar Haelwenn
Browse files

Remove sensitive-property setting #nsfw, create HashtagPolicy

parent 998437d4
Pipeline #35224 passed with stages
in 6 minutes and 37 seconds
......@@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- **Breaking**: Changed `mix pleroma.user toggle_confirmed` to `mix pleroma.user confirm`
- **Breaking**: Changed `mix pleroma.user toggle_activated` to `mix pleroma.user activate/deactivate`
- **Breaking:** NSFW hashtag is no longer added on sensitive posts
- Polls now always return a `voters_count`, even if they are single-choice.
- Admin Emails: The ap id is used as the user link in emails now.
- Improved registration workflow for email confirmation and account approval modes.
......@@ -489,7 +490,6 @@ switched to a new configuration mechanism, however it was not officially removed
- Static-FE: Fix remote posts not being sanitized
### Fixed
=======
- Rate limiter crashes when there is no explicitly specified ip in the config
- 500 errors when no `Accept` header is present if Static-FE is enabled
- Instance panel not being updated immediately due to wrong `Cache-Control` headers
......
......@@ -391,6 +391,11 @@
federated_timeline_removal: [],
replace: []
config :pleroma, :mrf_hashtag,
sensitive: ["nsfw"],
reject: [],
federated_timeline_removal: []
config :pleroma, :mrf_subchain, match_actor: %{}
config :pleroma, :mrf_activity_expiration, days: 365
......
......@@ -210,6 +210,16 @@ config :pleroma, :mrf_user_allowlist, %{
* `days`: Default global expiration time for all local Create activities (in days)
#### :mrf_hashtag
* `sensitive`: List of hashtags to mark activities as sensitive (default: `nsfw`)
* `federated_timeline_removal`: List of hashtags to remove activities from the federated timeline (aka TWNK)
* `reject`: List of hashtags to reject activities from
Notes:
- The hashtags in the configuration do not have a leading `#`.
- This MRF Policy is always enabled, if you want to disable it you have to set empty lists
### :activitypub
* `unfollow_blocked`: Whether blocks result in people getting unfollowed
* `outgoing_blocks`: Whether to federate blocks to other instances
......
......@@ -92,7 +92,9 @@ def pipeline_filter(%{} = message, meta) do
end
def get_policies do
Pleroma.Config.get([:mrf, :policies], []) |> get_policies()
Pleroma.Config.get([:mrf, :policies], [])
|> get_policies()
|> Enum.concat([Pleroma.Web.ActivityPub.MRF.HashtagPolicy])
end
defp get_policies(policy) when is_atom(policy), do: [policy]
......
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicy do
require Pleroma.Constants
alias Pleroma.Config
alias Pleroma.Object
@moduledoc """
Reject, TWKN-remove or Set-Sensitive messsages with specific hashtags (without the leading #)
Note: This MRF Policy is always enabled, if you want to disable it you have to set empty lists.
"""
@behaviour Pleroma.Web.ActivityPub.MRF
defp check_reject(message, hashtags) do
if Enum.any?(Config.get([:mrf_hashtag, :reject]), fn match -> match in hashtags end) do
{:reject, "[HashtagPolicy] Matches with rejected keyword"}
else
{:ok, message}
end
end
defp check_ftl_removal(%{"to" => to} = message, hashtags) do
if Pleroma.Constants.as_public() in to and
Enum.any?(Config.get([:mrf_hashtag, :federated_timeline_removal]), fn match ->
match in hashtags
end) do
to = List.delete(to, Pleroma.Constants.as_public())
cc = [Pleroma.Constants.as_public() | message["cc"] || []]
message =
message
|> Map.put("to", to)
|> Map.put("cc", cc)
|> Kernel.put_in(["object", "to"], to)
|> Kernel.put_in(["object", "cc"], cc)
{:ok, message}
else
{:ok, message}
end
end
defp check_ftl_removal(message, _hashtags), do: {:ok, message}
defp check_sensitive(message, hashtags) do
if Enum.any?(Config.get([:mrf_hashtag, :sensitive]), fn match -> match in hashtags end) do
{:ok, Kernel.put_in(message, ["object", "sensitive"], true)}
else
{:ok, message}
end
end
@impl true
def filter(%{"type" => "Create", "object" => object} = message) do
hashtags = Object.hashtags(%Object{data: object})
if hashtags != [] do
with {:ok, message} <- check_reject(message, hashtags),
{:ok, message} <- check_ftl_removal(message, hashtags),
{:ok, message} <- check_sensitive(message, hashtags) do
{:ok, message}
end
else
{:ok, message}
end
end
@impl true
def filter(message), do: {:ok, message}
@impl true
def describe do
mrf_hashtag =
Config.get(:mrf_hashtag)
|> Enum.into(%{})
{:ok, %{mrf_hashtag: mrf_hashtag}}
end
@impl true
def config_description do
%{
key: :mrf_hashtag,
related_policy: "Pleroma.Web.ActivityPub.MRF.HashtagPolicy",
label: "MRF Hashtag",
description: @moduledoc,
children: [
%{
key: :reject,
type: {:list, :string},
description: "A list of hashtags which result in message being rejected.",
suggestions: ["foo"]
},
%{
key: :federated_timeline_removal,
type: {:list, :string},
description:
"A list of hashtags which result in message being removed from federated timelines (a.k.a unlisted).",
suggestions: ["foo"]
},
%{
key: :sensitive,
type: {:list, :string},
description:
"A list of hashtags which result in message being set as sensitive (a.k.a NSFW/R-18)",
suggestions: ["nsfw", "r18"]
}
]
}
end
end
......@@ -64,22 +64,16 @@ defp check_media_nsfw(
%{host: actor_host} = _actor_info,
%{
"type" => "Create",
"object" => child_object
"object" => %{} = _child_object
} = object
)
when is_map(child_object) do
) do
media_nsfw =
Config.get([:mrf_simple, :media_nsfw])
|> MRF.subdomains_regex()
object =
if MRF.subdomain_match?(media_nsfw, actor_host) do
child_object =
child_object
|> Map.put("tag", (child_object["tag"] || []) ++ ["nsfw"])
|> Map.put("sensitive", true)
Map.put(object, "object", child_object)
Kernel.put_in(object, ["object", "sensitive"], true)
else
object
end
......
......@@ -28,20 +28,11 @@ defp process_tag(
"mrf_tag:media-force-nsfw",
%{
"type" => "Create",
"object" => %{"attachment" => child_attachment} = object
"object" => %{"attachment" => child_attachment}
} = message
)
when length(child_attachment) > 0 do
tags = (object["tag"] || []) ++ ["nsfw"]
object =
object
|> Map.put("tag", tags)
|> Map.put("sensitive", true)
message = Map.put(message, "object", object)
{:ok, message}
{:ok, Kernel.put_in(message, ["object", "sensitive"], true)}
end
defp process_tag(
......
......@@ -40,7 +40,6 @@ def fix_object(object, options \\ []) do
|> fix_in_reply_to(options)
|> fix_emoji()
|> fix_tag()
|> set_sensitive()
|> fix_content_map()
|> fix_addressing()
|> fix_summary()
......@@ -741,7 +740,6 @@ def replies(_), do: []
# Prepares the object of an outgoing create activity.
def prepare_object(object) do
object
|> set_sensitive
|> add_hashtags
|> add_mention_tags
|> add_emoji_tags
......@@ -932,15 +930,6 @@ def set_conversation(object) do
Map.put(object, "conversation", object["context"])
end
def set_sensitive(%{"sensitive" => _} = object) do
object
end
def set_sensitive(object) do
tags = object["tag"] || []
Map.put(object, "sensitive", "nsfw" in tags)
end
def set_type(%{"type" => "Answer"} = object) do
Map.put(object, "type", "Note")
end
......
......@@ -179,7 +179,7 @@ defp context(draft) do
end
defp sensitive(draft) do
sensitive = draft.params[:sensitive] || Enum.member?(draft.tags, {"#nsfw", "nsfw"})
sensitive = draft.params[:sensitive]
%__MODULE__{draft | sensitive: sensitive}
end
......
......@@ -217,7 +217,6 @@ def make_content_html(%ActivityDraft{} = draft) do
draft.status
|> format_input(content_type, options)
|> maybe_add_attachments(draft.attachments, attachment_links)
|> maybe_add_nsfw_tag(draft.params)
end
defp get_content_type(content_type) do
......@@ -228,13 +227,6 @@ defp get_content_type(content_type) do
end
end
defp maybe_add_nsfw_tag({text, mentions, tags}, %{"sensitive" => sensitive})
when sensitive in [true, "True", "true", "1"] do
{text, mentions, [{"#nsfw", "nsfw"} | tags]}
end
defp maybe_add_nsfw_tag(data, _), do: data
def make_context(_, %Participation{} = participation) do
Repo.preload(participation, :conversation).conversation.ap_id
end
......
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicyTest do
use Oban.Testing, repo: Pleroma.Repo
use Pleroma.DataCase
alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.CommonAPI
import Pleroma.Factory
test "it sets the sensitive property with relevant hashtags" do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: "#nsfw hey"})
{:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
assert modified["object"]["sensitive"]
end
test "it doesn't sets the sensitive property with irrelevant hashtags" do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: "#cofe hey"})
{:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
refute modified["object"]["sensitive"]
end
end
......@@ -75,10 +75,7 @@ test "has a matching host" do
local_message = build_local_message()
assert SimplePolicy.filter(media_message) ==
{:ok,
media_message
|> put_in(["object", "tag"], ["foo", "nsfw"])
|> put_in(["object", "sensitive"], true)}
{:ok, put_in(media_message, ["object", "sensitive"], true)}
assert SimplePolicy.filter(local_message) == {:ok, local_message}
end
......@@ -89,10 +86,7 @@ test "match with wildcard domain" do
local_message = build_local_message()
assert SimplePolicy.filter(media_message) ==
{:ok,
media_message
|> put_in(["object", "tag"], ["foo", "nsfw"])
|> put_in(["object", "sensitive"], true)}
{:ok, put_in(media_message, ["object", "sensitive"], true)}
assert SimplePolicy.filter(local_message) == {:ok, local_message}
end
......
......@@ -114,7 +114,7 @@ test "Mark as sensitive on presence of attachments" do
except_message = %{
"actor" => actor.ap_id,
"type" => "Create",
"object" => %{"tag" => ["test", "nsfw"], "attachment" => ["file1"], "sensitive" => true}
"object" => %{"tag" => ["test"], "attachment" => ["file1"], "sensitive" => true}
}
assert TagPolicy.filter(message) == {:ok, except_message}
......
......@@ -68,7 +68,12 @@ test "it works as expected with noop policy" do
clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.NoOpPolicy])
expected = %{
mrf_policies: ["NoOpPolicy"],
mrf_policies: ["NoOpPolicy", "HashtagPolicy"],
mrf_hashtag: %{
federated_timeline_removal: [],
reject: [],
sensitive: ["nsfw"]
},
exclusions: false
}
......@@ -79,8 +84,13 @@ test "it works as expected with mock policy" do
clear_config([:mrf, :policies], [MRFModuleMock])
expected = %{
mrf_policies: ["MRFModuleMock"],
mrf_policies: ["MRFModuleMock", "HashtagPolicy"],
mrf_module_mock: "some config data",
mrf_hashtag: %{
federated_timeline_removal: [],
reject: [],
sensitive: ["nsfw"]
},
exclusions: false
}
......
......@@ -153,15 +153,6 @@ test "it turns mentions into tags" do
end
end
test "it adds the sensitive property" do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: "#nsfw hey"})
{:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
assert modified["object"]["sensitive"]
end
test "it adds the json-ld context and the conversation property" do
user = insert(:user)
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment