Skip to content

Instantly share code, notes, and snippets.

@trptcolin
Created October 26, 2018 19:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save trptcolin/ed63cd4b38a7e9e23eac2825411a3da4 to your computer and use it in GitHub Desktop.
Save trptcolin/ed63cd4b38a7e9e23eac2825411a3da4 to your computer and use it in GitHub Desktop.
defmodule MyApp.Router do
use MyApp, :router
use Plug.ErrorHandler
use Sentry.Plug
alias MyApp.ScrubError
defp handle_errors(conn, %{kind: _kind, reason: _reason, stack: _stack} = error) do
updated_error = ScrubError.call(error)
super(conn, updated_error)
end
end
defmodule MyApp.ScrubError do
def call(%{kind: _kind, reason: _reason, stack: _stack} = error) do
param_strings_to_filter = Application.get_env(:phoenix, :filter_parameters)
as_atoms = Enum.map(param_strings_to_filter, fn x -> String.to_atom(x) end)
param_strings_to_filter = param_strings_to_filter ++ as_atoms
discard_values(error, param_strings_to_filter)
end
defp discard_values(%{__struct__: mod} = struct, _params) when is_atom(mod) do
struct
end
defp discard_values(%{} = map, params) do
Enum.into(map, %{}, fn {k, v} ->
cond do
Enum.any?(params, fn x -> x == k end) -> {k, "[FILTERED]"}
true -> {k, discard_values(v, params)}
end
end)
end
defp discard_values([_ | _] = list, params) do
Enum.map(list, &discard_values(&1, params))
end
defp discard_values(other, _params), do: other
end
defmodule MyApp.ScrubErrorTest do
use ExUnit.Case
alias MyApp.ScrubError
describe "call/1" do
test "filters out password at the top level" do
error = %{
kind: :error,
reason: %{changeset: %{params: %{"password" => "top secret password"}}},
stack: [],
more: "stuff"
}
scrubbed_error = ScrubError.call(error)
assert scrubbed_error.reason.changeset.params["password"] == "[FILTERED]"
end
test "filters out nested password" do
error = %{
kind: :error,
reason: %{changeset: %{params: %{"secrets" => %{"password" => "top secret password"}}}},
stack: [],
more: "stuff"
}
scrubbed_error = ScrubError.call(error)
assert scrubbed_error.reason.changeset.params["secrets"]["password"] == "[FILTERED]"
end
test "filters out nested password as an atom" do
error = %{
kind: :error,
reason: %{changeset: %{params: %{"secrets" => %{password: "top secret password"}}}},
stack: [],
more: "stuff"
}
scrubbed_error = ScrubError.call(error)
assert scrubbed_error.reason.changeset.params["secrets"].password == "[FILTERED]"
end
test "filters out nested password underneath a list" do
error = %{
kind: :error,
reason: %{changeset: %{params: %{"secrets" => [%{password: "top secret password"}]}}},
stack: [],
more: "stuff"
}
scrubbed_error = ScrubError.call(error)
assert scrubbed_error.reason.changeset.params["secrets"] == [%{password: "[FILTERED]"}]
end
test "leaves bad changeset alone" do
error = %{kind: :error, reason: %{changeset: "bad"}, stack: [], more: "stuff"}
scrubbed_error = ScrubError.call(error)
assert scrubbed_error == error
end
test "leaves bad params alone" do
error = %{kind: :error, reason: %{changeset: %{params: "bad"}}, stack: [], more: "stuff"}
scrubbed_error = ScrubError.call(error)
assert scrubbed_error == error
end
test "leaves top-level map params alone when no filtered items exist" do
error = %{
kind: :error,
reason: %{changeset: %{params: %{"user_id" => 1}}},
stack: [],
more: "stuff"
}
scrubbed_error = ScrubError.call(error)
assert scrubbed_error == error
end
test "leaves top-level list params alone when no filtered items exist" do
error = %{
kind: :error,
reason: %{changeset: %{params: %{"user_ids" => [1]}}},
stack: [],
more: "stuff"
}
scrubbed_error = ScrubError.call(error)
assert scrubbed_error == error
end
test "leaves nested structs alone when no filtered items exist" do
error = %{
kind: :error,
reason: %{changeset: %{params: %{"users" => [%MyApp.User{}]}}},
stack: [],
more: "stuff"
}
scrubbed_error = ScrubError.call(error)
assert scrubbed_error == error
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment