Compare commits

..

1 commit

Author SHA1 Message Date
e03206a9a0 use postgres 15 in CI 2022-12-31 18:29:16 +00:00
17 changed files with 86 additions and 163 deletions

View file

@ -41,7 +41,7 @@ variables:
services:
postgres:
image: postgres:13
image: postgres:15
when:
event:
- pull_request

View file

@ -29,7 +29,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Quote posts are now considered as part of the same thread as the post they are quoting
- Simplified HTTP signature processing
- Rich media will now hard-exit after 5 seconds, to prevent timeline hangs
- HTTP Content Security Policy is now far more strict to prevent any potential XSS/CSS leakages
### Fixed
- /api/v1/accounts/lookup will now respect restrict\_unauthenticated

View file

@ -11,11 +11,11 @@ If you want to generate a restrictive `robots.txt`, you can run the following mi
=== "OTP"
```sh
./bin/pleroma_ctl robotstxt disallow_all
./bin/pleroma_ctl robots_txt disallow_all
```
=== "From Source"
```sh
mix pleroma.robotstxt disallow_all
mix pleroma.robots_txt disallow_all
```

View file

@ -1,49 +0,0 @@
defmodule Pleroma.PrometheusExporter do
@moduledoc """
Exports metrics in Prometheus format.
Mostly exists because of https://github.com/beam-telemetry/telemetry_metrics_prometheus_core/issues/52
Basically we need to fetch metrics every so often, or the lib will let them pile up and eventually crash the VM.
It also sorta acts as a cache so there is that too.
"""
use GenServer
require Logger
def start_link(_opts) do
GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
end
def init(_opts) do
schedule_next()
{:ok, ""}
end
defp schedule_next do
Process.send_after(self(), :gather, 60_000)
end
# Scheduled function, gather metrics and schedule next run
def handle_info(:gather, _state) do
schedule_next()
state = TelemetryMetricsPrometheus.Core.scrape()
{:noreply, state}
end
# Trigger the call dynamically, mostly for testing
def handle_call(:gather, _from, _state) do
state = TelemetryMetricsPrometheus.Core.scrape()
{:reply, state, state}
end
def handle_call(:show, _from, state) do
{:reply, state, state}
end
def show do
GenServer.call(__MODULE__, :show)
end
def gather do
GenServer.call(__MODULE__, :gather)
end
end

View file

@ -14,7 +14,6 @@ defmodule Pleroma.Upload.Filter.Exiftool do
# Formats not compatible with exiftool at this time
def filter(%Pleroma.Upload{content_type: "image/heic"}), do: {:ok, :noop}
def filter(%Pleroma.Upload{content_type: "image/webp"}), do: {:ok, :noop}
def filter(%Pleroma.Upload{content_type: "image/svg+xml"}), do: {:ok, :noop}
def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _}) do
try do

View file

@ -132,6 +132,66 @@ defp maybe_halt_on_missing_oauth_scopes_check(conn) do
end
end
def view do
quote do
use Phoenix.View,
root: "lib/pleroma/web/templates",
namespace: Pleroma.Web
# Import convenience functions from controllers
import Phoenix.Controller, only: [get_csrf_token: 0, get_flash: 2, view_module: 1]
import Pleroma.Web.ErrorHelpers
import Pleroma.Web.Gettext
alias Pleroma.Web.Router.Helpers, as: Routes
require Logger
@doc "Same as `render/3` but wrapped in a rescue block"
def safe_render(view, template, assigns \\ %{}) do
Phoenix.View.render(view, template, assigns)
rescue
error ->
Logger.error(
"#{__MODULE__} failed to render #{inspect({view, template})}\n" <>
Exception.format(:error, error, __STACKTRACE__)
)
nil
end
@doc """
Same as `render_many/4` but wrapped in rescue block.
"""
def safe_render_many(collection, view, template, assigns \\ %{}) do
Enum.map(collection, fn resource ->
as = Map.get(assigns, :as) || view.__resource__
assigns = Map.put(assigns, as, resource)
safe_render(view, template, assigns)
end)
|> Enum.filter(& &1)
end
end
end
def router do
quote do
use Phoenix.Router
# credo:disable-for-next-line Credo.Check.Consistency.MultiAliasImportRequireUse
import Plug.Conn
import Phoenix.Controller
end
end
def channel do
quote do
# credo:disable-for-next-line Credo.Check.Consistency.MultiAliasImportRequireUse
import Phoenix.Channel
import Pleroma.Web.Gettext
end
end
def plug do
quote do
@behaviour Pleroma.Web.Plug
@ -176,80 +236,6 @@ def call(%Plug.Conn{} = conn, options) do
end
end
def view do
quote do
use Phoenix.View,
root: "lib/pleroma/web/templates",
namespace: Pleroma.Web
# Import convenience functions from controllers
import Phoenix.Controller,
only: [get_flash: 1, get_flash: 2, view_module: 1, view_template: 1]
# Include shared imports and aliases for views
unquote(view_helpers())
end
end
def live_view do
quote do
use Phoenix.LiveView,
layout: {Pleroma.Web.LayoutView, "live.html"}
unquote(view_helpers())
end
end
def live_component do
quote do
use Phoenix.LiveComponent
unquote(view_helpers())
end
end
def component do
quote do
use Phoenix.Component
unquote(view_helpers())
end
end
def router do
quote do
use Phoenix.Router
import Plug.Conn
import Phoenix.Controller
import Phoenix.LiveView.Router
end
end
def channel do
quote do
use Phoenix.Channel
import Pleroma.Web.Gettext
end
end
defp view_helpers do
quote do
# Use all HTML functionality (forms, tags, etc)
use Phoenix.HTML
# Import LiveView and .heex helpers (live_render, live_patch, <.form>, etc)
import Phoenix.LiveView.Helpers
# Import basic rendering functionality (render, render_layout, etc)
import Phoenix.View
import Pleroma.Web.ErrorHelpers
import Pleroma.Web.Gettext
alias Pleroma.Web.Router.Helpers, as: Routes
end
end
@doc """
When used, dispatch to the appropriate controller/view/etc.
"""

View file

@ -14,11 +14,11 @@ defmodule Pleroma.Web.AdminAPI.StatusView do
defdelegate merge_account_views(user), to: AdminAPI.AccountView
def render("index.json", %{total: total} = opts) do
%{total: total, activities: render_many(opts.activities, __MODULE__, "show.json", opts)}
%{total: total, activities: safe_render_many(opts.activities, __MODULE__, "show.json", opts)}
end
def render("index.json", opts) do
render_many(opts.activities, __MODULE__, "show.json", opts)
safe_render_many(opts.activities, __MODULE__, "show.json", opts)
end
def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do

View file

@ -15,7 +15,7 @@ defmodule Pleroma.Web.AkkomaAPI.MetricsController do
def show(conn, _params) do
if Config.get([:instance, :export_prometheus_metrics], true) do
conn
|> text(Pleroma.PrometheusExporter.show())
|> text(TelemetryMetricsPrometheus.Core.scrape())
else
conn
|> send_resp(404, "Not Found")

View file

@ -12,7 +12,7 @@ defmodule Pleroma.Web.MastodonAPI.ConversationView do
alias Pleroma.Web.MastodonAPI.StatusView
def render("participations.json", %{participations: participations, for: user}) do
render_many(participations, __MODULE__, "participation.json", %{
safe_render_many(participations, __MODULE__, "participation.json", %{
as: :participation,
for: user
})

View file

@ -66,7 +66,7 @@ def render("index.json", %{notifications: notifications, for: reading_user} = op
|> Map.put(:parent_activities, parent_activities)
|> Map.put(:relationships, relationships_opt)
render_many(notifications, NotificationView, "show.json", opts)
safe_render_many(notifications, NotificationView, "show.json", opts)
end
def render(

View file

@ -131,7 +131,7 @@ def render("index.json", opts) do
|> Map.put(:parent_activities, parent_activities)
|> Map.put(:relationships, relationships_opt)
render_many(activities, StatusView, "show.json", opts)
safe_render_many(activities, StatusView, "show.json", opts)
end
def render(

View file

@ -4,7 +4,7 @@ defmodule Pleroma.Web.MastodonAPI.TagView do
alias Pleroma.Web.Router.Helpers
def render("index.json", %{tags: tags, for_user: user}) do
render_many(tags, __MODULE__, "show.json", %{for_user: user})
safe_render_many(tags, __MODULE__, "show.json", %{for_user: user})
end
def render("show.json", %{tag: tag, for_user: user}) do

View file

@ -106,15 +106,20 @@ defp csp_string(conn) do
connect_src =
if Config.get([:media_proxy, :enabled]) do
sources = build_csp_multimedia_source_list()
["connect-src 'self' ", static_url, ?\s, websocket_url, ?\s, sources]
["connect-src 'self' blob: ", static_url, ?\s, websocket_url, ?\s, sources]
else
["connect-src 'self' ", static_url, ?\s, websocket_url]
["connect-src 'self' blob: ", static_url, ?\s, websocket_url]
end
style_src = "style-src 'self' '#{nonce_tag}'"
font_src = "font-src 'self'"
style_src = "style-src 'self' 'unsafe-inline'"
font_src = "font-src 'self' data:"
script_src = "script-src 'self' '#{nonce_tag}'"
script_src =
if Config.get(:env) == :dev do
"script-src 'self' 'unsafe-eval' '#{nonce_tag}'"
else
"script-src 'self' '#{nonce_tag}'"
end
report = if report_uri, do: ["report-uri ", report_uri, ";report-to csp-endpoint"]
insecure = if scheme == "https", do: "upgrade-insecure-requests"

View file

@ -921,7 +921,7 @@ defmodule Pleroma.Web.Router do
# TODO: Change to Phoenix.Router.routes/1 for Phoenix 1.6.0+
def get_api_routes do
Phoenix.Router.routes(__MODULE__)
__MODULE__.__routes__()
|> Enum.reject(fn r -> r.plug == Pleroma.Web.Fallback.RedirectController end)
|> Enum.map(fn r ->
r.path

View file

@ -2,7 +2,6 @@ defmodule Pleroma.Web.Telemetry do
use Supervisor
import Telemetry.Metrics
alias Pleroma.Stats
alias Pleroma.Config
def start_link(arg) do
Supervisor.start_link(__MODULE__, arg, name: __MODULE__)
@ -10,28 +9,14 @@ def start_link(arg) do
@impl true
def init(_arg) do
children =
[
{:telemetry_poller, measurements: periodic_measurements(), period: 10_000}
] ++
prometheus_children()
children = [
{:telemetry_poller, measurements: periodic_measurements(), period: 10_000},
{TelemetryMetricsPrometheus.Core, metrics: prometheus_metrics()}
]
Supervisor.init(children, strategy: :one_for_one)
end
defp prometheus_children do
config = Config.get([:instance, :export_prometheus_metrics], true)
if config do
[
{TelemetryMetricsPrometheus.Core, metrics: prometheus_metrics()},
Pleroma.PrometheusExporter
]
else
[]
end
end
# A seperate set of metrics for distributions because phoenix dashboard does NOT handle them well
defp distribution_metrics do
[

View file

@ -5,8 +5,6 @@ defmodule Pleroma.Web.AkkomaAPI.MetricsControllerTest do
test "should return metrics when the user has admin:metrics" do
%{conn: conn} = oauth_access(["admin:metrics"])
Pleroma.PrometheusExporter.gather()
resp =
conn
|> get("/api/v1/akkoma/metrics")

View file

@ -140,7 +140,7 @@ defp assert_media_img_src(conn, url) do
defp assert_connect_src(conn, url) do
conn = get(conn, "/api/v1/instance")
[csp] = Conn.get_resp_header(conn, "content-security-policy")
assert csp =~ ~r/connect-src 'self' [^;]+ #{url}/
assert csp =~ ~r/connect-src 'self' blob: [^;]+ #{url}/
end
test "it does not send CSP headers when disabled", %{conn: conn} do