redact
gleam add redact
Further documentation can be found at https://hexdocs.pm/redact.
Usage
The recommended usage of this library is use secret.Secret
on the edges of IO.
For instance, if you need to store an API_KEY
in your application’s config record, wrap it with secret.new
as soon as it is read in, and call secret.expose
when it is needed to make a request.
Usage of secret.expose
also allows you to easily audit your code for where your secrets are being used.
import envoy
import gleam/int
import gleam/result
import redact/secret
pub type Error {
Required(name: String)
}
pub type Env {
Env(
database_url: secret.Secret(String),
port: Int,
secret_key: secret.Secret(String),
)
}
pub fn extract_env() -> Result(Env, Error) {
use database_url <- result.try(database_url())
let port = port()
use secret_key <- result.try(secret_key())
Ok(Env(database_url:, port:, secret_key:))
}
pub fn database_url() {
required_env("DATABASE_URL") |> result.map(secret.new)
}
pub fn port() {
envoy.get("PORT")
|> result.map(int.parse)
|> result.flatten()
|> result.unwrap(8000)
}
pub fn secret_key() {
required_env("SECRET_KEY") |> result.map(secret.new)
}
pub fn required_env(name: String) -> Result(String, Error) {
envoy.get(name)
|> result.replace_error(Required(name))
}
Caveats
string.inspect output could change at any time
The implementation of string.inspect is not considered stable and may change at any time. It is possible in the future that string.inspect
may change and expose the value stored in a closure. When in doubt, verify that however you output a secret.Secret
that it does not expose that secret.
Not cryptographically secure
This library is not a cryptographically secure way to store a secret in RAM. It is a convenience. An attacker could find a way to inspect your program’s memory at runtime and see the secret in the clear. If your threat model requires more secrecy, please use a different library.
secret.Secret equality
Two instances of a secret.Secret
with the same inner value are not equal. The test secret.Secret("wibble") == secret.Secret("wibble")
appears to work in Erlang, but fails in JavaScript.
Sample output
If you use the string.inspect
method to convert a Gleam value to a string, any values of type secret.Secret
will be hidden.
For instance, given the following record:
pub type Env {
Env(
api_key: secret.Secret(String),
)
}
In the Erlang target, the Secret record looks like the following when passed to string.inspect
string.inspect(
Env(api_key: secret.new(
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMzEyIiwibmFtZSI6IkpvaG4gQnJvd24iLCJpYXQiOjE1MTYyMzkwMjJ9.__2EDbjt_49s6ssnr1tQgg--xEK6fbEK5bWG85OqB_c",
)),
)
// -> "Env(Secret(//fn() { ... }))"
In JavaScript, the result of string.inspect
looks slightly different:
string.inspect(
Env(api_key: secret.new(
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMzEyIiwibmFtZSI6IkpvaG4gQnJvd24iLCJpYXQiOjE1MTYyMzkwMjJ9.__2EDbjt_49s6ssnr1tQgg--xEK6fbEK5bWG85OqB_c",
)),
)
// -> "Env(api_key: Secret(expose: //fn() { ... }))"
Using echo
in the Erlang target will print the following:
test/redact_test.gleam:53
Env(Secret(//fn() { ... }))
In JavaScript, echo
will print the following:
test/redact_test.gleam:53
Env(api_key: Secret(expose: //fn() { ... }))
Development
gleam test # Run the tests
gleam test --target javascript # Run the tests