SafeAtom (SafeAtom v0.1.0)

Copy Markdown View Source

Safe, whitelist-based casting of values to atoms.

SafeAtom returns only atoms that are explicitly present in the :allowed list.

Binary input is never converted with String.to_atom/1 or String.to_existing_atom/1. Instead, binary values are compared with the string representation of atoms already present in :allowed.

This avoids creating new atoms from external input and avoids querying the VM atom table for arbitrary binary values.

Examples

iex> SafeAtom.cast("user", allowed: [:user, :guest])
{:ok, :user}

iex> SafeAtom.cast(:user, allowed: [:user, :guest])
{:ok, :user}

iex> SafeAtom.cast("admin", allowed: [:user, :guest])
{:error, :not_allowed}

iex> SafeAtom.cast(:admin, allowed: [:user, :guest])
{:error, :not_allowed}

iex> SafeAtom.cast("anything", allowed: [])
{:error, :not_allowed}

iex> SafeAtom.cast("user", [])
{:error, :missing_allowed}

iex> SafeAtom.cast("user", allowed: ["user"])
{:error, :invalid_allowed}

iex> SafeAtom.cast(123, allowed: [:user])
{:error, :invalid_value}

iex> SafeAtom.cast(nil, allowed: [:user])
{:error, :not_allowed}

iex> SafeAtom.cast(nil, allowed: [nil])
{:ok, nil}

Error reasons

  • :missing_allowed - the :allowed option was not provided.
  • :invalid_allowed - :allowed is not a list of atoms.
  • :invalid_value - the input value is not a binary or an atom.
  • :not_allowed - the input value is valid, but does not match any allowed atom.

Telemetry events

SafeAtom emits the following Telemetry events:

[:safe_atom, :cast, :rejected]

Emitted whenever cast/2 returns {:error, reason}.

  • measurements:
  • metadata:
    • :reason - one of :missing_allowed, :invalid_allowed, :invalid_value, or :not_allowed
    • :value - the input value passed to cast/2
    • :allowed - the :allowed option value, or nil when the option is missing

Summary

Functions

Casts a binary or atom to one of the explicitly allowed atoms.

Types

reason()

@type reason() :: :missing_allowed | :invalid_allowed | :invalid_value | :not_allowed

Functions

cast(value, arg2)

@spec cast(
  term(),
  keyword()
) :: {:ok, atom()} | {:error, reason()}
@spec cast(term(), term()) :: {:error, :missing_allowed}

Casts a binary or atom to one of the explicitly allowed atoms.

The :allowed option is required and must be a list of atoms.

For binary input, SafeAtom compares the input with Atom.to_string/1 for each allowed atom. The returned atom is always taken from the :allowed list.

Returns {:error, :missing_allowed} when called with options that do not contain :allowed.

Examples

iex> SafeAtom.cast("user", allowed: [:user, :guest])
{:ok, :user}

iex> SafeAtom.cast(:guest, allowed: [:user, :guest])
{:ok, :guest}

iex> SafeAtom.cast("admin", allowed: [:user, :guest])
{:error, :not_allowed}

iex> SafeAtom.cast(:admin, allowed: [:user, :guest])
{:error, :not_allowed}

iex> SafeAtom.cast("user", allowed: [])
{:error, :not_allowed}

iex> SafeAtom.cast("user", [])
{:error, :missing_allowed}

iex> SafeAtom.cast("user", allowed: :user)
{:error, :invalid_allowed}

iex> SafeAtom.cast("user", allowed: [:user, "guest"])
{:error, :invalid_allowed}

iex> SafeAtom.cast(123, allowed: [:user])
{:error, :invalid_value}

iex> SafeAtom.cast(nil, allowed: [nil])
{:ok, nil}

cast!(value, opts)

@spec cast!(
  term(),
  keyword()
) :: atom()