defmodule PlugSecureHeaders do use Pipe import Plug.Conn, only: [halt: 1, delete_resp_header: 2, update_resp_header: 4] @secure_config [ warn_only: false, merge_config: false, report_only: false, use_secure_config: true, config: [ content_security_policy: "default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self'; style-src 'self';", http_public_key_pins: "", strict_transport_security: "max-age=631138519", x_content_type_options: "nosniff", x_download_options: "noopen", x_frame_options: "sameorigin", x_permitted_cross_domain_policies: "none", x_xss_protection: "1; mode=block" ] ] defp get_config(options, boolean) do if(boolean) do get_in(options, [:plug_secure_headers, :config]) end end defp merge_config?(options) do get_in(options, [:plug_secure_headers, :merge_config]) end def init(options) do options = options |> merge_config? |> merge_config! validate(options) IO.puts "init:" IO.inspect options options end @doc "Callback implementation for Plug.call/2" def call(conn, options) do case Keyword.has_key?(options, :config) do false -> halt(conn) true -> set_headers(conn, options) end end defp set_headers(conn, options) when options |> is_list do conn |> delete(options[:config]) |> set(options[:config]) end defp set_headers(conn, _) do conn end defp validate(options) do pipe_matching x, {:ok, x}, options |> PlugSecureHeaders.PlugSecureHeaders.validate |> PlugSecureHeaders.ContentSecurityPolicy.validate |> PlugSecureHeaders.HttpPublicKeyPins.validate |> PlugSecureHeaders.StrictTrasportSecurity.validate |> PlugSecureHeaders.XContentTypeOptions.validate |> PlugSecureHeaders.XDownloadOptions.validate |> PlugSecureHeaders.XFrameOptions.validate |> PlugSecureHeaders.XPermittedCrossDomainPolicies.validate |> PlugSecureHeaders.XXssProtection.validate end defp set(conn, nil), do: conn defp set(conn, list) when list |> is_list do List.foldl(list, conn, fn({key, value}, conn) -> set(conn, dasherize(key), value) end) end defp set(conn, _key, nil), do: conn defp set(conn, key, value) when value |> is_bitstring do update_resp_header(conn, key, value, fn(_) -> value end) end defp delete(conn, nil), do: conn defp delete(conn, []), do: conn defp delete(conn, [h|t]) when h |> is_bitstring do conn |> delete_resp_header(h) |> delete(t) end defp delete(conn, list) when list |> is_list do List.foldl(list, conn, fn({key, _}, conn) -> delete(conn, dasherize(key)) end) end defp delete(conn, key) when key |> is_bitstring, do: delete(conn, [key]) defp dasherize(data) when is_atom(data), do: dasherize(Atom.to_string(data)) defp dasherize(data), do: String.replace(data, "_", "-") defp merge_config!(options) do env_options = Application.get_env(:plug_secure_headers, PlugSecureHeaders) env_config = get_config(env_options, true) config = get_config(options, true) merged_config = Keyword.merge(env_config, config) # options = set_config(options, merged_config) IO.inspect merged_config IO.puts "merge_config!" IO.inspect config Keyword.merge(env_options, options) end end