|
| 1 | +defmodule Sentry.Scrubber.StacktraceScrubberTest.Card do |
| 2 | + @moduledoc false |
| 3 | + defstruct [:card_number, :name, :secret] |
| 4 | +end |
| 5 | + |
| 6 | +defmodule Sentry.Scrubber.StacktraceScrubberTest do |
| 7 | + use ExUnit.Case, async: true |
| 8 | + |
| 9 | + alias Sentry.Scrubber.StacktraceScrubber |
| 10 | + alias Sentry.Scrubber.StacktraceScrubberTest.Card |
| 11 | + |
| 12 | + describe "scrub_args/1" do |
| 13 | + test "scrubs each arg with Sentry.Scrubber.scrub/1" do |
| 14 | + conn = %Plug.Conn{ |
| 15 | + req_headers: [{"authorization", "Bearer secret"}, {"x-keep", "yes"}], |
| 16 | + params: %{"password" => "secret", "name" => "Alice"} |
| 17 | + } |
| 18 | + |
| 19 | + args = [conn, %{"password" => "another", "ok" => "fine"}, "plain", 42] |
| 20 | + |
| 21 | + assert [scrubbed_conn, scrubbed_map, "plain", 42] = StacktraceScrubber.scrub_args(args) |
| 22 | + |
| 23 | + # the conn is scrubbed as a conn |
| 24 | + assert scrubbed_conn.params == %{"password" => "*********", "name" => "Alice"} |
| 25 | + assert scrubbed_conn.req_headers == [{"x-keep", "yes"}] |
| 26 | + |
| 27 | + # a plain map is key-scrubbed |
| 28 | + assert scrubbed_map == %{"password" => "*********", "ok" => "fine"} |
| 29 | + end |
| 30 | + |
| 31 | + test "scrubs a non-Plug.Conn struct's fields but keeps its type" do |
| 32 | + args = [%Card{card_number: "4242424242424242", name: "Alice", secret: "top-secret"}] |
| 33 | + |
| 34 | + assert [scrubbed] = StacktraceScrubber.scrub_args(args) |
| 35 | + |
| 36 | + # The struct keeps its type (so it inspects as %Card{...} in the frame var, |
| 37 | + # not a bare map)... |
| 38 | + assert is_struct(scrubbed, Card) |
| 39 | + # ...while its fields are scrubbed by value (credit-card heuristic) and by |
| 40 | + # name (the atom key :secret matches the sensitive-key list). |
| 41 | + assert scrubbed.card_number == "*********" |
| 42 | + assert scrubbed.secret == "*********" |
| 43 | + assert scrubbed.name == "Alice" |
| 44 | + end |
| 45 | + |
| 46 | + test "scrubs each arg independently, with no conn/params mirroring" do |
| 47 | + # A registered body_scrubber only governs the conn's params field; the standalone |
| 48 | + # params arg is scrubbed independently with the default keys (no mirror). |
| 49 | + Sentry.Scrubber.put_conn_scrubber(body_scrubber: fn _conn -> %{"marker" => "scrubbed"} end) |
| 50 | + |
| 51 | + conn = %Plug.Conn{params: %{"password" => "secret", "ssn" => "123-45-6789"}} |
| 52 | + args = [conn, conn.params] |
| 53 | + |
| 54 | + assert [scrubbed_conn, scrubbed_params] = StacktraceScrubber.scrub_args(args) |
| 55 | + |
| 56 | + # conn's params field goes through the registered body_scrubber |
| 57 | + assert scrubbed_conn.params == %{"marker" => "scrubbed"} |
| 58 | + |
| 59 | + # the standalone params arg is scrubbed independently (default keys only): the |
| 60 | + # "password" value is redacted, but "ssn" (not a default key) is left intact — |
| 61 | + # proving the conn's scrubbed params are NOT mirrored onto it. |
| 62 | + assert scrubbed_params == %{"password" => "*********", "ssn" => "123-45-6789"} |
| 63 | + end |
| 64 | + end |
| 65 | + |
| 66 | + describe "scrub/2" do |
| 67 | + test "scrubs an exception's :args with the default per-arg scrubber" do |
| 68 | + conn = %Plug.Conn{params: %{"password" => "secret", "name" => "Alice"}} |
| 69 | + exception = %FunctionClauseError{module: Foo, function: :bar, arity: 2, args: [conn, "x"]} |
| 70 | + |
| 71 | + assert %FunctionClauseError{args: [scrubbed_conn, "x"]} = |
| 72 | + StacktraceScrubber.scrub(exception) |
| 73 | + |
| 74 | + assert scrubbed_conn.params == %{"password" => "*********", "name" => "Alice"} |
| 75 | + end |
| 76 | + |
| 77 | + test "applies a custom args_scrubber callback to the exception's args" do |
| 78 | + exception = %FunctionClauseError{module: Foo, function: :bar, arity: 2, args: [1, 2, 3]} |
| 79 | + |
| 80 | + assert %FunctionClauseError{args: [2, 4, 6]} = |
| 81 | + StacktraceScrubber.scrub(exception, fn args -> Enum.map(args, &(&1 * 2)) end) |
| 82 | + end |
| 83 | + |
| 84 | + test "leaves an exception without a list :args field unchanged" do |
| 85 | + exception = %RuntimeError{message: "boom"} |
| 86 | + |
| 87 | + assert StacktraceScrubber.scrub(exception) == exception |
| 88 | + end |
| 89 | + end |
| 90 | +end |
0 commit comments