akkoma/lib/pleroma/web/plugs/instance_static.ex
Oneric 066d5b48ed Fix Content-Type sanitisation for emoji and local uploads
This was accidentally broken in c8e0f7848b
due to a one-letter mistake in the plug option name and an absence of
tests. Therefore it was once again possible to serve e.g. Javascript or
CSS payloads via uploads and emoji.
However due to other protections it was still NOT possible for anyone to
serve any payload with an ActivityPub Content-Type. With the CSP policy
hardening from previous JS payload exloits predating the Content-Type
sanitisation, there is currently no known way of abusing this weakened
Content-Type sanitisation, but should be fixed regardless.

This commit fixes the option name and adds tests to ensure
such a regression doesn't occur again in the future.

Reported-by: Lain Soykaf <lain@lain.com>
2025-03-10 19:45:26 +01:00

71 lines
2 KiB
Elixir

# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.InstanceStatic do
import Plug.Conn
require Pleroma.Constants
alias Pleroma.Web.Plugs.Utils
@moduledoc """
This is a shim to call `Plug.Static` but with runtime `from` configuration.
Mountpoints are defined directly in the module to avoid calling the configuration for every request including non-static ones.
"""
@behaviour Plug
def file_path(path, frontend_type \\ :primary) do
instance_path =
Path.join(Pleroma.Config.get([:instance, :static_dir], "instance/static/"), path)
frontend_path = Pleroma.Web.Plugs.FrontendStatic.file_path(path, frontend_type)
(File.exists?(instance_path) && instance_path) ||
(frontend_path && File.exists?(frontend_path) && frontend_path) ||
Path.join(Application.app_dir(:pleroma, "priv/static/"), path)
end
def init(opts) do
opts
|> Keyword.put(:from, "__unconfigured_instance_static_plug")
|> Plug.Static.init()
end
for only <- Pleroma.Constants.static_only_files() do
def call(%{request_path: "/" <> unquote(only) <> _} = conn, opts) do
call_static(
conn,
opts,
Pleroma.Config.get([:instance, :static_dir], "instance/static")
)
end
end
def call(conn, _) do
conn
end
defp set_static_content_type(conn, "/emoji/" <> _ = request_path) do
real_mime = MIME.from_path(request_path)
safe_mime = Utils.get_safe_mime_type(%{allowed_mime_types: ["image"]}, real_mime)
put_resp_header(conn, "content-type", safe_mime)
end
defp set_static_content_type(conn, request_path) do
put_resp_header(conn, "content-type", MIME.from_path(request_path))
end
defp call_static(%{request_path: request_path} = conn, opts, from) do
opts =
opts
|> Map.put(:from, from)
|> Map.put(:content_types, false)
conn
|> set_static_content_type(request_path)
|> Plug.Static.call(opts)
end
end