summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--installation/pleroma.nginx1
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub.ex40
-rw-r--r--lib/pleroma/web/mastodon_api/mastodon_api_controller.ex9
-rw-r--r--lib/pleroma/web/mastodon_api/views/account_view.ex10
-rw-r--r--lib/pleroma/web/streamer.ex21
-rw-r--r--priv/repo/migrations/20180829082446_add_recipients_to_and_cc_fields_to_activities.exs13
-rw-r--r--priv/repo/migrations/20180829182612_activities_add_to_cc_indices.exs8
-rw-r--r--priv/repo/migrations/20180829183529_remove_recipients_to_and_cc_fields_from_activities.exs10
-rw-r--r--test/web/mastodon_api/mastodon_api_controller_test.exs24
9 files changed, 125 insertions, 11 deletions
diff --git a/installation/pleroma.nginx b/installation/pleroma.nginx
index 78327594f..a333d116c 100644
--- a/installation/pleroma.nginx
+++ b/installation/pleroma.nginx
@@ -77,6 +77,7 @@ server {
add_header X-Content-Type-Options nosniff;
add_header Referrer-Policy same-origin;
add_header X-Download-Options noopen;
+ add_header Content-Security-Policy "default-src 'none'; base-uri 'self'; form-action 'self'; img-src 'self' data: https:; media-src 'self' https:; style-src 'self' 'unsafe-inline'; font-src 'self'; script-src 'self'; connect-src 'self' wss://example.tld; upgrade-insecure-requests;";
# Uncomment this only after you get HTTPS working.
# add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index 68b398786..e6c2dc9cf 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -14,8 +14,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
# For Announce activities, we filter the recipients based on following status for any actors
# that match actual users. See issue #164 for more information about why this is necessary.
- def get_recipients(%{"type" => "Announce"} = data) do
- recipients = (data["to"] || []) ++ (data["cc"] || [])
+ defp get_recipients(%{"type" => "Announce"} = data) do
+ to = data["to"] || []
+ cc = data["cc"] || []
+ recipients = to ++ cc
actor = User.get_cached_by_ap_id(data["actor"])
recipients
@@ -28,10 +30,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
User.following?(user, actor)
end
end)
+
+ {recipients, to, cc}
end
- def get_recipients(data) do
- (data["to"] || []) ++ (data["cc"] || [])
+ defp get_recipients(data) do
+ to = data["to"] || []
+ cc = data["cc"] || []
+ recipients = to ++ cc
+ {recipients, to, cc}
end
defp check_actor_is_active(actor) do
@@ -53,12 +60,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
:ok <- check_actor_is_active(map["actor"]),
{:ok, map} <- MRF.filter(map),
:ok <- insert_full_object(map) do
+ {recipients, _, _} = get_recipients(map)
+
{:ok, activity} =
Repo.insert(%Activity{
data: map,
local: local,
actor: map["actor"],
- recipients: get_recipients(map)
+ recipients: recipients
})
Notification.create_notifications(activity)
@@ -399,6 +408,20 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp restrict_tag(query, _), do: query
+ defp restrict_to_cc(query, recipients_to, recipients_cc) do
+ from(
+ activity in query,
+ where:
+ fragment(
+ "(?->'to' \\?| ?) or (?->'cc' \\?| ?)",
+ activity.data,
+ ^recipients_to,
+ activity.data,
+ ^recipients_cc
+ )
+ )
+ end
+
defp restrict_recipients(query, [], _user), do: query
defp restrict_recipients(query, recipients, nil) do
@@ -540,6 +563,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> Enum.reverse()
end
+ def fetch_activities_bounded(recipients_to, recipients_cc, opts \\ %{}) do
+ fetch_activities_query([], opts)
+ |> restrict_to_cc(recipients_to, recipients_cc)
+ |> Repo.all()
+ |> Enum.reverse()
+ end
+
def upload(file) do
data = Upload.store(file, Application.get_env(:pleroma, :instance)[:dedupe_media])
Repo.insert(%Object{data: data})
diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
index f482de6fd..c90f9fa05 100644
--- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
+++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
@@ -850,9 +850,14 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|> Map.put("type", "Create")
|> Map.put("blocking_user", user)
- # adding title is a hack to not make empty lists function like a public timeline
+ # we must filter the following list for the user to avoid leaking statuses the user
+ # does not actually have permission to see (for more info, peruse security issue #270).
+ following_to =
+ following
+ |> Enum.filter(fn x -> x in user.following end)
+
activities =
- ActivityPub.fetch_activities([title | following], params)
+ ActivityPub.fetch_activities_bounded(following_to, following, params)
|> Enum.reverse()
conn
diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex
index d9edcae7f..133cae3b5 100644
--- a/lib/pleroma/web/mastodon_api/views/account_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/account_view.ex
@@ -28,7 +28,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
%{
id: to_string(user.id),
- username: hd(String.split(user.nickname, "@")),
+ username: username_from_nickname(user.nickname),
acct: user.nickname,
display_name: user.name || user.nickname,
locked: user_info.locked,
@@ -56,7 +56,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
%{
id: to_string(user.id),
acct: user.nickname,
- username: hd(String.split(user.nickname, "@")),
+ username: username_from_nickname(user.nickname),
url: user.ap_id
}
end
@@ -76,4 +76,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
def render("relationships.json", %{user: user, targets: targets}) do
render_many(targets, AccountView, "relationship.json", user: user, as: :target)
end
+
+ defp username_from_nickname(string) when is_binary(string) do
+ hd(String.split(string, "@"))
+ end
+
+ defp username_from_nickname(_), do: nil
end
diff --git a/lib/pleroma/web/streamer.ex b/lib/pleroma/web/streamer.ex
index c61bad830..6b6d40346 100644
--- a/lib/pleroma/web/streamer.ex
+++ b/lib/pleroma/web/streamer.ex
@@ -1,7 +1,8 @@
defmodule Pleroma.Web.Streamer do
use GenServer
require Logger
- alias Pleroma.{User, Notification, Activity, Object}
+ alias Pleroma.{User, Notification, Activity, Object, Repo}
+ alias Pleroma.Web.ActivityPub.ActivityPub
def init(args) do
{:ok, args}
@@ -60,8 +61,24 @@ defmodule Pleroma.Web.Streamer do
end
def handle_cast(%{action: :stream, topic: "list", item: item}, topics) do
+ author = User.get_cached_by_ap_id(item.data["actor"])
+
+ # filter the recipient list if the activity is not public, see #270.
+ recipient_lists =
+ case ActivityPub.is_public?(item) do
+ true ->
+ Pleroma.List.get_lists_from_activity(item)
+
+ _ ->
+ Pleroma.List.get_lists_from_activity(item)
+ |> Enum.filter(fn list ->
+ owner = Repo.get(User, list.user_id)
+ author.follower_address in owner.following
+ end)
+ end
+
recipient_topics =
- Pleroma.List.get_lists_from_activity(item)
+ recipient_lists
|> Enum.map(fn %{id: id} -> "list:#{id}" end)
Enum.each(recipient_topics || [], fn list_topic ->
diff --git a/priv/repo/migrations/20180829082446_add_recipients_to_and_cc_fields_to_activities.exs b/priv/repo/migrations/20180829082446_add_recipients_to_and_cc_fields_to_activities.exs
new file mode 100644
index 000000000..96af412f0
--- /dev/null
+++ b/priv/repo/migrations/20180829082446_add_recipients_to_and_cc_fields_to_activities.exs
@@ -0,0 +1,13 @@
+defmodule Pleroma.Repo.Migrations.AddRecipientsToAndCcFieldsToActivities do
+ use Ecto.Migration
+
+ def change do
+ alter table(:activities) do
+ add :recipients_to, {:array, :string}
+ add :recipients_cc, {:array, :string}
+ end
+
+ create index(:activities, [:recipients_to], using: :gin)
+ create index(:activities, [:recipients_cc], using: :gin)
+ end
+end
diff --git a/priv/repo/migrations/20180829182612_activities_add_to_cc_indices.exs b/priv/repo/migrations/20180829182612_activities_add_to_cc_indices.exs
new file mode 100644
index 000000000..f6c622e3e
--- /dev/null
+++ b/priv/repo/migrations/20180829182612_activities_add_to_cc_indices.exs
@@ -0,0 +1,8 @@
+defmodule Pleroma.Repo.Migrations.ActivitiesAddToCcIndices do
+ use Ecto.Migration
+
+ def change do
+ create index(:activities, ["(data->'to')"], name: :activities_to_index, using: :gin)
+ create index(:activities, ["(data->'cc')"], name: :activities_cc_index, using: :gin)
+ end
+end
diff --git a/priv/repo/migrations/20180829183529_remove_recipients_to_and_cc_fields_from_activities.exs b/priv/repo/migrations/20180829183529_remove_recipients_to_and_cc_fields_from_activities.exs
new file mode 100644
index 000000000..ed4f5af30
--- /dev/null
+++ b/priv/repo/migrations/20180829183529_remove_recipients_to_and_cc_fields_from_activities.exs
@@ -0,0 +1,10 @@
+defmodule Pleroma.Repo.Migrations.RemoveRecipientsToAndCcFieldsFromActivities do
+ use Ecto.Migration
+
+ def change do
+ alter table(:activities) do
+ remove :recipients_to
+ remove :recipients_cc
+ end
+ end
+end
diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs
index 9e33c1d04..d4ff16c68 100644
--- a/test/web/mastodon_api/mastodon_api_controller_test.exs
+++ b/test/web/mastodon_api/mastodon_api_controller_test.exs
@@ -368,6 +368,30 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
assert id == to_string(activity_two.id)
end
+
+ test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
+ user = insert(:user)
+ other_user = insert(:user)
+ {:ok, activity_one} = TwitterAPI.create_status(other_user, %{"status" => "Marisa is cute."})
+
+ {:ok, activity_two} =
+ TwitterAPI.create_status(other_user, %{
+ "status" => "Marisa is cute.",
+ "visibility" => "private"
+ })
+
+ {:ok, list} = Pleroma.List.create("name", user)
+ {:ok, list} = Pleroma.List.follow(list, other_user)
+
+ conn =
+ conn
+ |> assign(:user, user)
+ |> get("/api/v1/timelines/list/#{list.id}")
+
+ assert [%{"id" => id}] = json_response(conn, 200)
+
+ assert id == to_string(activity_one.id)
+ end
end
describe "notifications" do