Page MenuHomePhorge

No OneTemporary

Size
9 KB
Referenced Files
None
Subscribers
None
diff --git a/changelog.d/frontendstatic-plug-crash.fix b/changelog.d/frontendstatic-plug-crash.fix
new file mode 100644
index 000000000..7fe5dffad
--- /dev/null
+++ b/changelog.d/frontendstatic-plug-crash.fix
@@ -0,0 +1 @@
+Fix FrontendStatic plug crash with invalid config
diff --git a/lib/pleroma/web/plugs/frontend_static.ex b/lib/pleroma/web/plugs/frontend_static.ex
index f1df185e3..e347665dd 100644
--- a/lib/pleroma/web/plugs/frontend_static.ex
+++ b/lib/pleroma/web/plugs/frontend_static.ex
@@ -1,108 +1,111 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.FrontendStatic do
require Pleroma.Constants
@frontend_cookie_name "preferred_frontend"
@moduledoc """
This is a shim to call `Plug.Static` but with runtime `from` configuration`. It dispatches to the different frontends.
"""
@behaviour Plug
defp instance_static_path do
Pleroma.Config.get([:instance, :static_dir], "instance/static")
end
def file_path(path, frontend_type \\ :primary)
def file_path(path, frontend_type) when is_atom(frontend_type) do
if configuration = Pleroma.Config.get([:frontends, frontend_type]) do
+ # name and ref are required configs, but user can still set an invalid config
+ # via prod.secret.exs. Instead of crashing on a nil value, default to nothing
+ # and serve likely the bundled FE.
Path.join([
instance_static_path(),
"frontends",
- configuration["name"],
- configuration["ref"],
+ configuration["name"] || "",
+ configuration["ref"] || "",
path
])
else
nil
end
end
def file_path(path, frontend_type) when is_binary(frontend_type) do
Path.join([
instance_static_path(),
"frontends",
frontend_type,
path
])
end
def init(opts) do
opts
|> Keyword.put(:from, "__unconfigured_frontend_static_plug")
|> Plug.Static.init()
|> Map.put(:frontend_type, opts[:frontend_type])
end
def call(conn, opts) do
with false <- api_route?(conn.path_info),
false <- invalid_path?(conn.path_info),
fallback_frontend_type <- Map.get(opts, :frontend_type, :primary),
frontend_type <- preferred_or_fallback(conn, fallback_frontend_type),
path when not is_nil(path) <- file_path("", frontend_type) do
call_static(conn, opts, path)
else
_ ->
conn
end
end
def preferred_frontend(conn) do
%{req_cookies: cookies} =
conn
|> Plug.Conn.fetch_cookies()
Map.get(cookies, @frontend_cookie_name)
end
# Only override primary frontend
def preferred_or_fallback(conn, :primary) do
case preferred_frontend(conn) do
nil ->
:primary
frontend ->
if Enum.member?(Pleroma.Config.get([:frontends, :pickable], []), frontend) do
frontend
else
:primary
end
end
end
def preferred_or_fallback(_conn, fallback), do: fallback
defp invalid_path?(list) do
invalid_path?(list, :binary.compile_pattern(["/", "\\", ":", "\0"]))
end
defp invalid_path?([h | _], _match) when h in [".", "..", ""], do: true
defp invalid_path?([h | t], match), do: String.contains?(h, match) or invalid_path?(t)
defp invalid_path?([], _match), do: false
defp api_route?([]), do: false
defp api_route?([h | t]) do
api_routes = Pleroma.Web.Router.get_api_routes()
if h in api_routes, do: true, else: api_route?(t)
end
defp call_static(conn, opts, from) do
opts = Map.put(opts, :from, from)
Plug.Static.call(conn, opts)
end
end
diff --git a/test/pleroma/web/plugs/frontend_static_plug_test.exs b/test/pleroma/web/plugs/frontend_static_plug_test.exs
index 7dc7e52cf..5c7a0ce2d 100644
--- a/test/pleroma/web/plugs/frontend_static_plug_test.exs
+++ b/test/pleroma/web/plugs/frontend_static_plug_test.exs
@@ -1,160 +1,188 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.FrontendStaticPlugTest do
use Pleroma.Web.ConnCase
import Mock
import Mox
alias Pleroma.UnstubbedConfigMock, as: ConfigMock
@dir "test/tmp/instance_static"
setup do
Pleroma.Backports.mkdir_p!(@dir)
on_exit(fn -> File.rm_rf(@dir) end)
end
setup do: clear_config([:instance, :static_dir], @dir)
test "init will give a static plug config + the frontend type" do
opts =
[
at: "/admin",
frontend_type: :admin
]
|> Pleroma.Web.Plugs.FrontendStatic.init()
assert opts[:at] == ["admin"]
assert opts[:frontend_type] == :admin
end
test "overrides existing static files", %{conn: conn} do
name = "pelmora"
ref = "uguu"
clear_config([:frontends, :primary], %{"name" => name, "ref" => ref})
path = "#{@dir}/frontends/#{name}/#{ref}"
Pleroma.Backports.mkdir_p!(path)
File.write!("#{path}/index.html", "from frontend plug")
index = get(conn, "/")
assert html_response(index, 200) == "from frontend plug"
end
test "overrides existing static files for the `pleroma/admin` path", %{conn: conn} do
name = "pelmora"
ref = "uguu"
clear_config([:frontends, :admin], %{"name" => name, "ref" => ref})
path = "#{@dir}/frontends/#{name}/#{ref}"
Pleroma.Backports.mkdir_p!(path)
File.write!("#{path}/index.html", "from frontend plug")
index = get(conn, "/pleroma/admin/")
assert html_response(index, 200) == "from frontend plug"
end
test "exclude invalid path", %{conn: conn} do
name = "pleroma-fe"
ref = "dist"
clear_config([:media_proxy, :enabled], true)
clear_config([Pleroma.Web.Endpoint, :secret_key_base], "00000000000")
clear_config([:frontends, :primary], %{"name" => name, "ref" => ref})
path = "#{@dir}/frontends/#{name}/#{ref}"
Pleroma.Backports.mkdir_p!("#{path}/proxy/rr/ss")
File.write!("#{path}/proxy/rr/ss/Ek7w8WPVcAApOvN.jpg:large", "FB image")
ConfigMock
|> stub_with(Pleroma.Test.StaticConfig)
url =
Pleroma.Web.MediaProxy.encode_url("https://pbs.twimg.com/media/Ek7w8WPVcAApOvN.jpg:large")
with_mock Pleroma.ReverseProxy,
call: fn _conn, _url, _opts -> %Plug.Conn{status: :success} end do
assert %Plug.Conn{status: :success} = get(conn, url)
end
end
+ test "works with invalid frontend config (missing name)", %{conn: conn} do
+ clear_config([:frontends, :primary], %{"ref" => "#cofe"})
+
+ index = get(conn, "/index.html")
+ assert html_response(index, 200)
+ end
+
+ test "works with invalid frontend config (empty name)", %{conn: conn} do
+ clear_config([:frontends, :primary], %{"name" => "", "ref" => "#cofe"})
+
+ index = get(conn, "/index.html")
+ assert html_response(index, 200)
+ end
+
+ test "works with invalid frontend config (missing ref)", %{conn: conn} do
+ clear_config([:frontends, :primary], %{"name" => "and dreams"})
+
+ index = get(conn, "/index.html")
+ assert html_response(index, 200)
+ end
+
+ test "works with invalid frontend config (empty ref)", %{conn: conn} do
+ clear_config([:frontends, :primary], %{"name" => "and dreams", "ref" => ""})
+
+ index = get(conn, "/index.html")
+ assert html_response(index, 200)
+ end
+
test "api routes are detected correctly" do
# If this test fails we have probably added something
# new that should be in /api/ instead
expected_routes = [
"api",
"main",
"ostatus_subscribe",
"authorize_interaction",
"oauth",
"objects",
"activities",
"notice",
"users",
"tags",
"mailer",
"frontend_switcher",
"inbox",
"relay",
"internal",
".well-known",
"nodeinfo",
"manifest.json",
"auth",
"embed",
"proxy",
"test",
"user_exists",
"check_password"
]
assert expected_routes == Pleroma.Web.Router.get_api_routes()
end
describe "preferred frontend cookie handling" do
test "returns preferred frontend file", %{conn: conn} do
name = "test-fe"
ref = "develop"
clear_config([:frontends, :pickable], ["#{name}/#{ref}"])
path = "#{@dir}/frontends/#{name}/#{ref}"
path2 = "#{@dir}/frontends/#{name}/#{ref}/packs/html"
Pleroma.Backports.mkdir_p!(path)
File.write!("#{path}/index.html", "from frontend plug")
Pleroma.Backports.mkdir_p!(path2)
File.write!("#{path2}/custom.html", "from frontend plug")
index =
conn
|> put_req_cookie("preferred_frontend", "#{name}/#{ref}")
|> get("/")
assert html_response(index, 200) == "from frontend plug"
custom =
conn
|> put_req_cookie("preferred_frontend", "#{name}/#{ref}")
|> get("/packs/html/custom.html")
assert html_response(custom, 200) == "from frontend plug"
end
test "only returns content from pickable frontends", %{conn: conn} do
clear_config([:instance, :static_dir], "instance/static")
clear_config([:frontends, :pickable], ["pleroma-fe/develop", "pl-fe/develop"])
config_file =
conn
|> put_req_cookie("preferred_frontend", "../../../config")
|> get("/config.exs")
refute response(config_file, 200) =~ "import Config"
end
end
end

File Metadata

Mime Type
text/x-diff
Expires
Sat, Sep 19, 2:49 AM (18 h, 37 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1768691
Default Alt Text
(9 KB)

Event Timeline