Merge pull request 'Add sane defaults for :database_config_whitelist, add a task to remove non-whitelisted configs' (#7837) from pleroma-database-config-whitelist into develop

Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7837
This commit is contained in:
nicole mikołajczyk 2026-03-02 22:38:31 +00:00
commit 3620726ff3
8 changed files with 169 additions and 5 deletions

View file

@ -0,0 +1 @@
Add reasonable defaults for :database_config_whitelist

View file

@ -960,6 +960,15 @@ config :pleroma, Pleroma.Search.QdrantSearch,
vectors: %{size: 384, distance: "Cosine"} vectors: %{size: 384, distance: "Cosine"}
} }
config :pleroma, :database_config_whitelist, [
{:pleroma},
{:cors_plug},
{:ex_aws, :s3},
{:mime},
{:prometheus, Pleroma.Web.Endpoint.MetricsExporter},
{:web_push_encryption, :vapid_details}
]
# Import environment specific config. This must remain at the bottom # Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above. # of this file so it overrides the configuration defined above.
import_config "#{Mix.env()}.exs" import_config "#{Mix.env()}.exs"

View file

@ -169,4 +169,18 @@ This forcibly removes any enabled MRF that does not exist and will fix the abili
=== "From Source" === "From Source"
```sh ```sh
mix pleroma.config fix_mrf_policies mix pleroma.config fix_mrf_policies
``` ```
## Remove non-whitelisted configs from the database
This removes any configuration value that is not explicitly whitelisted by `:pleroma, :database_config_whitelist`. Might be useful after updating the whitelist.
=== "OTP"
```sh
./bin/pleroma_ctl config filter_whitelisted
```
=== "From Source"
```sh
mix pleroma.config filter_whitelisted
```

View file

@ -1132,8 +1132,9 @@ Boolean, enables/disables in-database configuration. Read [Transferring the conf
List of valid configuration sections which are allowed to be configured from the List of valid configuration sections which are allowed to be configured from the
database. Settings stored in the database before the whitelist is configured are database. Settings stored in the database before the whitelist is configured are
still applied, so it is suggested to only use the whitelist on instances that still applied. Consider running the `mix pleroma.config filter_whitelisted` task
have not migrated the config to the database. after updating the whitelist. Read [Remove non-whitelisted configs from the database](../administration/CLI_tasks/config.md#remove-non-whitelisted-configs-from-the-database)
for more information.
Example: Example:
```elixir ```elixir

View file

@ -234,6 +234,61 @@ defmodule Mix.Tasks.Pleroma.Config do
end) end)
end end
# Removes non-whitelisted configuration sections
def run(["filter_whitelisted" | rest]) do
{options, [], []} =
OptionParser.parse(
rest,
strict: [force: :boolean],
aliases: [f: :force]
)
force = Keyword.get(options, :force, false)
start_pleroma()
whitelisted_configs = Pleroma.Config.get(:database_config_whitelist)
if whitelisted_configs in [nil, false] do
shell_error("No unwanted settings in ConfigDB. No changes made.")
else
whitelisted_groups =
whitelisted_configs
|> Enum.filter(fn
{_group} -> true
_ -> false
end)
|> Enum.map(fn {group} -> group end)
whitelisted_keys =
whitelisted_configs
|> Enum.filter(fn
{_group, _key} -> true
_ -> false
end)
filtered =
from(c in ConfigDB)
|> Repo.all()
|> Enum.filter(&not_whitelisted?(&1, whitelisted_groups, whitelisted_keys))
if not Enum.empty?(filtered) do
shell_info("The following settings will be removed from ConfigDB:\n")
Enum.each(filtered, &dump(&1))
if force or shell_prompt("Are you sure you want to continue?", "n") in ~w(Yn Y y) do
filtered_ids = Enum.map(filtered, fn %{id: id} -> id end)
Repo.delete_all(from(c in ConfigDB, where: c.id in ^filtered_ids))
else
shell_error("No changes made.")
end
else
shell_error("No unwanted settings in ConfigDB. No changes made.")
end
end
end
@spec migrate_to_db(Path.t() | nil) :: any() @spec migrate_to_db(Path.t() | nil) :: any()
def migrate_to_db(file_path \\ nil) do def migrate_to_db(file_path \\ nil) do
with :ok <- Pleroma.Config.DeprecationWarnings.warn() do with :ok <- Pleroma.Config.DeprecationWarnings.warn() do
@ -434,4 +489,9 @@ defmodule Mix.Tasks.Pleroma.Config do
Ecto.Adapters.SQL.query!(Repo, "TRUNCATE config;") Ecto.Adapters.SQL.query!(Repo, "TRUNCATE config;")
Ecto.Adapters.SQL.query!(Repo, "ALTER SEQUENCE config_id_seq RESTART;") Ecto.Adapters.SQL.query!(Repo, "ALTER SEQUENCE config_id_seq RESTART;")
end end
defp not_whitelisted?(%{group: group, key: key}, whitelisted_groups, whitelisted_keys) do
not Enum.member?(whitelisted_groups, group) and
not Enum.member?(whitelisted_keys, {group, key})
end
end end

View file

@ -174,6 +174,8 @@ defmodule Pleroma.Web.AdminAPI.ConfigController do
end end
end end
defp whitelisted_config?(":pleroma", ":database_config_whitelist"), do: false
defp whitelisted_config?(group, key) do defp whitelisted_config?(group, key) do
if whitelisted_configs = Config.get(:database_config_whitelist) do if whitelisted_configs = Config.get(:database_config_whitelist) do
Enum.any?(whitelisted_configs, fn Enum.any?(whitelisted_configs, fn

View file

@ -329,5 +329,39 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do
assert config_records() == [] assert config_records() == []
end end
test "filters non-whitelisted settings" do
clear_config(:database_config_whitelist, [
{:pleroma},
{:web_push_encryption, :vapid_details}
])
insert_config_record(:web_push_encryption, :non_whitelisted_key, a: 1)
insert_config_record(:web_push_encryption, :vapid_details, b: 1)
MixTask.run(["filter_whitelisted", "--force"])
assert [
%ConfigDB{group: :pleroma, key: :instance},
%ConfigDB{group: :pleroma, key: Pleroma.Captcha},
%ConfigDB{group: :web_push_encryption, key: :vapid_details}
] = config_records()
end
test "filter_whitelisted doesn't crash when whitelist is unset" do
clear_config(:database_config_whitelist, nil)
existing = config_records()
MixTask.run(["filter_whitelisted", "--force"])
assert config_records() == existing
end
test "filter_whitelisted doesn't crash when whitelist is disabled" do
clear_config(:database_config_whitelist, false)
existing = config_records()
MixTask.run(["filter_whitelisted", "--force"])
assert config_records() == existing
end
end end
end end

View file

@ -194,6 +194,16 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
setup do: clear_config(:configurable_from_database, true) setup do: clear_config(:configurable_from_database, true)
setup do:
clear_config(:database_config_whitelist, [
{:pleroma},
{:http},
{:idna},
{:oban},
{:tesla},
{:ueberauth}
])
test "create new config setting in db", %{conn: conn} do test "create new config setting in db", %{conn: conn} do
ueberauth = Application.get_env(:ueberauth, Ueberauth) ueberauth = Application.get_env(:ueberauth, Ueberauth)
on_exit(fn -> Application.put_env(:ueberauth, Ueberauth, ueberauth) end) on_exit(fn -> Application.put_env(:ueberauth, Ueberauth, ueberauth) end)
@ -807,7 +817,7 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
%{ %{
"tuple" => [ "tuple" => [
"/websocket", "/websocket",
"Phoenix.Endpoint.CowboyWebSocket", ":sth",
%{ %{
"tuple" => [ "tuple" => [
"Phoenix.Transports.WebSocket", "Phoenix.Transports.WebSocket",
@ -871,7 +881,7 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
%{ %{
"tuple" => [ "tuple" => [
"/websocket", "/websocket",
"Phoenix.Endpoint.CowboyWebSocket", ":sth",
%{ %{
"tuple" => [ "tuple" => [
"Phoenix.Transports.WebSocket", "Phoenix.Transports.WebSocket",
@ -1210,6 +1220,31 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
assert Application.get_env(:not_real, :anything) == "value6" assert Application.get_env(:not_real, :anything) == "value6"
end end
test "doesn't allow updating the database_config_whitelist itself", %{conn: conn} do
original_whitelist = Pleroma.Config.get(:database_config_whitelist)
refute ConfigDB.get_by_group_and_key(:pleroma, :database_config_whitelist)
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/config", %{
configs: [
%{
group: ":pleroma",
key: ":database_config_whitelist",
value: [%{"tuple" => [":pleroma", ":key1"]}]
}
]
})
%{"configs" => configs} = json_response_and_validate_schema(conn, 200)
assert configs == []
assert Pleroma.Config.get(:database_config_whitelist) == original_whitelist
refute ConfigDB.get_by_group_and_key(:pleroma, :database_config_whitelist)
end
test "args for Pleroma.Upload.Filter.Mogrify with custom tuples", %{conn: conn} do test "args for Pleroma.Upload.Filter.Mogrify with custom tuples", %{conn: conn} do
assert conn assert conn
|> put_req_header("content-type", "application/json") |> put_req_header("content-type", "application/json")
@ -1472,5 +1507,13 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
web_endpoint = Enum.find(children, fn c -> c["key"] == "Pleroma.Upload" end) web_endpoint = Enum.find(children, fn c -> c["key"] == "Pleroma.Upload" end)
assert web_endpoint["children"] assert web_endpoint["children"]
end end
test "all keys from description are whitelisted", %{conn: conn} do
conn = get(conn, "/api/pleroma/admin/config/descriptions")
assert response = json_response_and_validate_schema(conn, 200)
assert length(response) == length(Pleroma.Docs.JSON.compiled_descriptions())
end
end end
end end