who_need_help/lib/who_need_help_web/security_headers.ex

57 lines
1.6 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_origins =
[
origin(%URI{scheme: "ws", host: conn.host, port: conn.port}),
origin(%URI{scheme: "wss", host: conn.host, port: default_https_port(conn)})
]
|> Enum.reject(&is_nil/1)
|> Enum.uniq()
connect_sources = sources(["'self'", tile_origin | websocket_origins])
image_sources = sources(["'self'", "data:", "blob:", tile_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 'self'"
]
|> Enum.join("; ")
put_resp_header(conn, "content-security-policy", policy)
end
defp default_https_port(%Plug.Conn{port: 443}), do: 443
defp default_https_port(_conn), do: nil
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