diff options
| -rw-r--r-- | CHANGELOG.md | 20 | ||||
| -rw-r--r-- | changelog.d/3893.skip | 0 | ||||
| -rw-r--r-- | lib/pleroma/web/streamer.ex | 46 | ||||
| -rw-r--r-- | mix.exs | 2 | ||||
| -rw-r--r-- | test/pleroma/web/streamer_test.exs | 77 | 
5 files changed, 137 insertions, 8 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d251050c..42a1bbb8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,26 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).  ### Removed  - BREAKING: Support for passwords generated with `crypt(3)` (Gnu Social migration artifact) +## 2.5.2 + +### Security +- `/proxy` endpoint now sets a Content-Security-Policy (sandbox) +- WebSocket endpoint now respects unauthenticated restrictions for streams of public posts +- OEmbed HTML tags are now filtered + +### Changed +- docs: Be more explicit about the level of compatibility of OTP releases +- Set default background worker timeout to 15 minutes + +### Fixed +- Atom/RSS formatting (HTML truncation, published, missing summary) +- Remove `static_fe` pipeline for `/users/:nickname/feed` +- Stop oban from retrying if validating errors occur when processing incoming data +- Make sure object refetching as used by already received polls follows MRF rules + +### Removed +- BREAKING: Support for passwords generated with `crypt(3)` (Gnu Social migration artifact) +  ## 2.5.1  ### Added diff --git a/changelog.d/3893.skip b/changelog.d/3893.skip new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/changelog.d/3893.skip diff --git a/lib/pleroma/web/streamer.ex b/lib/pleroma/web/streamer.ex index 3c0da5c27..b9a04cc76 100644 --- a/lib/pleroma/web/streamer.ex +++ b/lib/pleroma/web/streamer.ex @@ -25,6 +25,7 @@ defmodule Pleroma.Web.Streamer do    def registry, do: @registry    @public_streams ["public", "public:local", "public:media", "public:local:media"] +  @local_streams ["public:local", "public:local:media"]    @user_streams ["user", "user:notification", "direct", "user:pleroma_chat"]    @doc "Expands and authorizes a stream, and registers the process for streaming." @@ -41,14 +42,37 @@ defmodule Pleroma.Web.Streamer do      end    end +  defp can_access_stream(user, oauth_token, kind) do +    with {_, true} <- {:restrict?, Config.restrict_unauthenticated_access?(:timelines, kind)}, +         {_, %User{id: user_id}, %Token{user_id: user_id}} <- {:user, user, oauth_token}, +         {_, true} <- +           {:scopes, +            OAuthScopesPlug.filter_descendants(["read:statuses"], oauth_token.scopes) != []} do +      true +    else +      {:restrict?, _} -> +        true + +      _ -> +        false +    end +  end +    @doc "Expand and authorizes a stream"    @spec get_topic(stream :: String.t(), User.t() | nil, Token.t() | nil, Map.t()) ::            {:ok, topic :: String.t()} | {:error, :bad_topic}    def get_topic(stream, user, oauth_token, params \\ %{}) -  # Allow all public steams. -  def get_topic(stream, _user, _oauth_token, _params) when stream in @public_streams do -    {:ok, stream} +  # Allow all public steams if the instance allows unauthenticated access. +  # Otherwise, only allow users with valid oauth tokens. +  def get_topic(stream, user, oauth_token, _params) when stream in @public_streams do +    kind = if stream in @local_streams, do: :local, else: :federated + +    if can_access_stream(user, oauth_token, kind) do +      {:ok, stream} +    else +      {:error, :unauthorized} +    end    end    # Allow all hashtags streams. @@ -57,12 +81,20 @@ defmodule Pleroma.Web.Streamer do    end    # Allow remote instance streams. -  def get_topic("public:remote", _user, _oauth_token, %{"instance" => instance} = _params) do -    {:ok, "public:remote:" <> instance} +  def get_topic("public:remote", user, oauth_token, %{"instance" => instance} = _params) do +    if can_access_stream(user, oauth_token, :federated) do +      {:ok, "public:remote:" <> instance} +    else +      {:error, :unauthorized} +    end    end -  def get_topic("public:remote:media", _user, _oauth_token, %{"instance" => instance} = _params) do -    {:ok, "public:remote:media:" <> instance} +  def get_topic("public:remote:media", user, oauth_token, %{"instance" => instance} = _params) do +    if can_access_stream(user, oauth_token, :federated) do +      {:ok, "public:remote:media:" <> instance} +    else +      {:error, :unauthorized} +    end    end    # Expand user streams. @@ -4,7 +4,7 @@ defmodule Pleroma.Mixfile do    def project do      [        app: :pleroma, -      version: version("2.5.51"), +      version: version("2.5.52"),        elixir: "~> 1.11",        elixirc_paths: elixirc_paths(Mix.env()),        compilers: [:phoenix, :gettext] ++ Mix.compilers(), diff --git a/test/pleroma/web/streamer_test.exs b/test/pleroma/web/streamer_test.exs index 8b0c84164..7ab0e379b 100644 --- a/test/pleroma/web/streamer_test.exs +++ b/test/pleroma/web/streamer_test.exs @@ -29,6 +29,26 @@ defmodule Pleroma.Web.StreamerTest do        assert {:ok, "public:local:media"} = Streamer.get_topic("public:local:media", nil, nil)      end +    test "rejects local public streams if restricted_unauthenticated is on" do +      clear_config([:restrict_unauthenticated, :timelines, :local], true) + +      assert {:error, :unauthorized} = Streamer.get_topic("public:local", nil, nil) +      assert {:error, :unauthorized} = Streamer.get_topic("public:local:media", nil, nil) +    end + +    test "rejects remote public streams if restricted_unauthenticated is on" do +      clear_config([:restrict_unauthenticated, :timelines, :federated], true) + +      assert {:error, :unauthorized} = Streamer.get_topic("public", nil, nil) +      assert {:error, :unauthorized} = Streamer.get_topic("public:media", nil, nil) + +      assert {:error, :unauthorized} = +               Streamer.get_topic("public:remote", nil, nil, %{"instance" => "lain.com"}) + +      assert {:error, :unauthorized} = +               Streamer.get_topic("public:remote:media", nil, nil, %{"instance" => "lain.com"}) +    end +      test "allows instance streams" do        assert {:ok, "public:remote:lain.com"} =                 Streamer.get_topic("public:remote", nil, nil, %{"instance" => "lain.com"}) @@ -69,6 +89,63 @@ defmodule Pleroma.Web.StreamerTest do        end      end +    test "allows local public streams if restricted_unauthenticated is on", %{ +      user: user, +      token: oauth_token +    } do +      clear_config([:restrict_unauthenticated, :timelines, :local], true) + +      %{token: read_notifications_token} = oauth_access(["read:notifications"], user: user) +      %{token: badly_scoped_token} = oauth_access(["irrelevant:scope"], user: user) + +      assert {:ok, "public:local"} = Streamer.get_topic("public:local", user, oauth_token) + +      assert {:ok, "public:local:media"} = +               Streamer.get_topic("public:local:media", user, oauth_token) + +      for token <- [read_notifications_token, badly_scoped_token] do +        assert {:error, :unauthorized} = Streamer.get_topic("public:local", user, token) + +        assert {:error, :unauthorized} = Streamer.get_topic("public:local:media", user, token) +      end +    end + +    test "allows remote public streams if restricted_unauthenticated is on", %{ +      user: user, +      token: oauth_token +    } do +      clear_config([:restrict_unauthenticated, :timelines, :federated], true) + +      %{token: read_notifications_token} = oauth_access(["read:notifications"], user: user) +      %{token: badly_scoped_token} = oauth_access(["irrelevant:scope"], user: user) + +      assert {:ok, "public"} = Streamer.get_topic("public", user, oauth_token) +      assert {:ok, "public:media"} = Streamer.get_topic("public:media", user, oauth_token) + +      assert {:ok, "public:remote:lain.com"} = +               Streamer.get_topic("public:remote", user, oauth_token, %{"instance" => "lain.com"}) + +      assert {:ok, "public:remote:media:lain.com"} = +               Streamer.get_topic("public:remote:media", user, oauth_token, %{ +                 "instance" => "lain.com" +               }) + +      for token <- [read_notifications_token, badly_scoped_token] do +        assert {:error, :unauthorized} = Streamer.get_topic("public", user, token) +        assert {:error, :unauthorized} = Streamer.get_topic("public:media", user, token) + +        assert {:error, :unauthorized} = +                 Streamer.get_topic("public:remote", user, token, %{ +                   "instance" => "lain.com" +                 }) + +        assert {:error, :unauthorized} = +                 Streamer.get_topic("public:remote:media", user, token, %{ +                   "instance" => "lain.com" +                 }) +      end +    end +      test "allows user streams (with proper OAuth token scopes)", %{        user: user,        token: read_oauth_token  | 
