-
Notifications
You must be signed in to change notification settings - Fork 1
/
redact_ex.ex
95 lines (74 loc) · 3.66 KB
/
redact_ex.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
defmodule RedactEx do
@moduledoc """
RedactEx provides protocols and derivation utilities to work with
sensitive data that should be redacted in logs and external facing tools
* `RedactEx.Redactor` module gives you automagical generation of redacting
functions under a desired module namespace
* `RedactEx.Redactable` is the protocol for which implementation and
derivation should be created to ensure the best possible practices
in your codebase
## Protect your data
RedactEx usual protection consists in two or more steps:
## Generate your redactor functions
You can generate functions to redact your specific data using `RedactEx.Redactor` module.
iex> defmodule MyApp.Redacting do
...> @moduledoc false
...> use RedactEx.Redactor, redactors: [
...> {"redact_three", length: 3, algorithm: :simple},
...> {"redact", lengths: 1..3, algorithm: :simple}
...> ]
...> end
This will generate optimized functions and/or functions adopting the standards
and algorithms defined in this library.
In particular:
* `redact_three/1` will be created with an optimized match for strings of length 3 and a non optimized fallback function
for strings with generic length
* `redact/1` will be created with three optimized match for strings of lengths 1, 2 and 3 and a non optimized fallback function
for strings with generic length
## Protect your structs
The recommended way of protecting your structs is
### 1. Derive or implement `RedactEx.Redactable`
You can use the `@derive` macro from the `RedactEx.Redactable` protocol,
configured based on your needs and optionally using functions from a module generated with
`RedactEx.Redactor` helpers, or manually define an implementation of choice, e.g.
iex> defmodule MyRedactorModule do
...> def redact_function_one(_), do: "(redacted1)"
...> def redact_function_two(_), do: "(redacted2)"
...> end
...> defmodule MyApp.RedactStruct do
...> @derive {RedactEx.Redactable,
...> fields: [
...> myfield1: {MyRedactorModule, :redact_function_one},
...> myfield2: {MyRedactorModule, :redact_function_two},
...> ]}
...> defstruct [:myfield1, :myfield2]
...> end
or
iex> defmodule MyApp.OtherRedactStruct do
...> defstruct [:myfield1, :myfield2]
...> end
...> defimpl RedactEx.Redactable, for: MyApp.OtherRedactStruct do
...> def redact(_value), do: %MyApp.OtherRedactStruct{myfield1: "(redacted)", myfield2: "(redacted)"}
...> end
### 2. Use `inspect` protocol at your advantage
No strategy can eliminate the risk of leaking sensitive data with code only.
Our suggested approach is to derive also the [inspect](https://hexdocs.pm/elixir/1.14.0/Inspect.html) protocol
and use it for your struct whenever you log or send data to external systems, e.g.
iex> defimpl Inspect,
...> for: MyApp.OtherRedactStruct do
...> alias MyApp.OtherRedactStruct
...> alias RedactEx.Redactable
...>
...> @spec inspect(OtherRedactStruct.t(), Inspect.Opts.t()) :: Inspect.Algebra.t()
...> def inspect(input, opts) do
...> input
...> |> Redactable.redact()
...> |> Inspect.Any.inspect(opts)
...> end
...> end
Another strategy could be to directly call `RedactEx.Redactable.redact/1` on structs when logging, but using
`inspect` seems to us more natural and idiomatic.
Another strategy could be to `redact`/`inspect` your data directly in a custom Logger backend, or whatever the system
used for exporting potentially sensitive data.
"""
end