logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma git clone https://hacktivis.me/git/pleroma.git

media_proxy_controller_test.exs (11171B)


  1. # Pleroma: A lightweight social networking server
  2. # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
  3. # SPDX-License-Identifier: AGPL-3.0-only
  4. defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do
  5. use Pleroma.Web.ConnCase
  6. import Mock
  7. import Mox
  8. alias Pleroma.ReverseProxy.ClientMock
  9. alias Pleroma.UnstubbedConfigMock, as: ConfigMock
  10. alias Pleroma.Web.MediaProxy
  11. alias Plug.Conn
  12. setup do
  13. ConfigMock
  14. |> stub_with(Pleroma.Test.StaticConfig)
  15. :ok
  16. end
  17. describe "Media Proxy" do
  18. setup do
  19. clear_config([:media_proxy, :enabled], true)
  20. clear_config([Pleroma.Web.Endpoint, :secret_key_base], "00000000000")
  21. [url: MediaProxy.encode_url("https://google.fn/test.png")]
  22. end
  23. test "it returns 404 when disabled", %{conn: conn} do
  24. clear_config([:media_proxy, :enabled], false)
  25. assert %Conn{
  26. status: 404,
  27. resp_body: "Not Found"
  28. } = get(conn, "/proxy/hhgfh/eeeee")
  29. assert %Conn{
  30. status: 404,
  31. resp_body: "Not Found"
  32. } = get(conn, "/proxy/hhgfh/eeee/fff")
  33. end
  34. test "it returns 403 for invalid signature", %{conn: conn, url: url} do
  35. clear_config([Pleroma.Web.Endpoint, :secret_key_base], "000")
  36. %{path: path} = URI.parse(url)
  37. assert %Conn{
  38. status: 403,
  39. resp_body: "Forbidden"
  40. } = get(conn, path)
  41. assert %Conn{
  42. status: 403,
  43. resp_body: "Forbidden"
  44. } = get(conn, "/proxy/hhgfh/eeee")
  45. assert %Conn{
  46. status: 403,
  47. resp_body: "Forbidden"
  48. } = get(conn, "/proxy/hhgfh/eeee/fff")
  49. end
  50. test "redirects to valid url when filename is invalidated", %{conn: conn, url: url} do
  51. invalid_url = String.replace(url, "test.png", "test-file.png")
  52. response = get(conn, invalid_url)
  53. assert response.status == 302
  54. assert redirected_to(response) == url
  55. end
  56. test "it performs ReverseProxy.call with valid signature", %{conn: conn, url: url} do
  57. with_mock Pleroma.ReverseProxy,
  58. call: fn _conn, _url, _opts -> %Conn{status: :success} end do
  59. assert %Conn{status: :success} = get(conn, url)
  60. end
  61. end
  62. test "it returns 404 when url is in banned_urls cache", %{conn: conn, url: url} do
  63. MediaProxy.put_in_banned_urls("https://google.fn/test.png")
  64. with_mock Pleroma.ReverseProxy,
  65. call: fn _conn, _url, _opts -> %Conn{status: :success} end do
  66. assert %Conn{status: 404, resp_body: "Not Found"} = get(conn, url)
  67. end
  68. end
  69. test "it applies sandbox CSP to MediaProxy requests", %{conn: conn} do
  70. media_url = "https://lain.com/image.png"
  71. media_proxy_url = MediaProxy.encode_url(media_url)
  72. ClientMock
  73. |> expect(:request, fn :get, ^media_url, _, _, _ ->
  74. {:ok, 200, [{"content-type", "image/png"}]}
  75. end)
  76. %Conn{resp_headers: headers} = get(conn, media_proxy_url)
  77. assert {"content-security-policy", "sandbox;"} in headers
  78. end
  79. end
  80. describe "Media Preview Proxy" do
  81. def assert_dependencies_installed do
  82. missing_dependencies = Pleroma.Helpers.MediaHelper.missing_dependencies()
  83. assert missing_dependencies == [],
  84. "Error: missing dependencies (please refer to `docs/installation`): #{inspect(missing_dependencies)}"
  85. end
  86. setup do
  87. clear_config([:media_proxy, :enabled], true)
  88. clear_config([:media_preview_proxy, :enabled], true)
  89. clear_config([Pleroma.Web.Endpoint, :secret_key_base], "00000000000")
  90. original_url = "https://google.fn/test.png"
  91. [
  92. url: MediaProxy.encode_preview_url(original_url),
  93. media_proxy_url: MediaProxy.encode_url(original_url)
  94. ]
  95. end
  96. test "returns 404 when media proxy is disabled", %{conn: conn} do
  97. clear_config([:media_proxy, :enabled], false)
  98. assert %Conn{
  99. status: 404,
  100. resp_body: "Not Found"
  101. } = get(conn, "/proxy/preview/hhgfh/eeeee")
  102. assert %Conn{
  103. status: 404,
  104. resp_body: "Not Found"
  105. } = get(conn, "/proxy/preview/hhgfh/fff")
  106. end
  107. test "returns 404 when disabled", %{conn: conn} do
  108. clear_config([:media_preview_proxy, :enabled], false)
  109. assert %Conn{
  110. status: 404,
  111. resp_body: "Not Found"
  112. } = get(conn, "/proxy/preview/hhgfh/eeeee")
  113. assert %Conn{
  114. status: 404,
  115. resp_body: "Not Found"
  116. } = get(conn, "/proxy/preview/hhgfh/fff")
  117. end
  118. test "it returns 403 for invalid signature", %{conn: conn, url: url} do
  119. clear_config([Pleroma.Web.Endpoint, :secret_key_base], "000")
  120. %{path: path} = URI.parse(url)
  121. assert %Conn{
  122. status: 403,
  123. resp_body: "Forbidden"
  124. } = get(conn, path)
  125. assert %Conn{
  126. status: 403,
  127. resp_body: "Forbidden"
  128. } = get(conn, "/proxy/preview/hhgfh/eeee")
  129. assert %Conn{
  130. status: 403,
  131. resp_body: "Forbidden"
  132. } = get(conn, "/proxy/preview/hhgfh/eeee/fff")
  133. end
  134. test "redirects to valid url when filename is invalidated", %{conn: conn, url: url} do
  135. invalid_url = String.replace(url, "test.png", "test-file.png")
  136. response = get(conn, invalid_url)
  137. assert response.status == 302
  138. assert redirected_to(response) == url
  139. end
  140. test "responds with 424 Failed Dependency if HEAD request to media proxy fails", %{
  141. conn: conn,
  142. url: url,
  143. media_proxy_url: media_proxy_url
  144. } do
  145. Tesla.Mock.mock(fn
  146. %{method: :head, url: ^media_proxy_url} ->
  147. %Tesla.Env{status: 500, body: ""}
  148. end)
  149. response = get(conn, url)
  150. assert response.status == 424
  151. assert response.resp_body == "Can't fetch HTTP headers (HTTP 500)."
  152. end
  153. test "redirects to media proxy URI on unsupported content type", %{
  154. conn: conn,
  155. url: url,
  156. media_proxy_url: media_proxy_url
  157. } do
  158. Tesla.Mock.mock(fn
  159. %{method: :head, url: ^media_proxy_url} ->
  160. %Tesla.Env{status: 200, body: "", headers: [{"content-type", "application/pdf"}]}
  161. end)
  162. response = get(conn, url)
  163. assert response.status == 302
  164. assert redirected_to(response) == media_proxy_url
  165. end
  166. test "with `static=true` and GIF image preview requested, responds with JPEG image", %{
  167. conn: conn,
  168. url: url,
  169. media_proxy_url: media_proxy_url
  170. } do
  171. assert_dependencies_installed()
  172. # Setting a high :min_content_length to ensure this scenario is not affected by its logic
  173. clear_config([:media_preview_proxy, :min_content_length], 1_000_000_000)
  174. Tesla.Mock.mock(fn
  175. %{method: :head, url: ^media_proxy_url} ->
  176. %Tesla.Env{
  177. status: 200,
  178. body: "",
  179. headers: [{"content-type", "image/gif"}, {"content-length", "1001718"}]
  180. }
  181. %{method: :get, url: ^media_proxy_url} ->
  182. %Tesla.Env{status: 200, body: File.read!("test/fixtures/image.gif")}
  183. end)
  184. response = get(conn, url <> "?static=true")
  185. assert response.status == 200
  186. assert Conn.get_resp_header(response, "content-type") == ["image/jpeg"]
  187. assert response.resp_body != ""
  188. end
  189. test "with GIF image preview requested and no `static` param, redirects to media proxy URI",
  190. %{
  191. conn: conn,
  192. url: url,
  193. media_proxy_url: media_proxy_url
  194. } do
  195. Tesla.Mock.mock(fn
  196. %{method: :head, url: ^media_proxy_url} ->
  197. %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/gif"}]}
  198. end)
  199. response = get(conn, url)
  200. assert response.status == 302
  201. assert redirected_to(response) == media_proxy_url
  202. end
  203. test "with `static` param and non-GIF image preview requested, " <>
  204. "redirects to media preview proxy URI without `static` param",
  205. %{
  206. conn: conn,
  207. url: url,
  208. media_proxy_url: media_proxy_url
  209. } do
  210. Tesla.Mock.mock(fn
  211. %{method: :head, url: ^media_proxy_url} ->
  212. %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]}
  213. end)
  214. response = get(conn, url <> "?static=true")
  215. assert response.status == 302
  216. assert redirected_to(response) == url
  217. end
  218. test "with :min_content_length setting not matched by Content-Length header, " <>
  219. "redirects to media proxy URI",
  220. %{
  221. conn: conn,
  222. url: url,
  223. media_proxy_url: media_proxy_url
  224. } do
  225. clear_config([:media_preview_proxy, :min_content_length], 100_000)
  226. Tesla.Mock.mock(fn
  227. %{method: :head, url: ^media_proxy_url} ->
  228. %Tesla.Env{
  229. status: 200,
  230. body: "",
  231. headers: [{"content-type", "image/gif"}, {"content-length", "5000"}]
  232. }
  233. end)
  234. response = get(conn, url)
  235. assert response.status == 302
  236. assert redirected_to(response) == media_proxy_url
  237. end
  238. test "thumbnails PNG images into PNG", %{
  239. conn: conn,
  240. url: url,
  241. media_proxy_url: media_proxy_url
  242. } do
  243. assert_dependencies_installed()
  244. Tesla.Mock.mock(fn
  245. %{method: :head, url: ^media_proxy_url} ->
  246. %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/png"}]}
  247. %{method: :get, url: ^media_proxy_url} ->
  248. %Tesla.Env{status: 200, body: File.read!("test/fixtures/image.png")}
  249. end)
  250. response = get(conn, url)
  251. assert response.status == 200
  252. assert Conn.get_resp_header(response, "content-type") == ["image/png"]
  253. assert response.resp_body != ""
  254. end
  255. test "thumbnails JPEG images into JPEG", %{
  256. conn: conn,
  257. url: url,
  258. media_proxy_url: media_proxy_url
  259. } do
  260. assert_dependencies_installed()
  261. Tesla.Mock.mock(fn
  262. %{method: :head, url: ^media_proxy_url} ->
  263. %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]}
  264. %{method: :get, url: ^media_proxy_url} ->
  265. %Tesla.Env{status: 200, body: File.read!("test/fixtures/image.jpg")}
  266. end)
  267. response = get(conn, url)
  268. assert response.status == 200
  269. assert Conn.get_resp_header(response, "content-type") == ["image/jpeg"]
  270. assert response.resp_body != ""
  271. end
  272. test "redirects to media proxy URI in case of thumbnailing error", %{
  273. conn: conn,
  274. url: url,
  275. media_proxy_url: media_proxy_url
  276. } do
  277. Tesla.Mock.mock(fn
  278. %{method: :head, url: ^media_proxy_url} ->
  279. %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]}
  280. %{method: :get, url: ^media_proxy_url} ->
  281. %Tesla.Env{status: 200, body: "<html><body>error</body></html>"}
  282. end)
  283. response = get(conn, url)
  284. assert response.status == 302
  285. assert redirected_to(response) == media_proxy_url
  286. end
  287. end
  288. end