Packages
sobelow
0.7.6
0.14.1
0.14.0
0.13.0
0.12.2
0.12.1
0.12.0
0.11.1
0.11.0
0.10.6
0.10.5
0.10.4
0.10.3
0.10.2
0.10.1
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.8.0
0.7.8
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.12
0.3.11
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
Security-focused static analysis for Elixir & the Phoenix framework
Current section
Files
Jump to
Current section
Files
lib/sobelow/config/csp.ex
defmodule Sobelow.Config.CSP do @moduledoc """ # Missing Content-Security-Policy Content-Security-Policy is an HTTP header that helps mitigate a number of attacks, including Cross-Site Scripting. Read more about CSP here: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP Missing Content-Security-Policy is flagged by `sobelow` when a pipeline implements the `:put_secure_browser_headers` plug, but does not provide a Content-Security-Policy header in the custom headers map. Documentation on the `put_secure_browser_headers` plug function can be found here: https://hexdocs.pm/phoenix/Phoenix.Controller.html#put_secure_browser_headers/2 Content-Security-Policy checks can be ignored with the following command: $ mix sobelow -i Config.CSP """ alias Sobelow.Utils use Sobelow.Finding @finding_type "Missing Content-Security-Policy" def run(router, _) do meta_file = Utils.ast(router) |> Utils.get_meta_funs() Utils.get_pipelines(router) |> Enum.map(&check_vuln_pipeline(&1, meta_file)) |> Enum.each(fn {vuln?, conf, pipeline} -> if vuln?, do: add_finding(pipeline, conf, router) end) end def check_vuln_pipeline({:pipeline, _, [_name, [do: block]]} = pipeline, meta_file) do {vuln?, conf} = Utils.get_plug_list(block) |> Enum.find(&is_header_plug?/1) |> missing_csp_status(meta_file) {vuln?, conf, pipeline} end defp is_header_plug?({:plug, _, [:put_secure_browser_headers]}), do: true defp is_header_plug?({:plug, _, [:put_secure_browser_headers, _]}), do: true defp is_header_plug?(_), do: false defp missing_csp_status({_, _, [:put_secure_browser_headers]}, _), do: {true, :high} defp missing_csp_status({_, _, [:put_secure_browser_headers, {:%{}, _, opts}]}, _) do {!include_csp?(opts), :high} end defp missing_csp_status({_, _, [:put_secure_browser_headers, {:@, _, opts}]}, meta_file) do [{attr, _, nil} | _] = opts has_csp? = Enum.find_value(meta_file.module_attrs, fn mod_attr -> case mod_attr do {^attr, _, [{:%{}, _, definition}]} -> definition _ -> false end end) |> include_csp?() {!has_csp?, :high} end defp missing_csp_status({_, _, [:put_secure_browser_headers, _]}, _), do: {true, :low} defp missing_csp_status(_, _), do: {false, :high} defp include_csp?(nil), do: false defp include_csp?(headers) do Enum.any?(headers, fn {key, _} when is_binary(key) -> String.downcase(key) == "content-security-policy" _ -> false end) end defp add_finding({:pipeline, [line: line_no], [pipeline_name, _]} = pipeline, conf, router) do router_path = "File: #{Utils.normalize_path(router)}" custom_header = "Pipeline: #{pipeline_name}:#{line_no}" case Sobelow.format() do "json" -> finding = [ type: @finding_type, pipeline: "#{pipeline_name}:#{line_no}" ] Sobelow.log_finding(finding, conf) "txt" -> Sobelow.log_finding(@finding_type, conf) Utils.print_custom_finding_metadata( pipeline, :put_secure_browser_headers, conf, @finding_type, [router_path, custom_header] ) "compact" -> Utils.log_compact_finding(@finding_type, conf) _ -> Sobelow.log_finding(@finding_type, conf) end endend