aboutsummaryrefslogtreecommitdiff
path: root/patches/8(2.5.5).diff
blob: fef991f32097cc6259d72152dc7f1aabe334c318 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9d9aadc6e8a0162d8944622f783a6301fefd6cfa..32ec440de55b707d01be37e21f1517542c9cf7d9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,7 +14,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 
 ### Removed
 
-## 2.5.54
+## 2.5.5
+
+## Security
+- Prevent users from accessing media of other users by creating a status with reused attachment ID
+
+## 2.5.4
 
 ## Security
 - Fix XML External Entity (XXE) loading vulnerability allowing to fetch arbitary files from the server's filesystem
diff --git a/changelog.d/check-attachment-attribution.security b/changelog.d/check-attachment-attribution.security
new file mode 100644
index 0000000000000000000000000000000000000000..e0e46525b38d15e58423debbf9d799e85276d0ca
--- /dev/null
+++ b/changelog.d/check-attachment-attribution.security
@@ -0,0 +1 @@
+CommonAPI: Prevent users from accessing media of other users by creating a status with reused attachment ID
diff --git a/lib/pleroma/scheduled_activity.ex b/lib/pleroma/scheduled_activity.ex
index a7be585123d4be67e7513626b07c2e33c8b18388..0ed51ad07d43c0cc434e532852ce6a7ec1089d10 100644
--- a/lib/pleroma/scheduled_activity.ex
+++ b/lib/pleroma/scheduled_activity.ex
@@ -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
diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex
index 89cc0d6fe82acfd418e8e5928bc4b44d72b17f79..44eb00075ee694005241c1eabfbb846e4668d665 100644
--- a/lib/pleroma/web/common_api.ex
+++ b/lib/pleroma/web/common_api.ex
@@ -33,6 +33,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,
@@ -71,6 +72,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}
 
diff --git a/lib/pleroma/web/common_api/activity_draft.ex b/lib/pleroma/web/common_api/activity_draft.ex
index 9af635da8abafb9d9a4c1b59c00cc63261955d48..63ed48a27bbd4a4f2fdfd97776acadcf0fa72c74 100644
--- a/lib/pleroma/web/common_api/activity_draft.ex
+++ b/lib/pleroma/web/common_api/activity_draft.ex
@@ -111,7 +111,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
diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex
index ff0814329583726d43ad52e6082662aee1212b45..6410815ea89153a65f20bacc24a5a5aed82a1aa0 100644
--- a/lib/pleroma/web/common_api/utils.ex
+++ b/lib/pleroma/web/common_api/utils.ex
@@ -23,21 +23,21 @@ defmodule Pleroma.Web.CommonAPI.Utils do
   require Logger
   require Pleroma.Constants
 
-  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
@@ -45,21 +45,26 @@ 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
-    Repo.get(Object, media_id)
+  defp get_attachment(media_id, user) do
+    with %Object{data: _data} = object <- Repo.get(Object, media_id),
+         :ok <- Object.authorize_access(object, user) do
+      object
+    else
+      _ -> nil
+    end
   end
 
   @spec get_to_and_cc(ActivityDraft.t()) :: {list(String.t()), list(String.t())}
diff --git a/mix.exs b/mix.exs
index 12f721364dd75744651e5044936d195684d8cf08..e2aac0fc541f2820841a16feff06b058b18ed2ec 100644
--- a/mix.exs
+++ b/mix.exs
@@ -4,7 +4,7 @@ defmodule Pleroma.Mixfile do
   def project do
     [
       app: :pleroma,
-      version: version("2.5.4"),
+      version: version("2.5.5"),
       elixir: "~> 1.11",
       elixirc_paths: elixirc_paths(Mix.env()),
       compilers: [:phoenix, :gettext] ++ Mix.compilers(),
diff --git a/test/pleroma/web/common_api/utils_test.exs b/test/pleroma/web/common_api/utils_test.exs
index d309c6deda789fd7818b29e95425b77413b06f7c..c52d3e9c557b6040bb457f40d9ebd02c31975a64 100644
--- a/test/pleroma/web/common_api/utils_test.exs
+++ b/test/pleroma/web/common_api/utils_test.exs
@@ -586,41 +586,56 @@ 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(:note)
+      user = insert(:user)
+      object = insert(:note, %{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(:note)
+      user = insert(:user)
+      object = insert(:note, %{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(:note)
-      assert Utils.attachments_from_ids(%{media_ids: ["#{object.id}"]}) == [object.data]
+      user = insert(:user)
+      object = insert(:note, %{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
   end
 
diff --git a/test/pleroma/web/common_api_test.exs b/test/pleroma/web/common_api_test.exs
index 5c9103e9fc742b8082a432d8ac9ca2ce1929ec32..e60691995c51bdd829f0f67a3abbddc9fe919db9 100644
--- a/test/pleroma/web/common_api_test.exs
+++ b/test/pleroma/web/common_api_test.exs
@@ -279,6 +279,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
diff --git a/test/pleroma/web/mastodon_api/views/scheduled_activity_view_test.exs b/test/pleroma/web/mastodon_api/views/scheduled_activity_view_test.exs
index e5e510d33b88244b9b088ed93b98bfb2c411dd8b..07a65a3bc1bd09ece6afa2557a719b7550dfae2a 100644
--- a/test/pleroma/web/mastodon_api/views/scheduled_activity_view_test.exs
+++ b/test/pleroma/web/mastodon_api/views/scheduled_activity_view_test.exs
@@ -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),
diff --git a/test/pleroma/web/pleroma_api/views/chat_message_reference_view_test.exs b/test/pleroma/web/pleroma_api/views/chat_message_reference_view_test.exs
index 017c9c5c0d2fdf4686d920ea7f05084a6ddf44d7..7ab3f5acdc7971f9ac324db07be30dbff60d680a 100644
--- a/test/pleroma/web/pleroma_api/views/chat_message_reference_view_test.exs
+++ b/test/pleroma/web/pleroma_api/views/chat_message_reference_view_test.exs
@@ -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")