who_need_help/lib/who_need_help_web/security_headers.ex
SimpleTest eb5e0f896d
Some checks are pending
Quality / full-local-gates (push) Waiting to run
Isolate test and production deployments
2026-07-21 17:46:34 +03:00

60 lines
1.7 KiB
Elixir

defmodule WhoNeedHelpWeb.SecurityHeaders do
@moduledoc false
import Plug.Conn
def put_content_security_policy(conn, _opts) do
tile_origin =
:who_need_help
|> Application.fetch_env!(:map_tile_url)
|> origin()
websocket_origin =
case conn.scheme do
:http -> origin(%URI{scheme: "ws", host: conn.host, port: conn.port})
:https -> origin(%URI{scheme: "wss", host: conn.host, port: conn.port})
_other -> nil
end
connect_sources = sources(["'self'", tile_origin, websocket_origin])
image_sources = sources(["'self'", "data:", "blob:", tile_origin])
google_oauth_origin =
:who_need_help
|> Application.get_env(:google_oauth_base_url)
|> origin()
form_sources = sources(["'self'", google_oauth_origin])
policy =
[
"default-src 'self'",
"base-uri 'self'",
"frame-ancestors 'none'",
"object-src 'none'",
"script-src 'self'",
"style-src 'self' 'unsafe-inline'",
"img-src #{image_sources}",
"connect-src #{connect_sources}",
"worker-src 'self' blob:",
"manifest-src 'self'",
"form-action #{form_sources}"
]
|> Enum.join("; ")
put_resp_header(conn, "content-security-policy", policy)
end
defp sources(values),
do: values |> Enum.reject(&is_nil/1) |> Enum.uniq() |> Enum.join(" ")
defp origin(url) when is_binary(url), do: url |> URI.parse() |> origin()
defp origin(%URI{scheme: scheme, host: host} = uri)
when scheme in ["http", "https", "ws", "wss"] and is_binary(host) and host != "" do
URI.to_string(%URI{scheme: scheme, host: host, port: uri.port})
end
defp origin(_uri), do: nil
end