EmojiPackControllerDownloadZipTest: Add test.
This commit is contained in:
parent
8d0b29d718
commit
897c1ced5f
1 changed files with 311 additions and 0 deletions
|
|
@ -0,0 +1,311 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerDownloadZipTest do
|
||||||
|
use Pleroma.Web.ConnCase, async: false
|
||||||
|
|
||||||
|
import Tesla.Mock
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
@emoji_path Path.join(
|
||||||
|
Pleroma.Config.get!([:instance, :static_dir]),
|
||||||
|
"emoji"
|
||||||
|
)
|
||||||
|
|
||||||
|
setup do
|
||||||
|
admin = insert(:user, is_admin: true)
|
||||||
|
token = insert(:oauth_admin_token, user: admin)
|
||||||
|
|
||||||
|
admin_conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> assign(:token, token)
|
||||||
|
|
||||||
|
Pleroma.Emoji.reload()
|
||||||
|
|
||||||
|
# Clean up any test packs from previous runs
|
||||||
|
on_exit(fn ->
|
||||||
|
test_packs = [
|
||||||
|
"test_zip_pack",
|
||||||
|
"test_zip_pack_url",
|
||||||
|
"test_zip_pack_malicious",
|
||||||
|
"test_invalid_pack",
|
||||||
|
"test_bad_url_pack",
|
||||||
|
"test_no_source_pack"
|
||||||
|
]
|
||||||
|
|
||||||
|
Enum.each(test_packs, fn pack_name ->
|
||||||
|
pack_path = Path.join(@emoji_path, pack_name)
|
||||||
|
|
||||||
|
if File.exists?(pack_path) do
|
||||||
|
File.rm_rf!(pack_path)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
{:ok, %{admin_conn: admin_conn}}
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /api/pleroma/emoji/packs/download_zip" do
|
||||||
|
setup do
|
||||||
|
clear_config([:instance, :admin_privileges], [:emoji_manage_emoji])
|
||||||
|
end
|
||||||
|
|
||||||
|
test "creates pack from uploaded ZIP file", %{admin_conn: admin_conn} do
|
||||||
|
# Create a test ZIP file with emojis
|
||||||
|
{:ok, zip_path} = create_test_emoji_zip()
|
||||||
|
|
||||||
|
upload = %Plug.Upload{
|
||||||
|
content_type: "application/zip",
|
||||||
|
path: zip_path,
|
||||||
|
filename: "test_pack.zip"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert admin_conn
|
||||||
|
|> put_req_header("content-type", "multipart/form-data")
|
||||||
|
|> post("/api/pleroma/emoji/packs/download_zip", %{
|
||||||
|
name: "test_zip_pack",
|
||||||
|
file: upload
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(200) == "ok"
|
||||||
|
|
||||||
|
# Verify pack was created
|
||||||
|
assert File.exists?("#{@emoji_path}/test_zip_pack/pack.json")
|
||||||
|
assert File.exists?("#{@emoji_path}/test_zip_pack/test_emoji.png")
|
||||||
|
|
||||||
|
# Verify pack.json contents
|
||||||
|
{:ok, pack_json} = File.read("#{@emoji_path}/test_zip_pack/pack.json")
|
||||||
|
pack_data = Jason.decode!(pack_json)
|
||||||
|
|
||||||
|
assert pack_data["files"]["test_emoji"] == "test_emoji.png"
|
||||||
|
assert pack_data["pack"]["src_sha256"] != nil
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
File.rm!(zip_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "creates pack from URL", %{admin_conn: admin_conn} do
|
||||||
|
# Mock HTTP request to download ZIP
|
||||||
|
{:ok, zip_path} = create_test_emoji_zip()
|
||||||
|
{:ok, zip_data} = File.read(zip_path)
|
||||||
|
|
||||||
|
mock(fn
|
||||||
|
%{method: :get, url: "https://example.com/emoji_pack.zip"} ->
|
||||||
|
%Tesla.Env{status: 200, body: zip_data}
|
||||||
|
end)
|
||||||
|
|
||||||
|
assert admin_conn
|
||||||
|
|> put_req_header("content-type", "multipart/form-data")
|
||||||
|
|> post("/api/pleroma/emoji/packs/download_zip", %{
|
||||||
|
name: "test_zip_pack_url",
|
||||||
|
url: "https://example.com/emoji_pack.zip"
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(200) == "ok"
|
||||||
|
|
||||||
|
# Verify pack was created
|
||||||
|
assert File.exists?("#{@emoji_path}/test_zip_pack_url/pack.json")
|
||||||
|
assert File.exists?("#{@emoji_path}/test_zip_pack_url/test_emoji.png")
|
||||||
|
|
||||||
|
# Verify pack.json has URL as source
|
||||||
|
{:ok, pack_json} = File.read("#{@emoji_path}/test_zip_pack_url/pack.json")
|
||||||
|
pack_data = Jason.decode!(pack_json)
|
||||||
|
|
||||||
|
assert pack_data["pack"]["src"] == "https://example.com/emoji_pack.zip"
|
||||||
|
assert pack_data["pack"]["src_sha256"] != nil
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
File.rm!(zip_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "refuses to overwrite existing pack", %{admin_conn: admin_conn} do
|
||||||
|
# Create existing pack
|
||||||
|
pack_path = Path.join(@emoji_path, "test_zip_pack")
|
||||||
|
File.mkdir_p!(pack_path)
|
||||||
|
File.write!(Path.join(pack_path, "pack.json"), Jason.encode!(%{files: %{}}))
|
||||||
|
|
||||||
|
{:ok, zip_path} = create_test_emoji_zip()
|
||||||
|
|
||||||
|
upload = %Plug.Upload{
|
||||||
|
content_type: "application/zip",
|
||||||
|
path: zip_path,
|
||||||
|
filename: "test_pack.zip"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert admin_conn
|
||||||
|
|> put_req_header("content-type", "multipart/form-data")
|
||||||
|
|> post("/api/pleroma/emoji/packs/download_zip", %{
|
||||||
|
name: "test_zip_pack",
|
||||||
|
file: upload
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(400) == %{
|
||||||
|
"error" => "Pack already exists, refusing to import test_zip_pack"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
File.rm!(zip_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "handles invalid ZIP file", %{admin_conn: admin_conn} do
|
||||||
|
# Create invalid ZIP file
|
||||||
|
invalid_zip_path = Path.join(System.tmp_dir!(), "invalid.zip")
|
||||||
|
File.write!(invalid_zip_path, "not a zip file")
|
||||||
|
|
||||||
|
upload = %Plug.Upload{
|
||||||
|
content_type: "application/zip",
|
||||||
|
path: invalid_zip_path,
|
||||||
|
filename: "invalid.zip"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert admin_conn
|
||||||
|
|> put_req_header("content-type", "multipart/form-data")
|
||||||
|
|> post("/api/pleroma/emoji/packs/download_zip", %{
|
||||||
|
name: "test_invalid_pack",
|
||||||
|
file: upload
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(400) == %{
|
||||||
|
"error" => "Could not unzip pack"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
File.rm!(invalid_zip_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "handles URL download failure", %{admin_conn: admin_conn} do
|
||||||
|
mock(fn
|
||||||
|
%{method: :get, url: "https://example.com/bad_pack.zip"} ->
|
||||||
|
%Tesla.Env{status: 404, body: "Not found"}
|
||||||
|
end)
|
||||||
|
|
||||||
|
assert admin_conn
|
||||||
|
|> put_req_header("content-type", "multipart/form-data")
|
||||||
|
|> post("/api/pleroma/emoji/packs/download_zip", %{
|
||||||
|
name: "test_bad_url_pack",
|
||||||
|
url: "https://example.com/bad_pack.zip"
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(400) == %{
|
||||||
|
"error" => "Could not download pack"
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "requires either file or URL parameter", %{admin_conn: admin_conn} do
|
||||||
|
assert admin_conn
|
||||||
|
|> put_req_header("content-type", "multipart/form-data")
|
||||||
|
|> post("/api/pleroma/emoji/packs/download_zip", %{
|
||||||
|
name: "test_no_source_pack"
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(400) == %{
|
||||||
|
"error" => "Neither file nor URL was present in the request"
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "preserves existing pack.json if present in ZIP", %{admin_conn: admin_conn} do
|
||||||
|
# Create ZIP with pack.json
|
||||||
|
{:ok, zip_path} = create_test_emoji_zip_with_pack_json()
|
||||||
|
|
||||||
|
upload = %Plug.Upload{
|
||||||
|
content_type: "application/zip",
|
||||||
|
path: zip_path,
|
||||||
|
filename: "test_pack_with_json.zip"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert admin_conn
|
||||||
|
|> put_req_header("content-type", "multipart/form-data")
|
||||||
|
|> post("/api/pleroma/emoji/packs/download_zip", %{
|
||||||
|
name: "test_zip_pack",
|
||||||
|
file: upload
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(200) == "ok"
|
||||||
|
|
||||||
|
# Verify original pack.json was preserved
|
||||||
|
{:ok, pack_json} = File.read("#{@emoji_path}/test_zip_pack/pack.json")
|
||||||
|
pack_data = Jason.decode!(pack_json)
|
||||||
|
|
||||||
|
assert pack_data["pack"]["description"] == "Test pack from ZIP"
|
||||||
|
assert pack_data["pack"]["license"] == "Test License"
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
File.rm!(zip_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "rejects malicious pack names", %{admin_conn: admin_conn} do
|
||||||
|
{:ok, zip_path} = create_test_emoji_zip()
|
||||||
|
|
||||||
|
upload = %Plug.Upload{
|
||||||
|
content_type: "application/zip",
|
||||||
|
path: zip_path,
|
||||||
|
filename: "test_pack.zip"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test path traversal attempts
|
||||||
|
malicious_names = ["../evil", "../../evil", ".", "..", "evil/../../../etc"]
|
||||||
|
|
||||||
|
Enum.each(malicious_names, fn name ->
|
||||||
|
assert_raise RuntimeError, ~r/Invalid or malicious pack name/, fn ->
|
||||||
|
admin_conn
|
||||||
|
|> put_req_header("content-type", "multipart/form-data")
|
||||||
|
|> post("/api/pleroma/emoji/packs/download_zip", %{
|
||||||
|
name: name,
|
||||||
|
file: upload
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
File.rm!(zip_path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp create_test_emoji_zip do
|
||||||
|
tmp_dir = System.tmp_dir!()
|
||||||
|
zip_path = Path.join(tmp_dir, "test_emoji_pack_#{:rand.uniform(10000)}.zip")
|
||||||
|
|
||||||
|
# 1x1 pixel PNG
|
||||||
|
png_data =
|
||||||
|
Base.decode64!(
|
||||||
|
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=="
|
||||||
|
)
|
||||||
|
|
||||||
|
files = [
|
||||||
|
{~c"test_emoji.png", png_data},
|
||||||
|
# Will be treated as GIF based on extension
|
||||||
|
{~c"another_emoji.gif", png_data}
|
||||||
|
]
|
||||||
|
|
||||||
|
{:ok, {_name, zip_binary}} = :zip.zip(~c"test_pack.zip", files, [:memory])
|
||||||
|
File.write!(zip_path, zip_binary)
|
||||||
|
|
||||||
|
{:ok, zip_path}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp create_test_emoji_zip_with_pack_json do
|
||||||
|
tmp_dir = System.tmp_dir!()
|
||||||
|
zip_path = Path.join(tmp_dir, "test_emoji_pack_json_#{:rand.uniform(10000)}.zip")
|
||||||
|
|
||||||
|
png_data =
|
||||||
|
Base.decode64!(
|
||||||
|
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=="
|
||||||
|
)
|
||||||
|
|
||||||
|
pack_json =
|
||||||
|
Jason.encode!(%{
|
||||||
|
pack: %{
|
||||||
|
description: "Test pack from ZIP",
|
||||||
|
license: "Test License"
|
||||||
|
},
|
||||||
|
files: %{
|
||||||
|
"test_emoji" => "test_emoji.png"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
files = [
|
||||||
|
{~c"test_emoji.png", png_data},
|
||||||
|
{~c"pack.json", pack_json}
|
||||||
|
]
|
||||||
|
|
||||||
|
{:ok, {_name, zip_binary}} = :zip.zip(~c"test_pack.zip", files, [:memory])
|
||||||
|
File.write!(zip_path, zip_binary)
|
||||||
|
|
||||||
|
{:ok, zip_path}
|
||||||
|
end
|
||||||
|
end
|
||||||
Loading…
Add table
Add a link
Reference in a new issue