redact

Package Version Hex Docs

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
Search Document