philomena/lib/philomena_web/plugs/content_security_policy_plug.ex

92 lines
2.6 KiB
Elixir
Raw Normal View History

2019-12-06 18:41:02 +01:00
defmodule PhilomenaWeb.ContentSecurityPolicyPlug do
2024-05-04 03:06:15 +02:00
import PhilomenaWeb.Config
import Plug.Conn
@allowed_sources [
:script_src,
:frame_src,
:style_src
]
2019-12-06 18:41:02 +01:00
def init(opts) do
opts
end
def call(conn, _opts) do
2019-12-06 18:41:02 +01:00
cdn_uri = cdn_uri()
camo_uri = camo_uri()
register_before_send(conn, fn conn ->
config = get_config(conn)
script_src = Keyword.get(config, :script_src, [])
style_src = Keyword.get(config, :style_src, [])
frame_src = Keyword.get(config, :frame_src, nil)
csp_config = [
{:default_src, ["'self'"]},
{:script_src, [default_script_src() | script_src]},
{:connect_src, [default_connect_src()]},
{:style_src, [default_style_src() | style_src]},
{:object_src, ["'none'"]},
{:frame_ancestors, ["'none'"]},
{:frame_src, frame_src || ["'none'"]},
{:form_action, ["'self'"]},
{:manifest_src, ["'self'"]},
2021-10-06 03:31:50 +02:00
{:img_src, ["'self'", "blob:", "data:", cdn_uri, camo_uri]},
{:media_src, ["'self'", "blob:", "data:", cdn_uri, camo_uri]},
{:block_all_mixed_content, []}
]
csp_value =
csp_config
|> Enum.map(&cspify_element/1)
|> Enum.join("; ")
2024-05-04 03:06:15 +02:00
csp_relaxed? do
if conn.status == 500 do
# Allow Plug.Debugger to function in this case
delete_resp_header(conn, "content-security-policy")
else
# Enforce CSP otherwise
put_resp_header(conn, "content-security-policy", csp_value)
end
else
put_resp_header(conn, "content-security-policy", csp_value)
end
end)
end
2019-12-06 18:41:02 +01:00
def permit_source(conn, key, value) when key in @allowed_sources do
conn
|> get_config()
2020-09-12 19:43:16 +02:00
|> Keyword.update(key, value, &(value ++ &1))
|> set_config(conn)
2019-12-06 18:41:02 +01:00
end
defp get_config(conn), do: conn.private[:csp] || []
defp set_config(value, conn), do: put_private(conn, :csp, value)
2019-12-06 18:41:02 +01:00
defp cdn_uri, do: Application.get_env(:philomena, :cdn_host) |> to_uri()
defp camo_uri, do: Application.get_env(:philomena, :camo_host) |> to_uri()
2024-05-07 19:33:56 +02:00
defp default_script_src, do: vite_hmr?(do: "*", else: "'self'")
defp default_connect_src,
2024-05-07 19:33:56 +02:00
do: vite_hmr?(do: "*", else: "'self'")
2024-05-20 22:30:41 +02:00
defp default_style_src, do: vite_hmr?(do: "'unsafe-inline' *", else: "'self'")
2019-12-06 18:41:02 +01:00
defp to_uri(host) when host in [nil, ""], do: ""
defp to_uri(host), do: URI.to_string(%URI{scheme: "https", host: host})
defp cspify_element({key, value}) do
key =
key
|> Atom.to_string()
|> String.replace("_", "-")
Enum.join([key | value], " ")
end
2020-01-11 05:20:19 +01:00
end