diff --git a/lib/error_tracker/integrations/plug.ex b/lib/error_tracker/integrations/plug.ex index 07b24c7..f29f9a3 100644 --- a/lib/error_tracker/integrations/plug.ex +++ b/lib/error_tracker/integrations/plug.ex @@ -111,6 +111,8 @@ defmodule ErrorTracker.Integrations.Plug do conn |> build_context |> ErrorTracker.set_context() end + @sensitive_headers ["cookie", "authorization"] + defp build_context(conn = %Plug.Conn{}) do %{ "request.host" => conn.host, @@ -118,7 +120,7 @@ defmodule ErrorTracker.Integrations.Plug do "request.query" => conn.query_string, "request.method" => conn.method, "request.ip" => remote_ip(conn), - "request.headers" => conn.req_headers |> Map.new() |> Map.drop(["cookie"]), + "request.headers" => conn.req_headers |> Map.new() |> Map.drop(@sensitive_headers), # Depending on the error source, the request params may have not been fetched yet "request.params" => unless(is_struct(conn.params, Plug.Conn.Unfetched), do: conn.params) } diff --git a/test/integrations/plug_test.exs b/test/integrations/plug_test.exs new file mode 100644 index 0000000..75e2269 --- /dev/null +++ b/test/integrations/plug_test.exs @@ -0,0 +1,57 @@ +defmodule ErrorTracker.Integrations.PlugTest do + use ErrorTracker.Test.Case + + alias ErrorTracker.Integrations.Plug, as: IntegrationPlug + + @fake_callstack [] + + setup do + [conn: Phoenix.ConnTest.build_conn()] + end + + test "it reports errors, including the request headers", %{conn: conn} do + conn = conn |> Plug.Conn.put_req_header("accept", "application/json") + + IntegrationPlug.report_error( + conn, + {"an error from Phoenix", "something bad happened"}, + @fake_callstack + ) + + [error] = repo().all(ErrorTracker.Error) + + assert error.kind == "an error from Phoenix" + assert error.reason == "something bad happened" + + [occurrence] = repo().all(ErrorTracker.Occurrence) + assert occurrence.error_id == error.id + + %{"request.headers" => request_headers} = occurrence.context + assert request_headers == %{"accept" => "application/json"} + end + + test "it does not save sensitive request headers, to avoid storing them in cleartext", %{ + conn: conn + } do + conn = + conn + |> Plug.Conn.put_req_header("cookie", "who stole the cookie from the cookie jar ?") + |> Plug.Conn.put_req_header("authorization", "Bearer plz-dont-leak-my-secrets") + |> Plug.Conn.put_req_header("safe", "this can be safely stored in cleartext") + + IntegrationPlug.report_error( + conn, + {"an error from Phoenix", "something bad happened"}, + @fake_callstack + ) + + [occurrence] = repo().all(ErrorTracker.Occurrence) + + header_names = occurrence.context |> Map.get("request.headers") |> Map.keys() + + assert "cookie" not in header_names + assert "authorization" not in header_names + + assert "safe" in header_names + end +end