mirror of
https://git.pleroma.social/pleroma/pleroma.git
synced 2025-04-26 04:34:46 +00:00
ReverseProxy: Sanitize content.
This commit is contained in:
parent
d9ae9b676c
commit
c143653364
2 changed files with 90 additions and 5 deletions
|
@ -17,6 +17,8 @@ defmodule Pleroma.ReverseProxy do
|
||||||
@failed_request_ttl :timer.seconds(60)
|
@failed_request_ttl :timer.seconds(60)
|
||||||
@methods ~w(GET HEAD)
|
@methods ~w(GET HEAD)
|
||||||
|
|
||||||
|
@allowed_mime_types Pleroma.Config.get([Pleroma.Upload, :allowed_mime_types], [])
|
||||||
|
|
||||||
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
||||||
|
|
||||||
def max_read_duration_default, do: @max_read_duration
|
def max_read_duration_default, do: @max_read_duration
|
||||||
|
@ -301,10 +303,26 @@ defmodule Pleroma.ReverseProxy do
|
||||||
headers
|
headers
|
||||||
|> Enum.filter(fn {k, _} -> k in @keep_resp_headers end)
|
|> Enum.filter(fn {k, _} -> k in @keep_resp_headers end)
|
||||||
|> build_resp_cache_headers(opts)
|
|> build_resp_cache_headers(opts)
|
||||||
|
|> sanitise_content_type()
|
||||||
|> build_resp_content_disposition_header(opts)
|
|> build_resp_content_disposition_header(opts)
|
||||||
|> Keyword.merge(Keyword.get(opts, :resp_headers, []))
|
|> Keyword.merge(Keyword.get(opts, :resp_headers, []))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp sanitise_content_type(headers) do
|
||||||
|
original_ct = get_content_type(headers)
|
||||||
|
|
||||||
|
safe_ct =
|
||||||
|
Pleroma.Web.Plugs.Utils.get_safe_mime_type(
|
||||||
|
%{allowed_mime_types: @allowed_mime_types},
|
||||||
|
original_ct
|
||||||
|
)
|
||||||
|
|
||||||
|
[
|
||||||
|
{"content-type", safe_ct}
|
||||||
|
| Enum.filter(headers, fn {k, _v} -> k != "content-type" end)
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
defp build_resp_cache_headers(headers, _opts) do
|
defp build_resp_cache_headers(headers, _opts) do
|
||||||
has_cache? = Enum.any?(headers, fn {k, _} -> k in @resp_cache_headers end)
|
has_cache? = Enum.any?(headers, fn {k, _} -> k in @resp_cache_headers end)
|
||||||
|
|
||||||
|
|
|
@ -63,7 +63,11 @@ defmodule Pleroma.ReverseProxyTest do
|
||||||
|> Plug.Conn.put_req_header("user-agent", "fake/1.0")
|
|> Plug.Conn.put_req_header("user-agent", "fake/1.0")
|
||||||
|> ReverseProxy.call("/user-agent")
|
|> ReverseProxy.call("/user-agent")
|
||||||
|
|
||||||
assert json_response(conn, 200) == %{"user-agent" => Pleroma.Application.user_agent()}
|
# Convert the response to a map without relying on json_response
|
||||||
|
body = conn.resp_body
|
||||||
|
assert conn.status == 200
|
||||||
|
response = Jason.decode!(body)
|
||||||
|
assert response == %{"user-agent" => Pleroma.Application.user_agent()}
|
||||||
end
|
end
|
||||||
|
|
||||||
test "closed connection", %{conn: conn} do
|
test "closed connection", %{conn: conn} do
|
||||||
|
@ -138,11 +142,14 @@ defmodule Pleroma.ReverseProxyTest do
|
||||||
test "common", %{conn: conn} do
|
test "common", %{conn: conn} do
|
||||||
ClientMock
|
ClientMock
|
||||||
|> expect(:request, fn :head, "/head", _, _, _ ->
|
|> expect(:request, fn :head, "/head", _, _, _ ->
|
||||||
{:ok, 200, [{"content-type", "text/html; charset=utf-8"}]}
|
{:ok, 200, [{"content-type", "image/png"}]}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
conn = ReverseProxy.call(Map.put(conn, :method, "HEAD"), "/head")
|
conn = ReverseProxy.call(Map.put(conn, :method, "HEAD"), "/head")
|
||||||
assert html_response(conn, 200) == ""
|
|
||||||
|
assert conn.status == 200
|
||||||
|
assert Conn.get_resp_header(conn, "content-type") == ["image/png"]
|
||||||
|
assert conn.resp_body == ""
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -249,7 +256,10 @@ defmodule Pleroma.ReverseProxyTest do
|
||||||
)
|
)
|
||||||
|> ReverseProxy.call("/headers")
|
|> ReverseProxy.call("/headers")
|
||||||
|
|
||||||
%{"headers" => headers} = json_response(conn, 200)
|
body = conn.resp_body
|
||||||
|
assert conn.status == 200
|
||||||
|
response = Jason.decode!(body)
|
||||||
|
headers = response["headers"]
|
||||||
assert headers["Accept"] == "text/html"
|
assert headers["Accept"] == "text/html"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -262,7 +272,10 @@ defmodule Pleroma.ReverseProxyTest do
|
||||||
)
|
)
|
||||||
|> ReverseProxy.call("/headers")
|
|> ReverseProxy.call("/headers")
|
||||||
|
|
||||||
%{"headers" => headers} = json_response(conn, 200)
|
body = conn.resp_body
|
||||||
|
assert conn.status == 200
|
||||||
|
response = Jason.decode!(body)
|
||||||
|
headers = response["headers"]
|
||||||
refute headers["Accept-Language"]
|
refute headers["Accept-Language"]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -328,4 +341,58 @@ defmodule Pleroma.ReverseProxyTest do
|
||||||
assert {"content-disposition", "attachment; filename=\"filename.jpg\""} in conn.resp_headers
|
assert {"content-disposition", "attachment; filename=\"filename.jpg\""} in conn.resp_headers
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "content-type sanitisation" do
|
||||||
|
test "preserves allowed image type", %{conn: conn} do
|
||||||
|
ClientMock
|
||||||
|
|> expect(:request, fn :get, "/content", _, _, _ ->
|
||||||
|
{:ok, 200, [{"content-type", "image/png"}], %{url: "/content"}}
|
||||||
|
end)
|
||||||
|
|> expect(:stream_body, fn _ -> :done end)
|
||||||
|
|
||||||
|
conn = ReverseProxy.call(conn, "/content")
|
||||||
|
|
||||||
|
assert conn.status == 200
|
||||||
|
assert Conn.get_resp_header(conn, "content-type") == ["image/png"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "preserves allowed video type", %{conn: conn} do
|
||||||
|
ClientMock
|
||||||
|
|> expect(:request, fn :get, "/content", _, _, _ ->
|
||||||
|
{:ok, 200, [{"content-type", "video/mp4"}], %{url: "/content"}}
|
||||||
|
end)
|
||||||
|
|> expect(:stream_body, fn _ -> :done end)
|
||||||
|
|
||||||
|
conn = ReverseProxy.call(conn, "/content")
|
||||||
|
|
||||||
|
assert conn.status == 200
|
||||||
|
assert Conn.get_resp_header(conn, "content-type") == ["video/mp4"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "sanitizes ActivityPub content type", %{conn: conn} do
|
||||||
|
ClientMock
|
||||||
|
|> expect(:request, fn :get, "/content", _, _, _ ->
|
||||||
|
{:ok, 200, [{"content-type", "application/activity+json"}], %{url: "/content"}}
|
||||||
|
end)
|
||||||
|
|> expect(:stream_body, fn _ -> :done end)
|
||||||
|
|
||||||
|
conn = ReverseProxy.call(conn, "/content")
|
||||||
|
|
||||||
|
assert conn.status == 200
|
||||||
|
assert Conn.get_resp_header(conn, "content-type") == ["application/octet-stream"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "sanitizes LD-JSON content type", %{conn: conn} do
|
||||||
|
ClientMock
|
||||||
|
|> expect(:request, fn :get, "/content", _, _, _ ->
|
||||||
|
{:ok, 200, [{"content-type", "application/ld+json"}], %{url: "/content"}}
|
||||||
|
end)
|
||||||
|
|> expect(:stream_body, fn _ -> :done end)
|
||||||
|
|
||||||
|
conn = ReverseProxy.call(conn, "/content")
|
||||||
|
|
||||||
|
assert conn.status == 200
|
||||||
|
assert Conn.get_resp_header(conn, "content-type") == ["application/octet-stream"]
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue