Merge remote-tracking branch 'pleroma/develop' into merge-pleroma

This commit is contained in:
Alex Gleason 2023-09-03 10:09:05 -05:00
commit 82d99b835e
No known key found for this signature in database
GPG key ID: 7211D1F99744FBB7
18 changed files with 121 additions and 39 deletions

View file

@ -55,7 +55,7 @@ USER pleroma
COPY --from=build --chown=pleroma:0 /src/release ${HOME}
COPY ./config/docker.exs /etc/pleroma/config.exs
COPY --chown=pleroma --chmod=640 ./config/docker.exs /etc/pleroma/config.exs
COPY ./docker-entrypoint.sh ${HOME}
ENTRYPOINT ["/opt/pleroma/docker-entrypoint.sh"]

1
changelog.d/3879.fix Normal file
View file

@ -0,0 +1 @@
fix not being able to fetch flash file from remote instance

View file

@ -0,0 +1 @@
CommonAPI: Prevent users from accessing media of other users by creating a status with reused attachment ID

View file

@ -0,0 +1 @@
Disable XML entity resolution completely to fix a dos vulnerability

View file

@ -0,0 +1 @@
- Fix config ownership in dockerfile to pass restriction test

0
changelog.d/lint.skip Normal file
View file

View file

@ -40,7 +40,11 @@ defp with_media_attachments(
%{changes: %{params: %{"media_ids" => media_ids} = params}} = changeset
)
when is_list(media_ids) do
media_attachments = Utils.attachments_from_ids(%{media_ids: media_ids})
media_attachments =
Utils.attachments_from_ids(
%{media_ids: media_ids},
User.get_cached_by_id(changeset.data.user_id)
)
params =
params

View file

@ -34,6 +34,7 @@ def block(blocker, blocked) do
def post_chat_message(%User{} = user, %User{} = recipient, content, opts \\ []) do
with maybe_attachment <- opts[:media_id] && Object.get_by_id(opts[:media_id]),
:ok <- validate_chat_attachment_attribution(maybe_attachment, user),
:ok <- validate_chat_content_length(content, !!maybe_attachment),
{_, {:ok, chat_message_data, _meta}} <-
{:build_object,
@ -72,6 +73,17 @@ defp format_chat_content(content) do
text
end
defp validate_chat_attachment_attribution(nil, _), do: :ok
defp validate_chat_attachment_attribution(attachment, user) do
with :ok <- Object.authorize_access(attachment, user) do
:ok
else
e ->
e
end
end
defp validate_chat_content_length(_, true), do: :ok
defp validate_chat_content_length(nil, false), do: {:error, :no_content}

View file

@ -162,7 +162,7 @@ defp full_payload(%{status: status, summary: summary} = draft) do
end
defp attachments(%{params: params} = draft) do
attachments = Utils.attachments_from_ids(params)
attachments = Utils.attachments_from_ids(params, draft.user)
draft = %__MODULE__{draft | attachments: attachments}
case Utils.validate_attachments_count(attachments) do

View file

@ -32,21 +32,21 @@ defmodule Pleroma.Web.CommonAPI.Utils do
tk tl tn to tr ts tt tw ty ug uk ur uz ve vi vo wa wo xh yi yo za zh zu ast ckb kab kmr zgh
)
def attachments_from_ids(%{media_ids: ids, descriptions: desc}) do
attachments_from_ids_descs(ids, desc)
def attachments_from_ids(%{media_ids: ids, descriptions: desc}, user) do
attachments_from_ids_descs(ids, desc, user)
end
def attachments_from_ids(%{media_ids: ids}) do
attachments_from_ids_no_descs(ids)
def attachments_from_ids(%{media_ids: ids}, user) do
attachments_from_ids_no_descs(ids, user)
end
def attachments_from_ids(_), do: []
def attachments_from_ids(_, _), do: []
def attachments_from_ids_no_descs([]), do: []
def attachments_from_ids_no_descs([], _), do: []
def attachments_from_ids_no_descs(ids) do
def attachments_from_ids_no_descs(ids, user) do
Enum.map(ids, fn media_id ->
case get_attachment(media_id) do
case get_attachment(media_id, user) do
%Object{data: data} -> data
_ -> nil
end
@ -54,22 +54,23 @@ def attachments_from_ids_no_descs(ids) do
|> Enum.reject(&is_nil/1)
end
def attachments_from_ids_descs([], _), do: []
def attachments_from_ids_descs([], _, _), do: []
def attachments_from_ids_descs(ids, descs_str) do
def attachments_from_ids_descs(ids, descs_str, user) do
{_, descs} = Jason.decode(descs_str)
Enum.map(ids, fn media_id ->
with %Object{data: data} <- get_attachment(media_id) do
with %Object{data: data} <- get_attachment(media_id, user) do
Map.put(data, "name", descs[media_id])
end
end)
|> Enum.reject(&is_nil/1)
end
defp get_attachment(media_id) do
defp get_attachment(media_id, user) do
with %Object{data: data} = object <- Repo.get(Object, media_id),
%{"type" => type} when type in Pleroma.Constants.upload_object_types() <- data do
%{"type" => type} when type in Pleroma.Constants.upload_object_types() <- data,
:ok <- Object.authorize_access(object, user) do
object
else
_ -> nil

View file

@ -94,18 +94,26 @@ defp csp_string do
img_src = "img-src 'self' data: blob:"
media_src = "media-src 'self'"
connect_src = ["connect-src 'self' blob: ", static_url, ?\s, websocket_url]
# Strict multimedia CSP enforcement only when MediaProxy is enabled
{img_src, media_src} =
{img_src, media_src, connect_src} =
if Config.get([:media_proxy, :enabled]) &&
!Config.get([:media_proxy, :proxy_opts, :redirect_on_failure]) do
sources = build_csp_multimedia_source_list()
{[img_src, sources], [media_src, sources]}
else
{[img_src, " https:"], [media_src, " https:"]}
end
connect_src = ["connect-src 'self' blob: ", static_url, ?\s, websocket_url]
{
[img_src, sources],
[media_src, sources],
[connect_src, sources]
}
else
{
[img_src, " https:"],
[media_src, " https:"],
[connect_src, " https:"]
}
end
connect_src =
if Config.get(:env) == :dev do

View file

@ -31,7 +31,7 @@ def parse_document(text) do
|> :binary.bin_to_list()
|> :xmerl_scan.string(
quiet: true,
fetch_fun: fn _, _ -> raise "Resolving external entities not supported" end
allow_entities: false
)
{:ok, doc}

15
test/fixtures/xml_billion_laughs.xml vendored Normal file
View file

@ -0,0 +1,15 @@
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ELEMENT lolz (#PCDATA)>
<!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
<!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
<!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
<!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
<!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
<!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>

View file

@ -586,46 +586,61 @@ test "returns recipients when object not found" do
end
end
describe "attachments_from_ids_descs/2" do
describe "attachments_from_ids_descs/3" do
test "returns [] when attachment ids is empty" do
assert Utils.attachments_from_ids_descs([], "{}") == []
assert Utils.attachments_from_ids_descs([], "{}", nil) == []
end
test "returns list attachments with desc" do
object = insert(:attachment)
user = insert(:user)
object = insert(:attachment, %{user: user})
desc = Jason.encode!(%{object.id => "test-desc"})
assert Utils.attachments_from_ids_descs(["#{object.id}", "34"], desc) == [
assert Utils.attachments_from_ids_descs(["#{object.id}", "34"], desc, user) == [
Map.merge(object.data, %{"name" => "test-desc"})
]
end
end
describe "attachments_from_ids/1" do
describe "attachments_from_ids/2" do
test "returns attachments with descs" do
object = insert(:attachment)
user = insert(:user)
object = insert(:attachment, %{user: user})
desc = Jason.encode!(%{object.id => "test-desc"})
assert Utils.attachments_from_ids(%{
media_ids: ["#{object.id}"],
descriptions: desc
}) == [
assert Utils.attachments_from_ids(
%{
media_ids: ["#{object.id}"],
descriptions: desc
},
user
) == [
Map.merge(object.data, %{"name" => "test-desc"})
]
end
test "returns attachments without descs" do
object = insert(:attachment)
assert Utils.attachments_from_ids(%{media_ids: ["#{object.id}"]}) == [object.data]
user = insert(:user)
object = insert(:attachment, %{user: user})
assert Utils.attachments_from_ids(%{media_ids: ["#{object.id}"]}, user) == [object.data]
end
test "returns [] when not pass media_ids" do
assert Utils.attachments_from_ids(%{}) == []
assert Utils.attachments_from_ids(%{}, nil) == []
end
test "returns [] when media_ids not belong to current user" do
user = insert(:user)
user2 = insert(:user)
object = insert(:attachment, %{user: user})
assert Utils.attachments_from_ids(%{media_ids: ["#{object.id}"]}, user2) == []
end
test "checks that the object is of upload type" do
object = insert(:note)
assert Utils.attachments_from_ids(%{media_ids: ["#{object.id}"]}) == []
assert Utils.attachments_from_ids(%{media_ids: ["#{object.id}"]}, nil) == []
end
end

View file

@ -280,6 +280,24 @@ test "it reject messages via MRF" do
assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} ==
CommonAPI.post_chat_message(author, recipient, "GNO/Linux")
end
test "it reject messages with attachments not belonging to user" do
author = insert(:user)
not_author = insert(:user)
recipient = author
attachment = insert(:attachment, %{user: not_author})
{:error, message} =
CommonAPI.post_chat_message(
author,
recipient,
"123",
media_id: attachment.id
)
assert message == :forbidden
end
end
describe "unblocking" do

View file

@ -48,7 +48,7 @@ test "A scheduled activity with a media attachment" do
id: to_string(scheduled_activity.id),
media_attachments:
%{media_ids: [upload.id]}
|> Utils.attachments_from_ids()
|> Utils.attachments_from_ids(user)
|> Enum.map(&StatusView.render("attachment.json", %{attachment: &1})),
params: %{
in_reply_to_id: to_string(activity.id),

View file

@ -24,7 +24,7 @@ test "it displays a chat message" do
filename: "an_image.jpg"
}
{:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
{:ok, upload} = ActivityPub.upload(file, actor: recipient.ap_id)
{:ok, activity} =
CommonAPI.post_chat_message(user, recipient, "kippis :firefox:", idempotency_key: "123")

View file

@ -3,6 +3,11 @@ defmodule Pleroma.Web.XMLTest do
alias Pleroma.Web.XML
test "refuses to parse any entities from XML" do
data = File.read!("test/fixtures/xml_billion_laughs.xml")
assert(:error == XML.parse_document(data))
end
test "refuses to load external entities from XML" do
data = File.read!("test/fixtures/xml_external_entities.xml")
assert(:error == XML.parse_document(data))