More fixes for InstanceStatic
This commit is contained in:
parent
1dd9ba5d6f
commit
b1309bdb40
3 changed files with 66 additions and 1 deletions
config
lib/pleroma/web/plugs
test/pleroma/web/plugs
|
@ -66,7 +66,7 @@ config :pleroma, Pleroma.Upload,
|
||||||
filename_display_max_length: 30,
|
filename_display_max_length: 30,
|
||||||
default_description: nil,
|
default_description: nil,
|
||||||
base_url: nil,
|
base_url: nil,
|
||||||
allowed_mime_types: ["image", "audio", "video"]
|
allowed_mime_types: ["image", "audio", "video", "text"]
|
||||||
|
|
||||||
config :pleroma, Pleroma.Uploaders.Local, uploads: "uploads"
|
config :pleroma, Pleroma.Uploaders.Local, uploads: "uploads"
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.Plugs.InstanceStatic do
|
defmodule Pleroma.Web.Plugs.InstanceStatic do
|
||||||
require Pleroma.Constants
|
require Pleroma.Constants
|
||||||
|
import Plug.Conn, only: [put_resp_header: 3]
|
||||||
|
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
This is a shim to call `Plug.Static` but with runtime `from` configuration.
|
This is a shim to call `Plug.Static` but with runtime `from` configuration.
|
||||||
|
@ -44,10 +45,31 @@ defmodule Pleroma.Web.Plugs.InstanceStatic do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp call_static(conn, opts, from) do
|
defp call_static(conn, opts, from) do
|
||||||
|
# Prevent content-type spoofing by setting content_types: false
|
||||||
opts =
|
opts =
|
||||||
opts
|
opts
|
||||||
|> Map.put(:from, from)
|
|> 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)
|
||||||
|
|
||||||
|
# Call Plug.Static with our sanitized content-type
|
||||||
Plug.Static.call(conn, opts)
|
Plug.Static.call(conn, opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp set_content_type(conn, opts, filepath) do
|
||||||
|
real_mime = MIME.from_path(filepath)
|
||||||
|
clean_mime = Pleroma.Web.Plugs.Utils.get_safe_mime_type(opts, real_mime)
|
||||||
|
put_resp_header(conn, "content-type", clean_mime)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -62,4 +62,47 @@ defmodule Pleroma.Web.Plugs.InstanceStaticTest do
|
||||||
index = get(build_conn(), "/static/kaniini.html")
|
index = get(build_conn(), "/static/kaniini.html")
|
||||||
assert html_response(index, 200) == "<h1>rabbit hugs as a service</h1>"
|
assert html_response(index, 200) == "<h1>rabbit hugs as a service</h1>"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "sanitizes content-types for potentially dangerous file extensions" 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")
|
||||||
|
|
||||||
|
# Verify the file was served (status 200)
|
||||||
|
assert conn.status == 200
|
||||||
|
|
||||||
|
# The content should be served, but with a sanitized content-type
|
||||||
|
content_type =
|
||||||
|
Enum.find_value(conn.resp_headers, fn
|
||||||
|
{"content-type", value} -> value
|
||||||
|
_ -> nil
|
||||||
|
end)
|
||||||
|
|
||||||
|
# It should have been sanitized to application/octet-stream because "application"
|
||||||
|
# is not in the allowed_mime_types list
|
||||||
|
assert content_type == "application/octet-stream"
|
||||||
|
|
||||||
|
# Create a file with an allowed extension (.jpg)
|
||||||
|
File.write!(@dir <> "/static/safe.jpg", "fake image data")
|
||||||
|
|
||||||
|
# Request the safe file
|
||||||
|
conn = get(build_conn(), "/static/safe.jpg")
|
||||||
|
|
||||||
|
# Verify the file was served (status 200)
|
||||||
|
assert conn.status == 200
|
||||||
|
|
||||||
|
# Get the content-type
|
||||||
|
content_type =
|
||||||
|
Enum.find_value(conn.resp_headers, fn
|
||||||
|
{"content-type", value} -> value
|
||||||
|
_ -> nil
|
||||||
|
end)
|
||||||
|
|
||||||
|
# It should be preserved because "image" is in the allowed_mime_types list
|
||||||
|
assert content_type == "image/jpeg"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Reference in a new issue