Skip to content

refa(plug): consolidate access to the plug scrubber#1069

Open
solnic wants to merge 21 commits into
masterfrom
refa/consolidate-plug-scrubber-access
Open

refa(plug): consolidate access to the plug scrubber#1069
solnic wants to merge 21 commits into
masterfrom
refa/consolidate-plug-scrubber-access

Conversation

@solnic
Copy link
Copy Markdown
Collaborator

@solnic solnic commented May 26, 2026

This encapsulates scrub-related logic fully in the Sentry.Scrubber and expands its interface to make it easily usable standalone and introduces a "current scrubber" concept, so that customized scrubbers are actually respected and used across processes.

This will also make it simpler to implement the new spec that replaces "default PII" concept later this year.

The public API remains the same, which means that plug options accept the same keys which result in the same behavior and the original default scrubbing functions are still there (PlugContext.default_*_scrubber ones). To be honest, I'd deprecate them and shift entire scrubbing stuff to the Scrubber eventually. Something to consider.

These improvements make fixes here #1068 and here #1070 trivial, which is the actual biggest reason why I've done this.

Basic examples

Sentry.Scrubber.scrub(%{password: "super-secret", custom_secret: "HIDE PLZZZ"}, keys: [:password, :custom_secret])
#=> %{password: "*********", custom_secret: "*********"}

# Falls back to the default keys (["password", "passwd", "secret"]) when none given
Sentry.Scrubber.scrub(%{"password" => "hunter2", "name" => "Jane"})
#=> %{"password" => "*********", "name" => "Jane"}

# Recurses into nested maps/lists; credit-card-shaped values are caught too
Sentry.Scrubber.scrub(%{user: %{name: "Jane", password: "hunter2"}, tokens: ["4111 1111 1111 1111"]}, keys: [:password])
#=> %{user: %{name: "Jane", password: "*********"}, tokens: ["*********"]}

Web-related scrubbing helpers

Sentry.Scrubber.scrub_query_string("user=jane&password=hunter2")
#=> "user=jane&password=%2A%2A%2A%2A%2A%2A%2A%2A%2A"

Sentry.Scrubber.scrub_url("https://example.com/login?token=abc&secret=xyz", keys: ["secret"])
#=> "https://example.com/login?token=abc&secret=%2A%2A%2A%2A%2A%2A%2A%2A%2A"

Sentry.Scrubber.drop_keys(%{"authorization" => "Bearer x", "accept" => "json"})
#=> %{"accept" => "json"}

Stored scrubber instance

This stuff is used internally by the PlugContext:

# Register per-field scrubbers for the request
Sentry.Scrubber.put_conn_scrubber(
  body_scrubber: &MyScrubber.scrub_params/1,
  header_scrubber: {MyScrubber, :scrub_headers},
  cookie_scrubber: nil
)
#=> :ok

# Scrub a conn's params, headers, and cookies using the registered scrubbers
Sentry.Scrubber.scrub(conn)
#=> %Plug.Conn{params: %{"password" => "*********", ...}, ...}

# The URL is not a conn field — fetch its scrubber and apply it separately
Sentry.Scrubber.get(:url_scrubber).(conn)
#=> "https://example.com/login?secret=*********"

@solnic solnic force-pushed the refa/consolidate-plug-scrubber-access branch 5 times, most recently from 6d67fda to d234d46 Compare May 29, 2026 12:48
solnic and others added 18 commits June 2, 2026 14:43
…hing

Collapses the scrubbing entry points onto a single `scrub` surface driven
by clause-level pattern matching:

  * `scrub/1` is now a lenient, shallow dispatcher — `Plug.Conn` routes
    through the registered conn scrubbers, a plain map is scrubbed
    recursively, and anything else (binaries, lists, unrelated structs,
    scalars) is returned unchanged. This is suitable for callers that scrub
    values of unknown shape.
  * `scrub/2` carries the recursive logic via typed clauses (map, struct,
    list, credit-card binary, passthrough) instead of a private
    `cond`-based helper. The map clause redacts sensitive keys and scrubs
    the remaining values in turn.

Removes the `scrub_value/1` and private `scrub_pair/3` helpers, both now
expressed as `scrub` clauses.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@solnic solnic force-pushed the refa/consolidate-plug-scrubber-access branch 2 times, most recently from 4b4dd34 to e05c35a Compare June 3, 2026 12:35
@solnic solnic marked this pull request as ready for review June 3, 2026 13:59
cursor[bot]

This comment was marked as resolved.

solnic and others added 3 commits June 3, 2026 14:42
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Make Sentry.Scrubber's intrinsic :url default actually redact sensitive query
parameters (via scrub_url/1) instead of returning the URL unchanged — a sensible
default for a scrubbing module. A nil :url_scrubber still disables scrubbing
(nil_scrubber(:url) now returns the request URL directly rather than delegating
to the scrubbing default).

Sentry.PlugContext keeps its historical behavior of NOT scrubbing URLs unless a
:url_scrubber is configured: call/2 now defaults :url_scrubber to the local no-op
default_url_scrubber/1 (via Keyword.put_new) when the user passes none. An
explicit url_scrubber: nil or a custom scrubber is preserved. This also wires up
default_url_scrubber/1, which was otherwise orphaned.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@solnic solnic force-pushed the refa/consolidate-plug-scrubber-access branch from e05c35a to 6453968 Compare June 4, 2026 10:26
Comment thread lib/sentry/plug_context.ex
@getsentry getsentry deleted a comment from cursor Bot Jun 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant