From c82aa6a8f2ac67add64b50ad39d7e0f10db136b8 Mon Sep 17 00:00:00 2001 From: Tim Jarratt Date: Wed, 7 Jan 2026 11:44:48 +0100 Subject: [PATCH] Avoid leaking any headers that appear like they may be sensitive --- lib/error_tracker/integrations/plug.ex | 12 +++++++++--- test/integrations/plug_test.exs | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/lib/error_tracker/integrations/plug.ex b/lib/error_tracker/integrations/plug.ex index f29f9a3..a5c3ca6 100644 --- a/lib/error_tracker/integrations/plug.ex +++ b/lib/error_tracker/integrations/plug.ex @@ -111,8 +111,6 @@ 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, @@ -120,12 +118,20 @@ 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(@sensitive_headers), + "request.headers" => conn.req_headers |> Map.new() |> Map.reject(&should_not_be_leaked?/1), # 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) } end + @sensitive ~w[cookie auth key secret token password credential private] + + defp should_not_be_leaked?({key, _value}) do + downcased = key |> String.downcase() + + Enum.any?(@sensitive, &String.contains?(downcased, &1)) + end + defp remote_ip(conn = %Plug.Conn{}) do remote_ip = case Plug.Conn.get_req_header(conn, "x-forwarded-for") do diff --git a/test/integrations/plug_test.exs b/test/integrations/plug_test.exs index 75e2269..13510ef 100644 --- a/test/integrations/plug_test.exs +++ b/test/integrations/plug_test.exs @@ -37,6 +37,13 @@ defmodule ErrorTracker.Integrations.PlugTest do 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("authentication-helper", "hunter42") + |> Plug.Conn.put_req_header("important-token", "abcxyz") + |> Plug.Conn.put_req_header("private-name", "Some call me... Tim") + |> Plug.Conn.put_req_header("special-credential", "drink-your-ovaltine") + |> Plug.Conn.put_req_header("special-key", "Begin Private Key; dontleakmeplz") + |> Plug.Conn.put_req_header("special-secret", "Shh, it's a secret") + |> Plug.Conn.put_req_header("special-password", "correct-horse-battery-staple") |> Plug.Conn.put_req_header("safe", "this can be safely stored in cleartext") IntegrationPlug.report_error( @@ -51,7 +58,15 @@ defmodule ErrorTracker.Integrations.PlugTest do assert "cookie" not in header_names assert "authorization" not in header_names + assert "authentication-helper" not in header_names + assert "important-token" not in header_names + assert "private-name" not in header_names + assert "special-credential" not in header_names + assert "special-key" not in header_names + assert "special-password" not in header_names + assert "special-secret" not in header_names assert "safe" in header_names + assert length(header_names) == 1 end end