added tests for Web.MediaProxy
authorMaksim <parallel588@gmail.com>
Sun, 14 Jul 2019 21:01:32 +0000 (21:01 +0000)
committerkaniini <ariadne@dereferenced.org>
Sun, 14 Jul 2019 21:01:32 +0000 (21:01 +0000)
lib/pleroma/web/media_proxy/media_proxy.ex
lib/pleroma/web/media_proxy/media_proxy_controller.ex [moved from lib/pleroma/web/media_proxy/controller.ex with 80% similarity]
mix.exs
test/web/media_proxy/media_proxy_controller_test.exs [new file with mode: 0644]
test/web/media_proxy/media_proxy_test.exs [moved from test/media_proxy_test.exs with 92% similarity]

index dd8888a021635357b4fe88c1bb2748517c7c6062..a661e9bb7153cf6f2c02b672bae88b06f5b481de 100644 (file)
@@ -3,68 +3,71 @@
 # SPDX-License-Identifier: AGPL-3.0-only
 
 defmodule Pleroma.Web.MediaProxy do
-  @base64_opts [padding: false]
-
-  def url(nil), do: nil
+  alias Pleroma.Config
+  alias Pleroma.Web
 
-  def url(""), do: nil
+  @base64_opts [padding: false]
 
+  def url(url) when is_nil(url) or url == "", do: nil
   def url("/" <> _ = url), do: url
 
   def url(url) do
-    if !enabled?() or local?(url) or whitelisted?(url) do
+    if disabled?() or local?(url) or whitelisted?(url) do
       url
     else
       encode_url(url)
     end
   end
 
-  defp enabled?, do: Pleroma.Config.get([:media_proxy, :enabled], false)
+  defp disabled?, do: !Config.get([:media_proxy, :enabled], false)
 
   defp local?(url), do: String.starts_with?(url, Pleroma.Web.base_url())
 
   defp whitelisted?(url) do
     %{host: domain} = URI.parse(url)
 
-    Enum.any?(Pleroma.Config.get([:media_proxy, :whitelist]), fn pattern ->
+    Enum.any?(Config.get([:media_proxy, :whitelist]), fn pattern ->
       String.equivalent?(domain, pattern)
     end)
   end
 
   def encode_url(url) do
-    secret = Pleroma.Config.get([Pleroma.Web.Endpoint, :secret_key_base])
     base64 = Base.url_encode64(url, @base64_opts)
-    sig = :crypto.hmac(:sha, secret, base64)
-    sig64 = sig |> Base.url_encode64(@base64_opts)
+
+    sig64 =
+      base64
+      |> signed_url
+      |> Base.url_encode64(@base64_opts)
 
     build_url(sig64, base64, filename(url))
   end
 
   def decode_url(sig, url) do
-    secret = Pleroma.Config.get([Pleroma.Web.Endpoint, :secret_key_base])
-    sig = Base.url_decode64!(sig, @base64_opts)
-    local_sig = :crypto.hmac(:sha, secret, url)
-
-    if local_sig == sig do
+    with {:ok, sig} <- Base.url_decode64(sig, @base64_opts),
+         signature when signature == sig <- signed_url(url) do
       {:ok, Base.url_decode64!(url, @base64_opts)}
     else
-      {:error, :invalid_signature}
+      _ -> {:error, :invalid_signature}
     end
   end
 
+  defp signed_url(url) do
+    :crypto.hmac(:sha, Config.get([Web.Endpoint, :secret_key_base]), url)
+  end
+
   def filename(url_or_path) do
     if path = URI.parse(url_or_path).path, do: Path.basename(path)
   end
 
   def build_url(sig_base64, url_base64, filename \\ nil) do
     [
-      Pleroma.Config.get([:media_proxy, :base_url], Pleroma.Web.base_url()),
+      Pleroma.Config.get([:media_proxy, :base_url], Web.base_url()),
       "proxy",
       sig_base64,
       url_base64,
       filename
     ]
-    |> Enum.filter(fn value -> value end)
+    |> Enum.filter(& &1)
     |> Path.join()
   end
 end
similarity index 80%
rename from lib/pleroma/web/media_proxy/controller.ex
rename to lib/pleroma/web/media_proxy/media_proxy_controller.ex
index ea33d7685752fd05edcc64c5e91e556a088e97c0..1e9520d462c36fc29da22698e87fc533db0f237b 100644 (file)
@@ -13,7 +13,7 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyController do
     with config <- Pleroma.Config.get([:media_proxy], []),
          true <- Keyword.get(config, :enabled, false),
          {:ok, url} <- MediaProxy.decode_url(sig64, url64),
-         :ok <- filename_matches(Map.has_key?(params, "filename"), conn.request_path, url) do
+         :ok <- filename_matches(params, conn.request_path, url) do
       ReverseProxy.call(conn, url, Keyword.get(config, :proxy_opts, @default_proxy_opts))
     else
       false ->
@@ -27,13 +27,15 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyController do
     end
   end
 
-  def filename_matches(has_filename, path, url) do
-    filename = url |> MediaProxy.filename()
+  def filename_matches(%{"filename" => _} = _, path, url) do
+    filename = MediaProxy.filename(url)
 
-    if has_filename && filename && Path.basename(path) != filename do
+    if filename && Path.basename(path) != filename do
       {:wrong_filename, filename}
     else
       :ok
     end
   end
+
+  def filename_matches(_, _, _), do: :ok
 end
diff --git a/mix.exs b/mix.exs
index f96789d215870752c286ffa2aaa2d03669bf12c9..aa444035199ac9b7e3fdb0da75391474f2410949 100644 (file)
--- a/mix.exs
+++ b/mix.exs
@@ -14,7 +14,7 @@ defmodule Pleroma.Mixfile do
       aliases: aliases(),
       deps: deps(),
       test_coverage: [tool: ExCoveralls],
-
+      preferred_cli_env: ["coveralls.html": :test],
       # Docs
       name: "Pleroma",
       homepage_url: "https://pleroma.social/",
diff --git a/test/web/media_proxy/media_proxy_controller_test.exs b/test/web/media_proxy/media_proxy_controller_test.exs
new file mode 100644 (file)
index 0000000..53b8f55
--- /dev/null
@@ -0,0 +1,73 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do
+  use Pleroma.Web.ConnCase
+  import Mock
+  alias Pleroma.Config
+
+  setup do
+    media_proxy_config = Config.get([:media_proxy]) || []
+    on_exit(fn -> Config.put([:media_proxy], media_proxy_config) end)
+    :ok
+  end
+
+  test "it returns 404 when MediaProxy disabled", %{conn: conn} do
+    Config.put([:media_proxy, :enabled], false)
+
+    assert %Plug.Conn{
+             status: 404,
+             resp_body: "Not Found"
+           } = get(conn, "/proxy/hhgfh/eeeee")
+
+    assert %Plug.Conn{
+             status: 404,
+             resp_body: "Not Found"
+           } = get(conn, "/proxy/hhgfh/eeee/fff")
+  end
+
+  test "it returns 403 when signature invalidated", %{conn: conn} do
+    Config.put([:media_proxy, :enabled], true)
+    Config.put([Pleroma.Web.Endpoint, :secret_key_base], "00000000000")
+    path = URI.parse(Pleroma.Web.MediaProxy.encode_url("https://google.fn")).path
+    Config.put([Pleroma.Web.Endpoint, :secret_key_base], "000")
+
+    assert %Plug.Conn{
+             status: 403,
+             resp_body: "Forbidden"
+           } = get(conn, path)
+
+    assert %Plug.Conn{
+             status: 403,
+             resp_body: "Forbidden"
+           } = get(conn, "/proxy/hhgfh/eeee")
+
+    assert %Plug.Conn{
+             status: 403,
+             resp_body: "Forbidden"
+           } = get(conn, "/proxy/hhgfh/eeee/fff")
+  end
+
+  test "redirects on valid url when filename invalidated", %{conn: conn} do
+    Config.put([:media_proxy, :enabled], true)
+    Config.put([Pleroma.Web.Endpoint, :secret_key_base], "00000000000")
+    url = Pleroma.Web.MediaProxy.encode_url("https://google.fn/test.png")
+    invalid_url = String.replace(url, "test.png", "test-file.png")
+    response = get(conn, invalid_url)
+    html = "<html><body>You are being <a href=\"#{url}\">redirected</a>.</body></html>"
+    assert response.status == 302
+    assert response.resp_body == html
+  end
+
+  test "it performs ReverseProxy.call when signature valid", %{conn: conn} do
+    Config.put([:media_proxy, :enabled], true)
+    Config.put([Pleroma.Web.Endpoint, :secret_key_base], "00000000000")
+    url = Pleroma.Web.MediaProxy.encode_url("https://google.fn/test.png")
+
+    with_mock Pleroma.ReverseProxy,
+      call: fn _conn, _url, _opts -> %Plug.Conn{status: :success} end do
+      assert %Plug.Conn{status: :success} = get(conn, url)
+    end
+  end
+end
similarity index 92%
rename from test/media_proxy_test.exs
rename to test/web/media_proxy/media_proxy_test.exs
index fbf2009310382a3f4cd70da33b0b5166006689a2..cb4807e0be841e54971e5dbd080e7a3b15903f33 100644 (file)
@@ -2,7 +2,7 @@
 # Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
 # SPDX-License-Identifier: AGPL-3.0-only
 
-defmodule Pleroma.MediaProxyTest do
+defmodule Pleroma.Web.MediaProxyTest do
   use ExUnit.Case
   import Pleroma.Web.MediaProxy
   alias Pleroma.Web.MediaProxy.MediaProxyController
@@ -90,22 +90,28 @@ defmodule Pleroma.MediaProxyTest do
 
     test "filename_matches preserves the encoded or decoded path" do
       assert MediaProxyController.filename_matches(
-               true,
+               %{"filename" => "/Hello world.jpg"},
                "/Hello world.jpg",
                "http://pleroma.social/Hello world.jpg"
              ) == :ok
 
       assert MediaProxyController.filename_matches(
-               true,
+               %{"filename" => "/Hello%20world.jpg"},
                "/Hello%20world.jpg",
                "http://pleroma.social/Hello%20world.jpg"
              ) == :ok
 
       assert MediaProxyController.filename_matches(
-               true,
+               %{"filename" => "/my%2Flong%2Furl%2F2019%2F07%2FS.jpg"},
                "/my%2Flong%2Furl%2F2019%2F07%2FS.jpg",
                "http://pleroma.social/my%2Flong%2Furl%2F2019%2F07%2FS.jpg"
              ) == :ok
+
+      assert MediaProxyController.filename_matches(
+               %{"filename" => "/my%2Flong%2Furl%2F2019%2F07%2FS.jp"},
+               "/my%2Flong%2Furl%2F2019%2F07%2FS.jp",
+               "http://pleroma.social/my%2Flong%2Furl%2F2019%2F07%2FS.jpg"
+             ) == {:wrong_filename, "my%2Flong%2Furl%2F2019%2F07%2FS.jpg"}
     end
 
     test "uses the configured base_url" do