commit: d9ae9b676c2963466cbb8e440711db1759e25c31
parent b1309bdb403fdbfdb0a8b076a5a13af811191ca9
Author: Lain Soykaf <lain@lain.com>
Date: Mon, 10 Mar 2025 18:56:43 +0400
InstanceStatic: Extra-sanitize emoji
Diffstat:
3 files changed, 50 insertions(+), 18 deletions(-)
diff --git a/config/config.exs b/config/config.exs
@@ -66,7 +66,7 @@ config :pleroma, Pleroma.Upload,
filename_display_max_length: 30,
default_description: nil,
base_url: nil,
- allowed_mime_types: ["image", "audio", "video", "text"]
+ allowed_mime_types: ["image", "audio", "video"]
config :pleroma, Pleroma.Uploaders.Local, uploads: "uploads"
diff --git a/lib/pleroma/web/plugs/instance_static.ex b/lib/pleroma/web/plugs/instance_static.ex
@@ -51,25 +51,25 @@ defmodule Pleroma.Web.Plugs.InstanceStatic do
|> Map.put(:from, from)
|> Map.put(:content_types, false)
- # Get sanitized content type before calling Plug.Static
- # Include "text" to allow HTML files and other text-based content
- allowed_mime_types =
- Pleroma.Config.get([Pleroma.Upload, :allowed_mime_types], [
- "image",
- "audio",
- "video",
- "text"
- ])
-
- conn = set_content_type(conn, %{allowed_mime_types: allowed_mime_types}, conn.request_path)
+ conn = set_content_type(conn, conn.request_path)
# Call Plug.Static with our sanitized content-type
Plug.Static.call(conn, opts)
end
- defp set_content_type(conn, opts, filepath) do
+ defp set_content_type(conn, "/emoji/" <> filepath) do
real_mime = MIME.from_path(filepath)
- clean_mime = Pleroma.Web.Plugs.Utils.get_safe_mime_type(opts, real_mime)
+
+ clean_mime =
+ Pleroma.Web.Plugs.Utils.get_safe_mime_type(%{allowed_mime_types: ["image"]}, real_mime)
+
put_resp_header(conn, "content-type", clean_mime)
end
+
+ defp set_content_type(conn, filepath) do
+ real_mime = MIME.from_path(filepath)
+ put_resp_header(conn, "content-type", real_mime)
+ end
end
+
+# I think this needs to be uncleaned except for emoji.
diff --git a/test/pleroma/web/plugs/instance_static_test.exs b/test/pleroma/web/plugs/instance_static_test.exs
@@ -63,15 +63,47 @@ defmodule Pleroma.Web.Plugs.InstanceStaticTest do
assert html_response(index, 200) == "<h1>rabbit hugs as a service</h1>"
end
- test "sanitizes content-types for potentially dangerous file extensions" do
+ test "does not sanitize dangerous files in general, as there can be html and javascript files legitimately in this folder" do
# Create a file with a potentially dangerous extension (.json)
# This mimics an attacker trying to serve ActivityPub JSON with a static file
File.mkdir!(@dir <> "/static")
File.write!(@dir <> "/static/malicious.json", "{\"type\": \"ActivityPub\"}")
- # Request the malicious file
conn = get(build_conn(), "/static/malicious.json")
+ assert conn.status == 200
+
+ content_type =
+ Enum.find_value(conn.resp_headers, fn
+ {"content-type", value} -> value
+ _ -> nil
+ end)
+
+ assert content_type == "application/json"
+
+ File.write!(@dir <> "/static/safe.jpg", "fake image data")
+
+ conn = get(build_conn(), "/static/safe.jpg")
+
+ assert conn.status == 200
+
+ # Get the content-type
+ content_type =
+ Enum.find_value(conn.resp_headers, fn
+ {"content-type", value} -> value
+ _ -> nil
+ end)
+
+ assert content_type == "image/jpeg"
+ end
+
+ test "always sanitizes emojis to images" do
+ File.mkdir!(@dir <> "/emoji")
+ File.write!(@dir <> "/emoji/malicious.html", "<script>HACKED</script>")
+
+ # Request the malicious file
+ conn = get(build_conn(), "/emoji/malicious.html")
+
# Verify the file was served (status 200)
assert conn.status == 200
@@ -87,10 +119,10 @@ defmodule Pleroma.Web.Plugs.InstanceStaticTest do
assert content_type == "application/octet-stream"
# Create a file with an allowed extension (.jpg)
- File.write!(@dir <> "/static/safe.jpg", "fake image data")
+ File.write!(@dir <> "/emoji/safe.jpg", "fake image data")
# Request the safe file
- conn = get(build_conn(), "/static/safe.jpg")
+ conn = get(build_conn(), "/emoji/safe.jpg")
# Verify the file was served (status 200)
assert conn.status == 200