mirror of
https://git.pleroma.social/pleroma/pleroma.git
synced 2025-03-12 22:52:41 +00:00
Sanitize media uploads.
This commit is contained in:
parent
b469b9d9d3
commit
1dd9ba5d6f
3 changed files with 113 additions and 9 deletions
|
@ -83,7 +83,7 @@ defmodule Pleroma.Web.Plugs.UploadedMedia do
|
|||
Map.get(opts, :static_plug_opts)
|
||||
|> Map.put(:at, [@path])
|
||||
|> Map.put(:from, directory)
|
||||
|> Map.put(:content_type, false)
|
||||
|> Map.put(:content_types, false)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|
|
|
@ -227,4 +227,93 @@ defmodule Pleroma.Web.MastodonAPI.MediaControllerTest do
|
|||
|> json_response_and_validate_schema(403)
|
||||
end
|
||||
end
|
||||
|
||||
describe "Content-Type sanitization" do
|
||||
setup do: oauth_access(["write:media", "read:media"])
|
||||
|
||||
setup do
|
||||
ConfigMock
|
||||
|> stub_with(Pleroma.Test.StaticConfig)
|
||||
|
||||
config =
|
||||
Pleroma.Config.get([Pleroma.Upload])
|
||||
|> Keyword.put(:uploader, Pleroma.Uploaders.Local)
|
||||
|
||||
clear_config([Pleroma.Upload], config)
|
||||
clear_config([Pleroma.Upload, :allowed_mime_types], ["image", "audio", "video"])
|
||||
|
||||
# Create a file with a malicious content type and dangerous extension
|
||||
malicious_file = %Plug.Upload{
|
||||
content_type: "application/activity+json",
|
||||
path: Path.absname("test/fixtures/image.jpg"),
|
||||
# JSON extension to make MIME.from_path detect application/json
|
||||
filename: "malicious.json"
|
||||
}
|
||||
|
||||
[malicious_file: malicious_file]
|
||||
end
|
||||
|
||||
test "sanitizes malicious content types when serving media", %{
|
||||
conn: conn,
|
||||
malicious_file: malicious_file
|
||||
} do
|
||||
# First upload the file with the malicious content type
|
||||
media =
|
||||
conn
|
||||
|> put_req_header("content-type", "multipart/form-data")
|
||||
|> post("/api/v1/media", %{"file" => malicious_file})
|
||||
|> json_response_and_validate_schema(:ok)
|
||||
|
||||
# Get the file URL from the response
|
||||
url = media["url"]
|
||||
|
||||
# Now make a direct request to the media URL and check the content-type header
|
||||
response =
|
||||
build_conn()
|
||||
|> get(URI.parse(url).path)
|
||||
|
||||
# Find the content-type header
|
||||
content_type_header =
|
||||
Enum.find(response.resp_headers, fn {name, _} -> name == "content-type" end)
|
||||
|
||||
# The server should detect the application/json MIME type from the .json extension
|
||||
# and replace it with application/octet-stream since it's not in allowed_mime_types
|
||||
assert content_type_header == {"content-type", "application/octet-stream"}
|
||||
|
||||
# Verify that the file was still served correctly
|
||||
assert response.status == 200
|
||||
end
|
||||
|
||||
test "allows safe content types", %{conn: conn} do
|
||||
safe_image = %Plug.Upload{
|
||||
content_type: "image/jpeg",
|
||||
path: Path.absname("test/fixtures/image.jpg"),
|
||||
filename: "safe_image.jpg"
|
||||
}
|
||||
|
||||
# Upload a file with a safe content type
|
||||
media =
|
||||
conn
|
||||
|> put_req_header("content-type", "multipart/form-data")
|
||||
|> post("/api/v1/media", %{"file" => safe_image})
|
||||
|> json_response_and_validate_schema(:ok)
|
||||
|
||||
# Get the file URL from the response
|
||||
url = media["url"]
|
||||
|
||||
# Make a direct request to the media URL and check the content-type header
|
||||
response =
|
||||
build_conn()
|
||||
|> get(URI.parse(url).path)
|
||||
|
||||
# The server should preserve the image/jpeg MIME type since it's allowed
|
||||
content_type_header =
|
||||
Enum.find(response.resp_headers, fn {name, _} -> name == "content-type" end)
|
||||
|
||||
assert content_type_header == {"content-type", "image/jpeg"}
|
||||
|
||||
# Verify that the file was served correctly
|
||||
assert response.status == 200
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,17 +3,10 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Plugs.UploadedMediaTest do
|
||||
use Pleroma.Web.ConnCase, async: false
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
alias Pleroma.StaticStubbedConfigMock
|
||||
alias Pleroma.Web.Plugs.Utils
|
||||
|
||||
setup do
|
||||
Mox.stub_with(StaticStubbedConfigMock, Pleroma.Test.StaticConfig)
|
||||
|
||||
{:ok, %{}}
|
||||
end
|
||||
|
||||
describe "content-type sanitization with Utils.get_safe_mime_type/2" do
|
||||
test "it allows safe MIME types" do
|
||||
opts = %{allowed_mime_types: ["image", "audio", "video"]}
|
||||
|
@ -34,5 +27,27 @@ defmodule Pleroma.Web.Plugs.UploadedMediaTest do
|
|||
assert Utils.get_safe_mime_type(opts, "application/javascript") ==
|
||||
"application/octet-stream"
|
||||
end
|
||||
|
||||
test "it sanitizes ActivityPub content types" do
|
||||
opts = %{allowed_mime_types: ["image", "audio", "video"]}
|
||||
|
||||
assert Utils.get_safe_mime_type(opts, "application/activity+json") ==
|
||||
"application/octet-stream"
|
||||
|
||||
assert Utils.get_safe_mime_type(opts, "application/ld+json") == "application/octet-stream"
|
||||
assert Utils.get_safe_mime_type(opts, "application/jrd+json") == "application/octet-stream"
|
||||
end
|
||||
|
||||
test "it sanitizes other potentially dangerous types" do
|
||||
opts = %{allowed_mime_types: ["image", "audio", "video"]}
|
||||
|
||||
assert Utils.get_safe_mime_type(opts, "text/html") == "application/octet-stream"
|
||||
|
||||
assert Utils.get_safe_mime_type(opts, "application/javascript") ==
|
||||
"application/octet-stream"
|
||||
|
||||
assert Utils.get_safe_mime_type(opts, "text/javascript") == "application/octet-stream"
|
||||
assert Utils.get_safe_mime_type(opts, "application/xhtml+xml") == "application/octet-stream"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue