View Source Sentry.Scrubber (Sentry v14.0.0)
Shared, framework-agnostic helpers for scrubbing sensitive data before it is sent to Sentry.
Available since v13.1.0.
This module owns the default sensitive key lists, the placeholder used in
place of redacted values, the credit-card detection heuristic, and the
recursive map/list traversal used by the rest of the SDK to redact values.
Integrations such as Sentry.PlugContext, Sentry.PlugCapture, and
Sentry.LiveViewHook delegate to the functions exposed here so that
scrubbing rules live in a single place.
Defaults
The default sensitive parameter keys (used for body params, query strings, and arbitrary maps) are:
"auth""token""secret""password""passwd""pwd""key""jwt""bearer""sso""saml""csrf""xsrf""credentials""session""sid""identity"
A key counts as sensitive when any of those terms appears anywhere in its
name, compared case-insensitively — so "auth" covers both "Authorization"
and "X-Auth-Token".
The default sensitive header keys are:
"authorization""authentication""cookie"
The "referer" header is kept, but its value is scrubbed as a URL: it names
the page a request came from, so a secret in that page's query string would
otherwise be reported next to a request URL that was already redacted.
Values matching a credit-card-like pattern (13–16 digits, optionally separated by spaces or dashes) are also replaced with the placeholder.
Custom scrubbing
Add your own terms to the parameter denylist with the :scrubber
configuration, which extends the list above:
config :sentry, scrubber: [param_keys: ["internal_ref"]]The map/query/header functions also accept an optional :keys option that
replaces the list outright for that call. Precedence is :keys > the
scrubber: [param_keys: ...] configuration > default_param_keys/0. This
makes it possible to compose custom scrubbers on top of the defaults:
def scrub(map) do
map
|> Sentry.Scrubber.scrub(keys: ["password", "api_key"])
|> Map.drop(["internal_notes"])
end
Scrubbing a %Plug.Conn{}
scrub/1 redacts a conn by applying, to each field listed in the
@scrubbable_conn_fields attribute, that field's strategy. A strategy is
either:
- a configurable scrubber (
:cookie_scrubber,:header_scrubber,:body_scrubber) — resolved per process viaput_conn_scrubber/1(typically fromSentry.PlugContext.call/2), falling back to the SDK defaultscrub(conn, field)clause when none is registered, or - a fixed tag —
:clearreplaces the field with%{},:paramsscrubs the field as a params-shaped map,:query_stringredacts sensitive params from a raw query string,:url_scrubbedderives the field from the URL the registered:url_scrubberreturns, and:private_allow_listkeeps only the registered allow-listed keys of the field (seedefault_private_allow_list/0and the:private_allow_listoption ofput_conn_scrubber/1), dropping everything else.
By default scrub/1 redacts cookies, req_headers, params, and
body_params (the configurable fields — body_params shares the
:body_scrubber with params, so it honors the same registered scrubber and
is emptied when body_scrubber is nil), clears req_cookies and assigns
to %{}, scrubs query_params and path_params as params-shaped maps, derives request_path,
path_info and query_string from the scrubbed URL, and reduces private to
its allow-listed keys (default_private_allow_list/0). assigns is cleared
wholesale because auth libraries (Guardian, Pow, Coherence) routinely store
decoded tokens, full user structs, and session data there, where no key-based
heuristic redacts safely. private keeps only the allow-listed framework
metadata and drops everything else (notably :plug_session).
The defaults can be overridden per call with scrub(conn, overrides), where
overrides is a field: strategy keyword list merged over the attribute —
for example scrub(conn, assigns: :clear).
The request URL itself is not a conn field, so callers that report it (such as
Sentry.PlugContext) fetch the registered :url_scrubber with get/1 and
apply it to the conn. The conn's own URL-derived fields are covered here: a
custom :url_scrubber that redacts a path segment redacts it in
request_path and path_info too, wherever the conn itself is reported.
Registering url_scrubber: nil opts out of that, though query_string is
still scrubbed against the sensitive key list.
Summary
Types
Options accepted by put_conn_scrubber/1.
A per-field scrubber identifying how to redact a particular %Plug.Conn{}
field.
Options accepted by the scrubbing functions in this module.
A resolved set of per-field scrubbers for a %Plug.Conn{}.
Functions
Returns the default list of sensitive header keys.
Returns the SDK default list of sensitive parameter keys.
Returns the default list of %Plug.Conn{} :private keys retained by the
:private_allow_list scrubbing strategy.
Drops sensitive keys from a flat map.
Returns the current process's resolved scrubber function for the given field.
Builds a resolved t/0 set of per-field scrubbers from the given options.
Registers the current process's per-field scrubbers for %Plug.Conn{}.
Scrubs a %Plug.Conn{} or a plain map.
Scrubs a value with the given options, dispatching on the value's type.
Scrubs an application/x-www-form-urlencoded query string, replacing the
value of any sensitive parameter with the placeholder.
Scrubs the query string portion of a URL, replacing the value of any sensitive query parameter with the placeholder. URLs without a query string are returned unchanged.
Returns the placeholder that replaces redacted values: "[Filtered]".
Types
@type conn_scrubber_opts() :: [ body_scrubber: field_scrubber(), header_scrubber: field_scrubber(), cookie_scrubber: field_scrubber(), url_scrubber: field_scrubber(), param_keys: [String.t()], private_allow_list: [atom()] ]
Options accepted by put_conn_scrubber/1.
Each *_scrubber key, when omitted, falls back to the field's default
scrubber — the matching scrub(conn, field) clause of scrub/2.
:param_keys and :private_allow_list default to the corresponding
:scrubber configuration values.
@type field_scrubber() :: (Plug.Conn.t() -> term()) | {module(), atom()} | nil
A per-field scrubber identifying how to redact a particular %Plug.Conn{}
field.
- a 1-arity function — invoked as
fun.(conn) {module, function}— invoked asapply(module, function, [conn])nil— disables the scrubber; map-shaped fields are replaced with%{}, and:urlfalls back to the request URL unchanged
@type option() :: {:keys, [String.t()]}
Options accepted by the scrubbing functions in this module.
@type t() :: %Sentry.Scrubber{ body_scrubber: (Plug.Conn.t() -> term()), cookie_scrubber: (Plug.Conn.t() -> term()), header_scrubber: (Plug.Conn.t() -> term()), param_keys: [String.t()], private_allow_list: [atom()], url_scrubber: (Plug.Conn.t() -> String.t()) }
A resolved set of per-field scrubbers for a %Plug.Conn{}.
Each scrubber field holds a 1-arity function that takes the conn and returns
the scrubbed value for the corresponding field. private_allow_list holds the
keys retained by the :private_allow_list strategy. Built by
put_conn_scrubber/1 from conn_scrubber_opts/0 and stored in the process
dictionary.
Functions
@spec default_header_keys() :: [String.t()]
Returns the default list of sensitive header keys.
@spec default_param_keys() :: [String.t()]
Returns the SDK default list of sensitive parameter keys.
A key is sensitive when any of these terms appears anywhere in its name,
compared case-insensitively. The scrubber: [param_keys: ...] configuration
option extends this list; it cannot shorten it.
@spec default_private_allow_list() :: [atom()]
Returns the default list of %Plug.Conn{} :private keys retained by the
:private_allow_list scrubbing strategy.
These are Phoenix's routing/render metadata keys, kept because they are
high-signal, non-sensitive breadcrumbs for triaging errors. This is the
default for the scrubber: [conn_private_allow_list: ...] configuration option.
Drops sensitive keys from a flat map.
This is the strategy used for HTTP headers, where the sensitive value should not appear in the payload at all.
Options
:keys- the list of sensitive keys to drop. Defaults todefault_header_keys/0.
@spec get(atom()) :: (Plug.Conn.t() -> term())
Returns the current process's resolved scrubber function for the given field.
key is one of [:body_scrubber, :header_scrubber, :cookie_scrubber, :url_scrubber]. Returns the scrubber registered
via put_conn_scrubber/1, or the field's default if none was registered. The
returned function takes a %Plug.Conn{} and returns the scrubbed value, so
callers apply it as Sentry.Scrubber.get(:url_scrubber).(conn).
@spec new(conn_scrubber_opts()) :: t()
Builds a resolved t/0 set of per-field scrubbers from the given options.
Accepts the same :body_scrubber, :header_scrubber, :cookie_scrubber,
and :url_scrubber keys as put_conn_scrubber/1. Each missing key falls
back to the field's default scrubber (the matching scrub(conn, field)
clause). Called with no arguments, new/0 returns the all-defaults scrubber.
Unlike put_conn_scrubber/1, this only constructs the struct — it does not
register it for the current process.
@spec put_conn_scrubber(conn_scrubber_opts()) :: :ok
Registers the current process's per-field scrubbers for %Plug.Conn{}.
Accepts the same :body_scrubber, :header_scrubber, :cookie_scrubber,
and :url_scrubber keys that Sentry.PlugContext takes as plug options,
resolves each missing key to the field's default scrubber (the
{__MODULE__, :scrub, [field]} MFA, i.e. the matching scrub(conn, field)
clause), and stores the resolved scrubbers in the process dictionary.
The registration lives for the lifetime of the calling process — typically
the request process when registered from Sentry.PlugContext.call/2. Used
by other parts of the SDK (notably Sentry.PlugCapture) so all conn
scrubbing honors the same configuration the user passed to
plug Sentry.PlugContext.
Returns :ok.
@spec scrub(Plug.Conn.t()) :: Plug.Conn.t()
@spec scrub(map()) :: map()
@spec scrub(term()) :: term()
Scrubs a %Plug.Conn{} or a plain map.
Given a %Plug.Conn{}, scrubs each field listed in @scrubbable_conn_fields
according to its strategy — see the "Scrubbing a %Plug.Conn{}" section in
the module docs and scrub/2 for the per-field defaults and how to override
them per call. This includes request_path, path_info and query_string,
which are derived from the URL the registered :url_scrubber returns. The
reported request URL is not a conn field; callers scrub that separately by
applying the :url_scrubber from get/1 (whose default is
scrub(conn, :url)).
Given a plain map, recursively scrubs it with the default sensitive keys —
equivalent to scrub(map, []). Any other struct is converted to a map and
scrubbed the same way, so a sensitive field can't slip through unredacted —
for example when the struct is inspected into stacktrace frame vars. See
scrub/2.
@spec scrub(map(), [option()]) :: map()
@spec scrub(list(), [option()]) :: list()
@spec scrub(term(), [option()]) :: term()
@spec scrub( Plug.Conn.t(), keyword() ) :: Plug.Conn.t()
@spec scrub(Plug.Conn.t(), :body | :headers | :cookies | :url) :: term()
Scrubs a value with the given options, dispatching on the value's type.
Scrubbing a map, list, or leaf value — scrub(value, opts)
Recursively scrubs a map: any value whose key is in the configured sensitive key list is replaced with the placeholder, and the remaining values are scrubbed in turn. Lists are scrubbed element-wise, structs are scrubbed as maps, and credit-card-shaped binaries are replaced with the placeholder. Any other leaf value is returned unchanged.
Accepts the same :keys option as the other scrubbing functions:
:keys- the list of sensitive keys to redact. Defaults todefault_param_keys/0.
Scrubbing a single %Plug.Conn{} field — scrub(conn, field)
Extracts the given field from the conn and applies the SDK's default
redaction for it. Each clause is what the field's default scrubber captures
as a {__MODULE__, :scrub, [field]} MFA, and what scrub/1 (conn fields)
and get/1 (URL) fall back to when no custom scrubber is registered:
:body— scrubsconn.paramsviascrub/2; non-map params (such as%Plug.Conn.Unfetched{}) pass through unchanged.:headers— drops sensitiveconn.req_headerscase-insensitively, preserving the list-of-tuples shape.:cookies— drops all cookies, returning%{}.:url— scrubs sensitive query parameters from the request URL viascrub_url/1. To disable URL scrubbing, register a:url_scrubberofnil(or a custom one); seeSentry.PlugContext.
Because these clauses are the defaults (not the registered scrubbers), a
custom :body_scrubber can safely compose on the default behavior without
recursing:
defmodule MyScrubber do
def scrub_params(conn) do
conn
|> Sentry.Scrubber.scrub(:body)
|> Map.drop(["my_secret_field"])
end
end
Scrubbing a whole %Plug.Conn{} with overrides — scrub(conn, overrides)
Behaves like scrub/1 but merges the field: strategy keyword overrides
over the @scrubbable_conn_fields defaults, so a caller can scrub additional
fields or change a field's strategy for that call. Strategies are a
configurable scrubber struct-key, :clear (replace with %{}), :params
(params-shaped scrub of that field), :query_string (redact sensitive params
from that raw query string), :url_scrubbed (derive that field from the
scrubbed URL), or :private_allow_list (keep only the allow-listed keys):
Sentry.Scrubber.scrub(conn, assigns: :clear, query_params: :params)
Scrubs an application/x-www-form-urlencoded query string, replacing the
value of any sensitive parameter with the placeholder.
Options
See scrub/2.
Scrubs the query string portion of a URL, replacing the value of any sensitive query parameter with the placeholder. URLs without a query string are returned unchanged.
Options
See scrub/2.
@spec scrubbed_value() :: String.t()
Returns the placeholder that replaces redacted values: "[Filtered]".