forked from AkkomaGang/akkoma
Merge branch 'release/2.1.2' into 'stable'
Release/2.1.2 See merge request pleroma/secteam/pleroma!17
This commit is contained in:
commit
a0f5e8b27e
45 changed files with 321 additions and 76 deletions
CHANGELOG.mdmix.exs
lib/pleroma/web
activity_pub
api_spec/operations
common_api
mastodon_api
oauth
pleroma_api/controllers
rich_media
templates/o_auth/o_auth
priv/static
index.htmlsw-pleroma.js
static
font
fontello.1599568314856.wofffontello.1599568314856.woff2fontello.1600365488745.eotfontello.1600365488745.svgfontello.1600365488745.ttffontello.1600365488745.wofffontello.1600365488745.woff2
fontello.1600365488745.cssfontello.jsonjs
test
22
CHANGELOG.md
22
CHANGELOG.md
|
@ -3,6 +3,28 @@ All notable changes to this project will be documented in this file.
|
|||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
|
||||
## [2.1.2] - 2020-09-17
|
||||
|
||||
### Security
|
||||
|
||||
- Fix most MRF rules either crashing or not being applied to objects passed into the Common Pipeline (ChatMessage, Question, Answer, Audio, Event).
|
||||
|
||||
### Fixed
|
||||
|
||||
- Welcome Chat messages preventing user registration with MRF Simple Policy applied to the local instance.
|
||||
- Mastodon API: the public timeline returning an error when the `reply_visibility` parameter is set to `self` for an unauthenticated user.
|
||||
- Mastodon Streaming API: Handler crashes on authentication failures, resulting in error logs.
|
||||
- Mastodon Streaming API: Error logs on client pings.
|
||||
- Rich media: Log spam on failures. Now the error is only logged once per attempt.
|
||||
|
||||
### Changed
|
||||
|
||||
- Rich Media: A HEAD request is now done to the url, to ensure it has the appropriate content type and size before proceeding with a GET.
|
||||
|
||||
### Upgrade notes
|
||||
|
||||
1. Restart Pleroma
|
||||
|
||||
## [2.1.1] - 2020-09-08
|
||||
|
||||
### Security
|
||||
|
|
|
@ -744,7 +744,7 @@ defp restrict_replies(query, %{exclude_replies: true}) do
|
|||
end
|
||||
|
||||
defp restrict_replies(query, %{
|
||||
reply_filtering_user: user,
|
||||
reply_filtering_user: %User{} = user,
|
||||
reply_visibility: "self"
|
||||
}) do
|
||||
from(
|
||||
|
@ -760,7 +760,7 @@ defp restrict_replies(query, %{
|
|||
end
|
||||
|
||||
defp restrict_replies(query, %{
|
||||
reply_filtering_user: user,
|
||||
reply_filtering_user: %User{} = user,
|
||||
reply_visibility: "following"
|
||||
}) do
|
||||
from(
|
||||
|
|
|
@ -5,16 +5,34 @@
|
|||
defmodule Pleroma.Web.ActivityPub.MRF do
|
||||
@callback filter(Map.t()) :: {:ok | :reject, Map.t()}
|
||||
|
||||
def filter(policies, %{} = object) do
|
||||
def filter(policies, %{} = message) do
|
||||
policies
|
||||
|> Enum.reduce({:ok, object}, fn
|
||||
policy, {:ok, object} -> policy.filter(object)
|
||||
|> Enum.reduce({:ok, message}, fn
|
||||
policy, {:ok, message} -> policy.filter(message)
|
||||
_, error -> error
|
||||
end)
|
||||
end
|
||||
|
||||
def filter(%{} = object), do: get_policies() |> filter(object)
|
||||
|
||||
def pipeline_filter(%{} = message, meta) do
|
||||
object = meta[:object_data]
|
||||
ap_id = message["object"]
|
||||
|
||||
if object && ap_id do
|
||||
with {:ok, message} <- filter(Map.put(message, "object", object)) do
|
||||
meta = Keyword.put(meta, :object_data, message["object"])
|
||||
{:ok, Map.put(message, "object", ap_id), meta}
|
||||
else
|
||||
{err, message} -> {err, message, meta}
|
||||
end
|
||||
else
|
||||
{err, message} = filter(message)
|
||||
|
||||
{err, message, meta}
|
||||
end
|
||||
end
|
||||
|
||||
def get_policies do
|
||||
Pleroma.Config.get([:mrf, :policies], []) |> get_policies()
|
||||
end
|
||||
|
|
|
@ -20,9 +20,17 @@ defp string_matches?(string, pattern) do
|
|||
String.match?(string, pattern)
|
||||
end
|
||||
|
||||
defp check_reject(%{"object" => %{"content" => content, "summary" => summary}} = message) do
|
||||
defp object_payload(%{} = object) do
|
||||
[object["content"], object["summary"], object["name"]]
|
||||
|> Enum.filter(& &1)
|
||||
|> Enum.join("\n")
|
||||
end
|
||||
|
||||
defp check_reject(%{"object" => %{} = object} = message) do
|
||||
payload = object_payload(object)
|
||||
|
||||
if Enum.any?(Pleroma.Config.get([:mrf_keyword, :reject]), fn pattern ->
|
||||
string_matches?(content, pattern) or string_matches?(summary, pattern)
|
||||
string_matches?(payload, pattern)
|
||||
end) do
|
||||
{:reject, "[KeywordPolicy] Matches with rejected keyword"}
|
||||
else
|
||||
|
@ -30,12 +38,12 @@ defp check_reject(%{"object" => %{"content" => content, "summary" => summary}} =
|
|||
end
|
||||
end
|
||||
|
||||
defp check_ftl_removal(
|
||||
%{"to" => to, "object" => %{"content" => content, "summary" => summary}} = message
|
||||
) do
|
||||
defp check_ftl_removal(%{"to" => to, "object" => %{} = object} = message) do
|
||||
payload = object_payload(object)
|
||||
|
||||
if Pleroma.Constants.as_public() in to and
|
||||
Enum.any?(Pleroma.Config.get([:mrf_keyword, :federated_timeline_removal]), fn pattern ->
|
||||
string_matches?(content, pattern) or string_matches?(summary, pattern)
|
||||
string_matches?(payload, pattern)
|
||||
end) do
|
||||
to = List.delete(to, Pleroma.Constants.as_public())
|
||||
cc = [Pleroma.Constants.as_public() | message["cc"] || []]
|
||||
|
@ -51,35 +59,24 @@ defp check_ftl_removal(
|
|||
end
|
||||
end
|
||||
|
||||
defp check_replace(%{"object" => %{"content" => content, "summary" => summary}} = message) do
|
||||
content =
|
||||
if is_binary(content) do
|
||||
content
|
||||
else
|
||||
""
|
||||
end
|
||||
defp check_replace(%{"object" => %{} = object} = message) do
|
||||
object =
|
||||
["content", "name", "summary"]
|
||||
|> Enum.filter(fn field -> Map.has_key?(object, field) && object[field] end)
|
||||
|> Enum.reduce(object, fn field, object ->
|
||||
data =
|
||||
Enum.reduce(
|
||||
Pleroma.Config.get([:mrf_keyword, :replace]),
|
||||
object[field],
|
||||
fn {pat, repl}, acc -> String.replace(acc, pat, repl) end
|
||||
)
|
||||
|
||||
summary =
|
||||
if is_binary(summary) do
|
||||
summary
|
||||
else
|
||||
""
|
||||
end
|
||||
Map.put(object, field, data)
|
||||
end)
|
||||
|
||||
{content, summary} =
|
||||
Enum.reduce(
|
||||
Pleroma.Config.get([:mrf_keyword, :replace]),
|
||||
{content, summary},
|
||||
fn {pattern, replacement}, {content_acc, summary_acc} ->
|
||||
{String.replace(content_acc, pattern, replacement),
|
||||
String.replace(summary_acc, pattern, replacement)}
|
||||
end
|
||||
)
|
||||
message = Map.put(message, "object", object)
|
||||
|
||||
{:ok,
|
||||
message
|
||||
|> put_in(["object", "content"], content)
|
||||
|> put_in(["object", "summary"], summary)}
|
||||
{:ok, message}
|
||||
end
|
||||
|
||||
@impl true
|
||||
|
|
|
@ -66,7 +66,8 @@ defp check_media_nsfw(
|
|||
"type" => "Create",
|
||||
"object" => child_object
|
||||
} = object
|
||||
) do
|
||||
)
|
||||
when is_map(child_object) do
|
||||
media_nsfw =
|
||||
Config.get([:mrf_simple, :media_nsfw])
|
||||
|> MRF.subdomains_regex()
|
||||
|
|
|
@ -28,8 +28,7 @@ def filter(%{"actor" => actor} = message) do
|
|||
}"
|
||||
)
|
||||
|
||||
subchain
|
||||
|> MRF.filter(message)
|
||||
MRF.filter(subchain, message)
|
||||
else
|
||||
_e -> {:ok, message}
|
||||
end
|
||||
|
|
|
@ -26,13 +26,17 @@ def common_pipeline(object, meta) do
|
|||
|
||||
{:error, e} ->
|
||||
{:error, e}
|
||||
|
||||
{:reject, e} ->
|
||||
{:reject, e}
|
||||
end
|
||||
end
|
||||
|
||||
def do_common_pipeline(object, meta) do
|
||||
with {_, {:ok, validated_object, meta}} <-
|
||||
{:validate_object, ObjectValidator.validate(object, meta)},
|
||||
{_, {:ok, mrfd_object}} <- {:mrf_object, MRF.filter(validated_object)},
|
||||
{_, {:ok, mrfd_object, meta}} <-
|
||||
{:mrf_object, MRF.pipeline_filter(validated_object, meta)},
|
||||
{_, {:ok, activity, meta}} <-
|
||||
{:persist_object, ActivityPub.persist(mrfd_object, meta)},
|
||||
{_, {:ok, activity, meta}} <-
|
||||
|
@ -40,7 +44,7 @@ def do_common_pipeline(object, meta) do
|
|||
{_, {:ok, _}} <- {:federation, maybe_federate(activity, meta)} do
|
||||
{:ok, activity, meta}
|
||||
else
|
||||
{:mrf_object, {:reject, _}} -> {:ok, nil, meta}
|
||||
{:mrf_object, {:reject, message, _}} -> {:reject, message}
|
||||
e -> {:error, e}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -311,7 +311,7 @@ def fix_url(object), do: object
|
|||
def fix_emoji(%{"tag" => tags} = object) when is_list(tags) do
|
||||
emoji =
|
||||
tags
|
||||
|> Enum.filter(fn data -> data["type"] == "Emoji" and data["icon"] end)
|
||||
|> Enum.filter(fn data -> is_map(data) and data["type"] == "Emoji" and data["icon"] end)
|
||||
|> Enum.reduce(%{}, fn data, mapping ->
|
||||
name = String.trim(data["name"], ":")
|
||||
|
||||
|
|
|
@ -184,7 +184,8 @@ def post_chat_message_operation do
|
|||
"application/json",
|
||||
ChatMessage
|
||||
),
|
||||
400 => Operation.response("Bad Request", "application/json", ApiError)
|
||||
400 => Operation.response("Bad Request", "application/json", ApiError),
|
||||
422 => Operation.response("MRF Rejection", "application/json", ApiError)
|
||||
},
|
||||
security: [
|
||||
%{
|
||||
|
|
|
@ -55,7 +55,7 @@ def create_operation do
|
|||
"application/json",
|
||||
%Schema{oneOf: [Status, ScheduledStatus]}
|
||||
),
|
||||
422 => Operation.response("Bad Request", "application/json", ApiError)
|
||||
422 => Operation.response("Bad Request / MRF Rejection", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
|
|
@ -49,6 +49,9 @@ def post_chat_message(%User{} = user, %User{} = recipient, content, opts \\ [])
|
|||
local: true
|
||||
)} do
|
||||
{:ok, activity}
|
||||
else
|
||||
{:common_pipeline, {:reject, _} = e} -> e
|
||||
e -> e
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -37,12 +37,12 @@ def init(%{qs: qs} = req, state) do
|
|||
else
|
||||
{:error, :bad_topic} ->
|
||||
Logger.debug("#{__MODULE__} bad topic #{inspect(req)}")
|
||||
{:ok, req} = :cowboy_req.reply(404, req)
|
||||
req = :cowboy_req.reply(404, req)
|
||||
{:ok, req, state}
|
||||
|
||||
{:error, :unauthorized} ->
|
||||
Logger.debug("#{__MODULE__} authentication error: #{inspect(req)}")
|
||||
{:ok, req} = :cowboy_req.reply(401, req)
|
||||
req = :cowboy_req.reply(401, req)
|
||||
{:ok, req, state}
|
||||
end
|
||||
end
|
||||
|
@ -64,7 +64,9 @@ def websocket_handle(:pong, state) do
|
|||
{:ok, %{state | timer: timer()}}
|
||||
end
|
||||
|
||||
# We never receive messages.
|
||||
# We only receive pings for now
|
||||
def websocket_handle(:ping, state), do: {:ok, state}
|
||||
|
||||
def websocket_handle(frame, state) do
|
||||
Logger.error("#{__MODULE__} received frame: #{inspect(frame)}")
|
||||
{:ok, state}
|
||||
|
@ -98,6 +100,10 @@ def websocket_info(:tick, state) do
|
|||
{:reply, :ping, %{state | timer: nil, count: 0}, :hibernate}
|
||||
end
|
||||
|
||||
# State can be `[]` only in case we terminate before switching to websocket,
|
||||
# we already log errors for these cases in `init/1`, so just do nothing here
|
||||
def terminate(_reason, _req, []), do: :ok
|
||||
|
||||
def terminate(reason, _req, state) do
|
||||
Logger.debug(
|
||||
"#{__MODULE__} terminating websocket connection for user #{
|
||||
|
|
|
@ -145,7 +145,10 @@ def create_authorization(
|
|||
def after_create_authorization(%Plug.Conn{} = conn, %Authorization{} = auth, %{
|
||||
"authorization" => %{"redirect_uri" => @oob_token_redirect_uri}
|
||||
}) do
|
||||
render(conn, "oob_authorization_created.html", %{auth: auth})
|
||||
# Enforcing the view to reuse the template when calling from other controllers
|
||||
conn
|
||||
|> put_view(OAuthView)
|
||||
|> render("oob_authorization_created.html", %{auth: auth})
|
||||
end
|
||||
|
||||
def after_create_authorization(%Plug.Conn{} = conn, %Authorization{} = auth, %{
|
||||
|
|
|
@ -90,6 +90,16 @@ def post_chat_message(
|
|||
conn
|
||||
|> put_view(MessageReferenceView)
|
||||
|> render("show.json", chat_message_reference: cm_ref)
|
||||
else
|
||||
{:reject, message} ->
|
||||
conn
|
||||
|> put_status(:unprocessable_entity)
|
||||
|> json(%{error: message})
|
||||
|
||||
{:error, message} ->
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(%{error: message})
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -96,6 +96,50 @@ def rich_media_get(url) do
|
|||
@rich_media_options
|
||||
end
|
||||
|
||||
Pleroma.HTTP.get(url, headers, adapter: options)
|
||||
head_check =
|
||||
case Pleroma.HTTP.head(url, headers, adapter: options) do
|
||||
# If the HEAD request didn't reach the server for whatever reason,
|
||||
# we assume the GET that comes right after won't either
|
||||
{:error, _} = e ->
|
||||
e
|
||||
|
||||
{:ok, %Tesla.Env{status: 200, headers: headers}} ->
|
||||
with :ok <- check_content_type(headers),
|
||||
:ok <- check_content_length(headers),
|
||||
do: :ok
|
||||
|
||||
_ ->
|
||||
:ok
|
||||
end
|
||||
|
||||
with :ok <- head_check, do: Pleroma.HTTP.get(url, headers, adapter: options)
|
||||
end
|
||||
|
||||
defp check_content_type(headers) do
|
||||
case List.keyfind(headers, "content-type", 0) do
|
||||
{_, content_type} ->
|
||||
case Plug.Conn.Utils.media_type(content_type) do
|
||||
{:ok, "text", "html", _} -> :ok
|
||||
_ -> {:error, {:content_type, content_type}}
|
||||
end
|
||||
|
||||
_ ->
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
@max_body @rich_media_options[:max_body]
|
||||
defp check_content_length(headers) do
|
||||
case List.keyfind(headers, "content-length", 0) do
|
||||
{_, maybe_content_length} ->
|
||||
case Integer.parse(maybe_content_length) do
|
||||
{content_length, ""} when content_length <= @max_body -> :ok
|
||||
{_, ""} -> {:error, :body_too_large}
|
||||
_ -> :ok
|
||||
end
|
||||
|
||||
_ ->
|
||||
:ok
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -31,6 +31,14 @@ defp get_cached_or_parse(url) do
|
|||
{:ok, _data} = res ->
|
||||
res
|
||||
|
||||
{:error, :body_too_large} = e ->
|
||||
e
|
||||
|
||||
{:error, {:content_type, _}} = e ->
|
||||
e
|
||||
|
||||
# The TTL is not set for the errors above, since they are unlikely to change
|
||||
# with time
|
||||
{:error, _} = e ->
|
||||
ttl = Pleroma.Config.get([:rich_media, :failure_backoff], 60_000)
|
||||
Cachex.expire(:rich_media_cache, url, ttl)
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
<h1>Successfully authorized</h1>
|
||||
<h2>Token code is <%= @auth.token %></h2>
|
||||
<h2>Token code is <br><%= @auth.token %></h2>
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
<h1>Authorization exists</h1>
|
||||
<h2>Access token is <%= @token.token %></h2>
|
||||
<h2>Access token is <br><%= @token.token %></h2>
|
||||
|
|
2
mix.exs
2
mix.exs
|
@ -4,7 +4,7 @@ defmodule Pleroma.Mixfile do
|
|||
def project do
|
||||
[
|
||||
app: :pleroma,
|
||||
version: version("2.1.1"),
|
||||
version: version("2.1.2"),
|
||||
elixir: "~> 1.9",
|
||||
elixirc_paths: elixirc_paths(Mix.env()),
|
||||
compilers: [:phoenix, :gettext] ++ Mix.compilers(),
|
||||
|
|
|
@ -1 +1 @@
|
|||
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,user-scalable=no"><title>Pleroma</title><!--server-generated-meta--><link rel=icon type=image/png href=/favicon.png><link href=/static/css/app.77b1644622e3bae24b6b.css rel=stylesheet><link href=/static/fontello.1599568314856.css rel=stylesheet></head><body class=hidden><noscript>To use Pleroma, please enable JavaScript.</noscript><div id=app></div><script type=text/javascript src=/static/js/vendors~app.90c4af83c1ae68f4cd95.js></script><script type=text/javascript src=/static/js/app.55d173dc5e39519aa518.js></script></body></html>
|
||||
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,user-scalable=no"><title>Pleroma</title><!--server-generated-meta--><link rel=icon type=image/png href=/favicon.png><link href=/static/css/app.77b1644622e3bae24b6b.css rel=stylesheet><link href=/static/fontello.1600365488745.css rel=stylesheet></head><body class=hidden><noscript>To use Pleroma, please enable JavaScript.</noscript><div id=app></div><script type=text/javascript src=/static/js/vendors~app.90c4af83c1ae68f4cd95.js></script><script type=text/javascript src=/static/js/app.826c44232e0a76bbd9ba.js></script></body></html>
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -92,6 +92,8 @@
|
|||
|
||||
<glyph glyph-name="block" unicode="" d="M732 359q0 90-48 164l-421-420q76-50 166-50 62 0 118 25t96 65 65 97 24 119z m-557-167l421 421q-75 50-167 50-83 0-153-40t-110-111-41-153q0-91 50-167z m682 167q0-88-34-168t-91-137-137-92-166-34-167 34-137 92-91 137-34 168 34 167 91 137 137 91 167 34 166-34 137-91 91-137 34-167z" horiz-adv-x="857.1" />
|
||||
|
||||
<glyph glyph-name="megaphone" unicode="" d="M929 500q29 0 50-21t21-51-21-50-50-21v-214q0-29-22-50t-50-22q-233 194-453 212-32-10-51-36t-17-57 22-51q-11-19-13-37t4-32 19-31 26-28 35-28q-17-32-63-46t-94-7-73 31q-4 13-17 49t-18 53-12 50-9 56 2 55 12 62h-68q-36 0-63 26t-26 63v107q0 37 26 63t63 26h268q243 0 500 215 29 0 50-22t22-50v-214z m-72-337v532q-220-168-428-191v-151q210-23 428-190z" horiz-adv-x="1000" />
|
||||
|
||||
<glyph glyph-name="spin3" unicode="" d="M494 857c-266 0-483-210-494-472-1-19 13-20 13-20l84 0c16 0 19 10 19 18 10 199 176 358 378 358 107 0 205-45 273-118l-58-57c-11-12-11-27 5-31l247-50c21-5 46 11 37 44l-58 227c-2 9-16 22-29 13l-65-60c-89 91-214 148-352 148z m409-508c-16 0-19-10-19-18-10-199-176-358-377-358-108 0-205 45-274 118l59 57c10 12 10 27-5 31l-248 50c-21 5-46-11-37-44l58-227c2-9 16-22 30-13l64 60c89-91 214-148 353-148 265 0 482 210 493 473 1 18-13 19-13 19l-84 0z" horiz-adv-x="1000" />
|
||||
|
||||
<glyph glyph-name="spin4" unicode="" d="M498 857c-114 0-228-39-320-116l0 0c173 140 428 130 588-31 134-134 164-332 89-495-10-29-5-50 12-68 21-20 61-23 84 0 3 3 12 15 15 24 71 180 33 393-112 539-99 98-228 147-356 147z m-409-274c-14 0-29-5-39-16-3-3-13-15-15-24-71-180-34-393 112-539 185-185 479-195 676-31l0 0c-173-140-428-130-589 31-134 134-163 333-89 495 11 29 6 50-12 68-11 11-27 17-44 16z" horiz-adv-x="1001" />
|
Before (image error) Size: 28 KiB After (image error) Size: 28 KiB |
Binary file not shown.
BIN
priv/static/static/font/fontello.1600365488745.woff
Normal file
BIN
priv/static/static/font/fontello.1600365488745.woff
Normal file
Binary file not shown.
BIN
priv/static/static/font/fontello.1600365488745.woff2
Normal file
BIN
priv/static/static/font/fontello.1600365488745.woff2
Normal file
Binary file not shown.
Binary file not shown.
|
@ -405,6 +405,12 @@
|
|||
"css": "block",
|
||||
"code": 59434,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "3e674995cacc2b09692c096ea7eb6165",
|
||||
"css": "megaphone",
|
||||
"code": 59435,
|
||||
"src": "fontawesome"
|
||||
}
|
||||
]
|
||||
}
|
Binary file not shown.
Binary file not shown.
BIN
priv/static/static/js/2.e852a6b4b3bba752b838.js.map
Normal file
BIN
priv/static/static/js/2.e852a6b4b3bba752b838.js.map
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
priv/static/static/js/app.826c44232e0a76bbd9ba.js
Normal file
BIN
priv/static/static/js/app.826c44232e0a76bbd9ba.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/app.826c44232e0a76bbd9ba.js.map
Normal file
BIN
priv/static/static/js/app.826c44232e0a76bbd9ba.js.map
Normal file
Binary file not shown.
Binary file not shown.
|
@ -99,30 +99,30 @@ test "accepts valid tokens", state do
|
|||
test "accepts the 'user' stream", %{token: token} = _state do
|
||||
assert {:ok, _} = start_socket("?stream=user&access_token=#{token.token}")
|
||||
|
||||
assert capture_log(fn ->
|
||||
assert {:error, {401, _}} = start_socket("?stream=user")
|
||||
Process.sleep(30)
|
||||
end) =~ ":badarg"
|
||||
capture_log(fn ->
|
||||
assert {:error, {401, _}} = start_socket("?stream=user")
|
||||
Process.sleep(30)
|
||||
end)
|
||||
end
|
||||
|
||||
test "accepts the 'user:notification' stream", %{token: token} = _state do
|
||||
assert {:ok, _} = start_socket("?stream=user:notification&access_token=#{token.token}")
|
||||
|
||||
assert capture_log(fn ->
|
||||
assert {:error, {401, _}} = start_socket("?stream=user:notification")
|
||||
Process.sleep(30)
|
||||
end) =~ ":badarg"
|
||||
capture_log(fn ->
|
||||
assert {:error, {401, _}} = start_socket("?stream=user:notification")
|
||||
Process.sleep(30)
|
||||
end)
|
||||
end
|
||||
|
||||
test "accepts valid token on Sec-WebSocket-Protocol header", %{token: token} do
|
||||
assert {:ok, _} = start_socket("?stream=user", [{"Sec-WebSocket-Protocol", token.token}])
|
||||
|
||||
assert capture_log(fn ->
|
||||
assert {:error, {401, _}} =
|
||||
start_socket("?stream=user", [{"Sec-WebSocket-Protocol", "I am a friend"}])
|
||||
capture_log(fn ->
|
||||
assert {:error, {401, _}} =
|
||||
start_socket("?stream=user", [{"Sec-WebSocket-Protocol", "I am a friend"}])
|
||||
|
||||
Process.sleep(30)
|
||||
end) =~ ":badarg"
|
||||
Process.sleep(30)
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1436,4 +1436,21 @@ def post(url, query, body, headers) do
|
|||
inspect(headers)
|
||||
}"}
|
||||
end
|
||||
|
||||
# Most of the rich media mocks are missing HEAD requests, so we just return 404.
|
||||
@rich_media_mocks [
|
||||
"https://example.com/ogp",
|
||||
"https://example.com/ogp-missing-data",
|
||||
"https://example.com/twitter-card"
|
||||
]
|
||||
def head(url, _query, _body, _headers) when url in @rich_media_mocks do
|
||||
{:ok, %Tesla.Env{status: 404, body: ""}}
|
||||
end
|
||||
|
||||
def head(url, query, body, headers) do
|
||||
{:error,
|
||||
"Mock response not implemented for HEAD #{inspect(url)}, #{query}, #{inspect(body)}, #{
|
||||
inspect(headers)
|
||||
}"}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -440,6 +440,45 @@ test "it sends a welcome chat message if it is set" do
|
|||
assert activity.actor == welcome_user.ap_id
|
||||
end
|
||||
|
||||
setup do:
|
||||
clear_config(:mrf_simple,
|
||||
media_removal: [],
|
||||
media_nsfw: [],
|
||||
federated_timeline_removal: [],
|
||||
report_removal: [],
|
||||
reject: [],
|
||||
followers_only: [],
|
||||
accept: [],
|
||||
avatar_removal: [],
|
||||
banner_removal: [],
|
||||
reject_deletes: []
|
||||
)
|
||||
|
||||
setup do:
|
||||
clear_config(:mrf,
|
||||
policies: [
|
||||
Pleroma.Web.ActivityPub.MRF.SimplePolicy
|
||||
]
|
||||
)
|
||||
|
||||
test "it sends a welcome chat message when Simple policy applied to local instance" do
|
||||
Pleroma.Config.put([:mrf_simple, :media_nsfw], ["localhost"])
|
||||
|
||||
welcome_user = insert(:user)
|
||||
Pleroma.Config.put([:welcome, :chat_message, :enabled], true)
|
||||
Pleroma.Config.put([:welcome, :chat_message, :sender_nickname], welcome_user.nickname)
|
||||
Pleroma.Config.put([:welcome, :chat_message, :message], "Hello, this is a chat message")
|
||||
|
||||
cng = User.register_changeset(%User{}, @full_user_data)
|
||||
{:ok, registered_user} = User.register(cng)
|
||||
ObanHelpers.perform_all()
|
||||
|
||||
activity = Repo.one(Pleroma.Activity)
|
||||
assert registered_user.ap_id in activity.recipients
|
||||
assert Object.normalize(activity).data["content"] =~ "chat message"
|
||||
assert activity.actor == welcome_user.ap_id
|
||||
end
|
||||
|
||||
test "it sends a welcome email message if it is set" do
|
||||
welcome_user = insert(:user)
|
||||
Pleroma.Config.put([:welcome, :email, :enabled], true)
|
||||
|
|
|
@ -1773,6 +1773,14 @@ test "public timeline with default reply_visibility `self`", %{users: %{u1: user
|
|||
|> Enum.map(& &1.id)
|
||||
|
||||
assert activities_ids == []
|
||||
|
||||
activities_ids =
|
||||
%{}
|
||||
|> Map.put(:reply_visibility, "self")
|
||||
|> Map.put(:reply_filtering_user, nil)
|
||||
|> ActivityPub.fetch_public_activities()
|
||||
|
||||
assert activities_ids == []
|
||||
end
|
||||
|
||||
test "home timeline", %{users: %{u1: user}} do
|
||||
|
|
|
@ -26,7 +26,7 @@ test "when given an `object_data` in meta, Federation will receive a the origina
|
|||
{
|
||||
Pleroma.Web.ActivityPub.MRF,
|
||||
[],
|
||||
[filter: fn o -> {:ok, o} end]
|
||||
[pipeline_filter: fn o, m -> {:ok, o, m} end]
|
||||
},
|
||||
{
|
||||
Pleroma.Web.ActivityPub.ActivityPub,
|
||||
|
@ -51,7 +51,7 @@ test "when given an `object_data` in meta, Federation will receive a the origina
|
|||
Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta)
|
||||
|
||||
assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta))
|
||||
assert_called(Pleroma.Web.ActivityPub.MRF.filter(activity))
|
||||
assert_called(Pleroma.Web.ActivityPub.MRF.pipeline_filter(activity, meta))
|
||||
assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta))
|
||||
assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta))
|
||||
refute called(Pleroma.Web.Federator.publish(activity))
|
||||
|
@ -68,7 +68,7 @@ test "it goes through validation, filtering, persisting, side effects and federa
|
|||
{
|
||||
Pleroma.Web.ActivityPub.MRF,
|
||||
[],
|
||||
[filter: fn o -> {:ok, o} end]
|
||||
[pipeline_filter: fn o, m -> {:ok, o, m} end]
|
||||
},
|
||||
{
|
||||
Pleroma.Web.ActivityPub.ActivityPub,
|
||||
|
@ -93,7 +93,7 @@ test "it goes through validation, filtering, persisting, side effects and federa
|
|||
Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta)
|
||||
|
||||
assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta))
|
||||
assert_called(Pleroma.Web.ActivityPub.MRF.filter(activity))
|
||||
assert_called(Pleroma.Web.ActivityPub.MRF.pipeline_filter(activity, meta))
|
||||
assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta))
|
||||
assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta))
|
||||
assert_called(Pleroma.Web.Federator.publish(activity))
|
||||
|
@ -109,7 +109,7 @@ test "it goes through validation, filtering, persisting, side effects without fe
|
|||
{
|
||||
Pleroma.Web.ActivityPub.MRF,
|
||||
[],
|
||||
[filter: fn o -> {:ok, o} end]
|
||||
[pipeline_filter: fn o, m -> {:ok, o, m} end]
|
||||
},
|
||||
{
|
||||
Pleroma.Web.ActivityPub.ActivityPub,
|
||||
|
@ -131,7 +131,7 @@ test "it goes through validation, filtering, persisting, side effects without fe
|
|||
Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta)
|
||||
|
||||
assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta))
|
||||
assert_called(Pleroma.Web.ActivityPub.MRF.filter(activity))
|
||||
assert_called(Pleroma.Web.ActivityPub.MRF.pipeline_filter(activity, meta))
|
||||
assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta))
|
||||
assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta))
|
||||
end
|
||||
|
@ -148,7 +148,7 @@ test "it goes through validation, filtering, persisting, side effects without fe
|
|||
{
|
||||
Pleroma.Web.ActivityPub.MRF,
|
||||
[],
|
||||
[filter: fn o -> {:ok, o} end]
|
||||
[pipeline_filter: fn o, m -> {:ok, o, m} end]
|
||||
},
|
||||
{
|
||||
Pleroma.Web.ActivityPub.ActivityPub,
|
||||
|
@ -170,7 +170,7 @@ test "it goes through validation, filtering, persisting, side effects without fe
|
|||
Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta)
|
||||
|
||||
assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta))
|
||||
assert_called(Pleroma.Web.ActivityPub.MRF.filter(activity))
|
||||
assert_called(Pleroma.Web.ActivityPub.MRF.pipeline_filter(activity, meta))
|
||||
assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta))
|
||||
assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta))
|
||||
end
|
||||
|
|
|
@ -213,6 +213,17 @@ test "it reject messages over the local limit" do
|
|||
|
||||
assert message == :content_too_long
|
||||
end
|
||||
|
||||
test "it reject messages via MRF" do
|
||||
clear_config([:mrf_keyword, :reject], ["GNO"])
|
||||
clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy])
|
||||
|
||||
author = insert(:user)
|
||||
recipient = insert(:user)
|
||||
|
||||
assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} ==
|
||||
CommonAPI.post_chat_message(author, recipient, "GNO/Linux")
|
||||
end
|
||||
end
|
||||
|
||||
describe "unblocking" do
|
||||
|
|
|
@ -100,7 +100,7 @@ test "it fails if there is no content", %{conn: conn, user: user} do
|
|||
|> post("/api/v1/pleroma/chats/#{chat.id}/messages")
|
||||
|> json_response_and_validate_schema(400)
|
||||
|
||||
assert result
|
||||
assert %{"error" => "no_content"} == result
|
||||
end
|
||||
|
||||
test "it works with an attachment", %{conn: conn, user: user} do
|
||||
|
@ -126,6 +126,23 @@ test "it works with an attachment", %{conn: conn, user: user} do
|
|||
|
||||
assert result["attachment"]
|
||||
end
|
||||
|
||||
test "gets MRF reason when rejected", %{conn: conn, user: user} do
|
||||
clear_config([:mrf_keyword, :reject], ["GNO"])
|
||||
clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy])
|
||||
|
||||
other_user = insert(:user)
|
||||
|
||||
{:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id)
|
||||
|
||||
result =
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/api/v1/pleroma/chats/#{chat.id}/messages", %{"content" => "GNO/Linux"})
|
||||
|> json_response_and_validate_schema(422)
|
||||
|
||||
assert %{"error" => "[KeywordPolicy] Matches with rejected keyword"} == result
|
||||
end
|
||||
end
|
||||
|
||||
describe "DELETE /api/v1/pleroma/chats/:id/messages/:message_id" do
|
||||
|
|
|
@ -56,6 +56,27 @@ defmodule Pleroma.Web.RichMedia.ParserTest do
|
|||
|
||||
%{method: :get, url: "http://example.com/error"} ->
|
||||
{:error, :overload}
|
||||
|
||||
%{
|
||||
method: :head,
|
||||
url: "http://example.com/huge-page"
|
||||
} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
headers: [{"content-length", "2000001"}, {"content-type", "text/html"}]
|
||||
}
|
||||
|
||||
%{
|
||||
method: :head,
|
||||
url: "http://example.com/pdf-file"
|
||||
} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
headers: [{"content-length", "1000000"}, {"content-type", "application/pdf"}]
|
||||
}
|
||||
|
||||
%{method: :head} ->
|
||||
%Tesla.Env{status: 404, body: "", headers: []}
|
||||
end)
|
||||
|
||||
:ok
|
||||
|
@ -146,4 +167,12 @@ test "rejects invalid OGP data" do
|
|||
test "returns error if getting page was not successful" do
|
||||
assert {:error, :overload} = Parser.parse("http://example.com/error")
|
||||
end
|
||||
|
||||
test "does a HEAD request to check if the body is too large" do
|
||||
assert {:error, :body_too_large} = Parser.parse("http://example.com/huge-page")
|
||||
end
|
||||
|
||||
test "does a HEAD request to check if the body is html" do
|
||||
assert {:error, {:content_type, _}} = Parser.parse("http://example.com/pdf-file")
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue