Current section
Files
Jump to
Current section
Files
lib/sentry/plug.ex
defmodule Sentry.Plug do
@moduledoc """
Provides basic funcitonality to handle Plug.ErrorHandler
#### Usage
Add the following to your router.ex:
use Plug.ErrorLogger
use Sentry.Plug
### Sending Post Body Params
In order to send post body parameters you need to first scrub them of sensitive information. To
do so we ask you to pass a `scrubber` key which accepts a `Plug.Conn` and returns a map with keys
to send.
def scrub_params(conn) do
conn.params # Make sure the params have been fetched.
|> Map.to_list
|> Enum.filter(fn ({key, val} ->
key in ~w(password passwd secret credit_card) ||
Regex.match?(~r/^(?:\d[ -]*?){13,16}$r/, val) # Matches Credit Cards
end)
|> Enum.into(%{})
end
Then pass it into Sentry.Plug
use Sentry.Plug, scrubber: scrub_params\1
You can also pass it in as a `{module, fun}` like so
use Sentry.Plug, scrubber: {MyModule, :scrub_params}
*Please Note*: If your are sending large files you will want to scrub them out.
### Headers Scrubber
By default we will scrub Authorization and Authentication headers from all requests before sending them.
"""
defmacro __using__(env) do
scrubber = Keyword.get(env, :scrubber, nil)
quote do
defp handle_errors(conn, %{kind: kind, reason: reason, stack: stack}) do
request = Sentry.Plug.build_request_interface_data(conn, unquote(scrubber))
exception = Exception.normalize(kind, reason, stack)
Sentry.capture_exception(exception, [stacktrace: stack, request: request])
end
end
end
def build_request_interface_data(%Plug.Conn{} = conn, scrubber) do
conn = conn
|> Plug.Conn.fetch_cookies
|> Plug.Conn.fetch_query_params
%{
url: "#{conn.scheme}://#{conn.host}:#{conn.port}#{conn.request_path}",
method: conn.method,
data: handle_request_data(conn, scrubber),
query_string: conn.query_string,
cookies: conn.req_cookies,
headers: Enum.into(conn.req_headers, %{}) |> scrub_headers(),
env: %{
"REMOTE_ADDR" => remote_address(conn.remote_ip),
"REMOTE_PORT" => remote_port(conn.peer),
"SERVER_NAME" => conn.host,
"SERVER_PORT" => conn.port,
}
}
end
def remote_address(address) do
address
|> :inet.ntoa()
|> to_string()
end
def remote_port({_, port}), do: port
defp handle_request_data(_conn, nil), do: %{}
defp handle_request_data(conn, {module, fun}) do
apply(module, fun, [conn])
end
defp handle_request_data(conn, fun) when is_function(fun) do
fun.(conn)
end
## TODO also reject too big
defp scrub_headers(data) do
Map.drop(data, ~w(authorization authentication))
end
end