Rich Media: add stream byte counting as an extra protection against malicious URLs

This commit is contained in:
Mark Felder 2024-08-29 09:37:10 -04:00
parent d01569822e
commit c17a78c55a

View file

@ -23,7 +23,7 @@ defmodule Pleroma.Web.RichMedia.Helpers do
{:get, Pleroma.HTTP.get(url, req_headers(), http_options())}, {:get, Pleroma.HTTP.get(url, req_headers(), http_options())},
{_, :ok} <- {:content_type, check_content_type(headers)}, {_, :ok} <- {:content_type, check_content_type(headers)},
{_, :ok} <- {:content_length, check_content_length(headers)}, {_, :ok} <- {:content_length, check_content_length(headers)},
body <- Enum.into(stream_body, <<>>) do {:read_stream, {:ok, body}} <- {:read_stream, read_stream(stream_body)} do
{:ok, body} {:ok, body}
end end
end end
@ -52,8 +52,12 @@ defmodule Pleroma.Web.RichMedia.Helpers do
Logger.debug("Rich media error for #{url}: content-type is #{type}") Logger.debug("Rich media error for #{url}: content-type is #{type}")
{:error, :content_type} {:error, :content_type}
{:content_length, {_, length}} -> {:content_length, :error} ->
Logger.debug("Rich media error for #{url}: content-length is #{length}") Logger.debug("Rich media error for #{url}: content-length exceeded")
{:error, :body_too_large}
{:read_stream, :error} ->
Logger.debug("Rich media error for #{url}: content-length exceeded")
{:error, :body_too_large} {:error, :body_too_large}
{:get, _} -> {:get, _} ->
@ -82,7 +86,7 @@ defmodule Pleroma.Web.RichMedia.Helpers do
{_, maybe_content_length} -> {_, maybe_content_length} ->
case Integer.parse(maybe_content_length) do case Integer.parse(maybe_content_length) do
{content_length, ""} when content_length <= max_body -> :ok {content_length, ""} when content_length <= max_body -> :ok
{_, ""} -> {:error, maybe_content_length} {_, ""} -> :error
_ -> :ok _ -> :ok
end end
@ -91,6 +95,28 @@ defmodule Pleroma.Web.RichMedia.Helpers do
end end
end end
defp read_stream(stream) do
max_body = Keyword.get(http_options(), :max_body)
try do
result =
Stream.transform(stream, 0, fn chunk, total_bytes ->
new_total = total_bytes + byte_size(chunk)
if new_total > max_body do
raise("Exceeds max body limit of #{max_body}")
else
{[chunk], new_total}
end
end)
|> Enum.into(<<>>)
{:ok, result}
rescue
_ -> :error
end
end
defp http_options do defp http_options do
[ [
pool: :rich_media, pool: :rich_media,