Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85710865
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
9 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
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)
Attached To
Mode
rPUBE pleroma-upstream
Attached
Detach File
Event Timeline
Log In to Comment