Merge branch 'nsfw-api-mrf' into 'develop'

NSFW API Policy

See merge request pleroma/pleroma!3471
This commit is contained in:
lain 2024-05-27 15:20:43 +00:00
commit e93ae96e13
5 changed files with 555 additions and 0 deletions

View file

@ -0,0 +1,267 @@
# 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.NsfwApiPolicyTest do
use Pleroma.DataCase
import ExUnit.CaptureLog
import Pleroma.Factory
alias Pleroma.Constants
alias Pleroma.Web.ActivityPub.MRF.NsfwApiPolicy
require Pleroma.Constants
@policy :mrf_nsfw_api
@sfw_url "https://kittens.co/kitty.gif"
@nsfw_url "https://b00bies.com/nsfw.jpg"
@timeout_url "http://time.out/i.jpg"
setup_all do
clear_config(@policy,
url: "http://127.0.0.1:5000/",
threshold: 0.7,
mark_sensitive: true,
unlist: false,
reject: false
)
end
setup do
Tesla.Mock.mock(fn
# NSFW URL
%{method: :get, url: "http://127.0.0.1:5000/?url=#{@nsfw_url}"} ->
%Tesla.Env{status: 200, body: ~s({"score":0.99772077798843384,"url":"#{@nsfw_url}"})}
# SFW URL
%{method: :get, url: "http://127.0.0.1:5000/?url=#{@sfw_url}"} ->
%Tesla.Env{status: 200, body: ~s({"score":0.00011714912398019806,"url":"#{@sfw_url}"})}
# Timeout URL
%{method: :get, url: "http://127.0.0.1:5000/?url=#{@timeout_url}"} ->
{:error, :timeout}
# Fallback URL
%{method: :get, url: "http://127.0.0.1:5000/?url=" <> url} ->
body =
~s({"error_code":500,"error_reason":"[Errno -2] Name or service not known","url":"#{url}"})
%Tesla.Env{status: 500, body: body}
end)
:ok
end
describe "build_request_url/1" do
test "it works" do
expected = "http://127.0.0.1:5000/?url=https://b00bies.com/nsfw.jpg"
assert NsfwApiPolicy.build_request_url(@nsfw_url) == expected
end
test "it adds a trailing slash" do
clear_config([@policy, :url], "http://localhost:5000")
expected = "http://localhost:5000/?url=https://b00bies.com/nsfw.jpg"
assert NsfwApiPolicy.build_request_url(@nsfw_url) == expected
end
test "it adds a trailing slash preserving the path" do
clear_config([@policy, :url], "http://localhost:5000/nsfw_api")
expected = "http://localhost:5000/nsfw_api/?url=https://b00bies.com/nsfw.jpg"
assert NsfwApiPolicy.build_request_url(@nsfw_url) == expected
end
end
describe "parse_url/1" do
test "returns decoded JSON from the API server" do
expected = %{"score" => 0.99772077798843384, "url" => @nsfw_url}
assert NsfwApiPolicy.parse_url(@nsfw_url) == {:ok, expected}
end
test "warns when the API server fails" do
expected = "[NsfwApiPolicy]: The API server failed. Skipping."
assert capture_log(fn -> NsfwApiPolicy.parse_url(@timeout_url) end) =~ expected
end
test "returns {:error, _} tuple when the API server fails" do
capture_log(fn ->
assert {:error, _} = NsfwApiPolicy.parse_url(@timeout_url)
end)
end
end
describe "check_url_nsfw/1" do
test "returns {:nsfw, _} tuple" do
expected = {:nsfw, %{url: @nsfw_url, score: 0.99772077798843384, threshold: 0.7}}
assert NsfwApiPolicy.check_url_nsfw(@nsfw_url) == expected
end
test "returns {:sfw, _} tuple" do
expected = {:sfw, %{url: @sfw_url, score: 0.00011714912398019806, threshold: 0.7}}
assert NsfwApiPolicy.check_url_nsfw(@sfw_url) == expected
end
test "returns {:sfw, _} on failure" do
expected = {:sfw, %{url: @timeout_url, score: nil, threshold: 0.7}}
capture_log(fn ->
assert NsfwApiPolicy.check_url_nsfw(@timeout_url) == expected
end)
end
test "works with map URL" do
expected = {:nsfw, %{url: @nsfw_url, score: 0.99772077798843384, threshold: 0.7}}
assert NsfwApiPolicy.check_url_nsfw(%{"href" => @nsfw_url}) == expected
end
end
describe "check_attachment_nsfw/1" do
test "returns {:nsfw, _} if any items are NSFW" do
attachment = %{"url" => [%{"href" => @nsfw_url}, @nsfw_url, @sfw_url]}
assert NsfwApiPolicy.check_attachment_nsfw(attachment) == {:nsfw, attachment}
end
test "returns {:sfw, _} if all items are SFW" do
attachment = %{"url" => [%{"href" => @sfw_url}, @sfw_url, @sfw_url]}
assert NsfwApiPolicy.check_attachment_nsfw(attachment) == {:sfw, attachment}
end
test "works with binary URL" do
attachment = %{"url" => @nsfw_url}
assert NsfwApiPolicy.check_attachment_nsfw(attachment) == {:nsfw, attachment}
end
end
describe "check_object_nsfw/1" do
test "returns {:nsfw, _} if any items are NSFW" do
object = %{"attachment" => [%{"url" => [%{"href" => @nsfw_url}, @sfw_url]}]}
assert NsfwApiPolicy.check_object_nsfw(object) == {:nsfw, object}
end
test "returns {:sfw, _} if all items are SFW" do
object = %{"attachment" => [%{"url" => [%{"href" => @sfw_url}, @sfw_url]}]}
assert NsfwApiPolicy.check_object_nsfw(object) == {:sfw, object}
end
test "works with embedded object" do
object = %{"object" => %{"attachment" => [%{"url" => [%{"href" => @nsfw_url}, @sfw_url]}]}}
assert NsfwApiPolicy.check_object_nsfw(object) == {:nsfw, object}
end
end
describe "unlist/1" do
test "unlist addressing" do
user = insert(:user)
object = %{
"to" => [Constants.as_public()],
"cc" => [user.follower_address, "https://hello.world/users/alex"],
"actor" => user.ap_id
}
expected = %{
"to" => [user.follower_address],
"cc" => [Constants.as_public(), "https://hello.world/users/alex"],
"actor" => user.ap_id
}
assert NsfwApiPolicy.unlist(object) == expected
end
test "raise if user isn't found" do
object = %{
"to" => [Constants.as_public()],
"cc" => [],
"actor" => "https://hello.world/users/alex"
}
assert_raise(RuntimeError, fn ->
NsfwApiPolicy.unlist(object)
end)
end
end
describe "mark_sensitive/1" do
test "adds nsfw tag and marks sensitive" do
object = %{"tag" => ["yolo"]}
expected = %{"tag" => ["yolo", "nsfw"], "sensitive" => true}
assert NsfwApiPolicy.mark_sensitive(object) == expected
end
test "works with embedded object" do
object = %{"object" => %{"tag" => ["yolo"]}}
expected = %{"object" => %{"tag" => ["yolo", "nsfw"], "sensitive" => true}}
assert NsfwApiPolicy.mark_sensitive(object) == expected
end
end
describe "filter/1" do
setup do
user = insert(:user)
nsfw_object = %{
"to" => [Constants.as_public()],
"cc" => [user.follower_address],
"actor" => user.ap_id,
"attachment" => [%{"url" => @nsfw_url}]
}
sfw_object = %{
"to" => [Constants.as_public()],
"cc" => [user.follower_address],
"actor" => user.ap_id,
"attachment" => [%{"url" => @sfw_url}]
}
%{user: user, nsfw_object: nsfw_object, sfw_object: sfw_object}
end
test "passes SFW object through", %{sfw_object: object} do
{:ok, _} = NsfwApiPolicy.filter(object)
end
test "passes NSFW object through when actions are disabled", %{nsfw_object: object} do
clear_config([@policy, :mark_sensitive], false)
clear_config([@policy, :unlist], false)
clear_config([@policy, :reject], false)
{:ok, _} = NsfwApiPolicy.filter(object)
end
test "passes NSFW object through when :threshold is 1", %{nsfw_object: object} do
clear_config([@policy, :reject], true)
clear_config([@policy, :threshold], 1)
{:ok, _} = NsfwApiPolicy.filter(object)
end
test "rejects SFW object through when :threshold is 0", %{sfw_object: object} do
clear_config([@policy, :reject], true)
clear_config([@policy, :threshold], 0)
{:reject, _} = NsfwApiPolicy.filter(object)
end
test "rejects NSFW when :reject is enabled", %{nsfw_object: object} do
clear_config([@policy, :reject], true)
{:reject, _} = NsfwApiPolicy.filter(object)
end
test "passes NSFW through when :reject is disabled", %{nsfw_object: object} do
clear_config([@policy, :reject], false)
{:ok, _} = NsfwApiPolicy.filter(object)
end
test "unlists NSFW when :unlist is enabled", %{user: user, nsfw_object: object} do
clear_config([@policy, :unlist], true)
{:ok, object} = NsfwApiPolicy.filter(object)
assert object["to"] == [user.follower_address]
end
test "passes NSFW through when :unlist is disabled", %{nsfw_object: object} do
clear_config([@policy, :unlist], false)
{:ok, object} = NsfwApiPolicy.filter(object)
assert object["to"] == [Constants.as_public()]
end
end
end