ForForkMerge #2

Merged
sliver merged 185 commits from ForForkMerge into stable 2024-03-31 06:59:36 +00:00
2 changed files with 62 additions and 15 deletions
Showing only changes of commit a8c6c780b4 - Show all commits

View file

@ -33,16 +33,9 @@ defp reject_emoji?({shortcode, _url}, installed_emoji) do
!valid_shortcode? or rejected_shortcode? or emoji_installed? !valid_shortcode? or rejected_shortcode? or emoji_installed?
end end
defp steal_emoji(%{} = response, {shortcode, url}, emoji_dir_path) do defp steal_emoji(%{} = response, {shortcode, extension}, emoji_dir_path) do
extension =
url
|> URI.parse()
|> Map.get(:path)
|> Path.basename()
|> Path.extname()
shortcode = Path.basename(shortcode) shortcode = Path.basename(shortcode)
file_path = Path.join(emoji_dir_path, shortcode <> (extension || ".png")) file_path = Path.join(emoji_dir_path, shortcode <> "." <> extension)
case File.write(file_path, response.body) do case File.write(file_path, response.body) do
:ok -> :ok ->
@ -54,14 +47,25 @@ defp steal_emoji(%{} = response, {shortcode, url}, emoji_dir_path) do
end end
end end
defp get_extension_if_safe(response) do
content_type =
:proplists.get_value("content-type", response.headers, MIME.from_path(response.url))
case content_type do
"image/" <> _ -> List.first(MIME.extensions(content_type))
_ -> nil
end
end
defp maybe_steal_emoji({shortcode, url}, emoji_dir_path) do defp maybe_steal_emoji({shortcode, url}, emoji_dir_path) do
url = Pleroma.Web.MediaProxy.url(url) url = Pleroma.Web.MediaProxy.url(url)
with {:ok, %{status: status} = response} when status in 200..299 <- Pleroma.HTTP.get(url) do with {:ok, %{status: status} = response} when status in 200..299 <- Pleroma.HTTP.get(url) do
size_limit = Config.get([:mrf_steal_emoji, :size_limit], 50_000) size_limit = Config.get([:mrf_steal_emoji, :size_limit], 50_000)
extension = get_extension_if_safe(response)
if byte_size(response.body) <= size_limit do if byte_size(response.body) <= size_limit and extension do
steal_emoji(response, {shortcode, url}, emoji_dir_path) steal_emoji(response, {shortcode, extension}, emoji_dir_path)
else else
Logger.debug( Logger.debug(
"MRF.StealEmojiPolicy: :#{shortcode}: at #{url} (#{byte_size(response.body)} B) over size limit (#{size_limit} B)" "MRF.StealEmojiPolicy: :#{shortcode}: at #{url} (#{byte_size(response.body)} B) over size limit (#{size_limit} B)"

View file

@ -45,7 +45,11 @@ test "Steals emoji on unknown shortcode from allowed remote host", %{
refute File.exists?(path) refute File.exists?(path)
Tesla.Mock.mock(fn %{method: :get, url: "https://example.org/emoji/firedfox.png"} -> Tesla.Mock.mock(fn %{method: :get, url: "https://example.org/emoji/firedfox.png"} ->
%Tesla.Env{status: 200, body: File.read!("test/fixtures/image.jpg")} %Tesla.Env{
status: 200,
body: File.read!("test/fixtures/image.jpg"),
url: "https://example.org/emoji/firedfox.png"
}
end) end)
clear_config(:mrf_steal_emoji, hosts: ["example.org"], size_limit: 284_468) clear_config(:mrf_steal_emoji, hosts: ["example.org"], size_limit: 284_468)
@ -72,7 +76,11 @@ test "rejects invalid shortcodes", %{path: path} do
fullpath = Path.join(path, "fired/fox.png") fullpath = Path.join(path, "fired/fox.png")
Tesla.Mock.mock(fn %{method: :get, url: "https://example.org/emoji/firedfox"} -> Tesla.Mock.mock(fn %{method: :get, url: "https://example.org/emoji/firedfox"} ->
%Tesla.Env{status: 200, body: File.read!("test/fixtures/image.jpg")} %Tesla.Env{
status: 200,
body: File.read!("test/fixtures/image.jpg"),
url: "https://example.org/emoji/firedfox.png"
}
end) end)
clear_config(:mrf_steal_emoji, hosts: ["example.org"], size_limit: 284_468) clear_config(:mrf_steal_emoji, hosts: ["example.org"], size_limit: 284_468)
@ -86,6 +94,36 @@ test "rejects invalid shortcodes", %{path: path} do
refute File.exists?(fullpath) refute File.exists?(fullpath)
end end
test "prefers content-type header for extension", %{path: path} do
message = %{
"type" => "Create",
"object" => %{
"emoji" => [{"firedfox", "https://example.org/emoji/firedfox.fud"}],
"actor" => "https://example.org/users/admin"
}
}
Tesla.Mock.mock(fn %{method: :get, url: "https://example.org/emoji/firedfox.fud"} ->
%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/image.jpg"),
url: "https://example.org/emoji/firedfox.wevp",
headers: [{"content-type", "image/gif"}]
}
end)
clear_config(:mrf_steal_emoji, hosts: ["example.org"], size_limit: 284_468)
assert {:ok, _message} = StealEmojiPolicy.filter(message)
assert "firedfox" in installed()
assert File.exists?(path)
assert path
|> Path.join("firedfox.gif")
|> File.exists?()
end
test "reject regex shortcode", %{message: message} do test "reject regex shortcode", %{message: message} do
refute "firedfox" in installed() refute "firedfox" in installed()
@ -118,7 +156,11 @@ test "reject if size is above the limit", %{message: message} do
refute "firedfox" in installed() refute "firedfox" in installed()
Tesla.Mock.mock(fn %{method: :get, url: "https://example.org/emoji/firedfox.png"} -> Tesla.Mock.mock(fn %{method: :get, url: "https://example.org/emoji/firedfox.png"} ->
%Tesla.Env{status: 200, body: File.read!("test/fixtures/image.jpg")} %Tesla.Env{
status: 200,
body: File.read!("test/fixtures/image.jpg"),
url: "https://example.org/emoji/firedfox.png"
}
end) end)
clear_config(:mrf_steal_emoji, hosts: ["example.org"], size_limit: 50_000) clear_config(:mrf_steal_emoji, hosts: ["example.org"], size_limit: 50_000)
@ -132,7 +174,8 @@ test "reject if host returns error", %{message: message} do
refute "firedfox" in installed() refute "firedfox" in installed()
Tesla.Mock.mock(fn %{method: :get, url: "https://example.org/emoji/firedfox.png"} -> Tesla.Mock.mock(fn %{method: :get, url: "https://example.org/emoji/firedfox.png"} ->
{:ok, %Tesla.Env{status: 404, body: "Not found"}} {:ok,
%Tesla.Env{status: 404, body: "Not found", url: "https://example.org/emoji/firedfox.png"}}
end) end)
clear_config(:mrf_steal_emoji, hosts: ["example.org"], size_limit: 284_468) clear_config(:mrf_steal_emoji, hosts: ["example.org"], size_limit: 284_468)