Packages

Secure Elixir BERT wrapper

Current section

Files

Jump to
ex_bert lib bert.ex
Raw

lib/bert.ex

defmodule BERT do
@moduledoc """
Elixir wrapper allowing safe use of binary erlang terms.
"""
@doc """
Encodes input into BERT.
Returns {:ok, binary} on success or {:error, reason} on failure.
## Options
:compression - sets the compression level of the generated
BERT. Possible values are integers in the range of 0..9, where
0 (the default) is no compression and 9 is maximum compression.
"""
@spec encode(any) :: {:ok, binary} | {:error, any}
@spec encode(any, compression: 0..9) :: {:ok, binary} | {:error, any}
def encode(term, opts \\ []) do
{:ok, encode!(term, opts)}
rescue
e in ArgumentError -> {:error, e}
end
@doc """
Encodes input into BERT.
Returns the BERT binary on success, raises on failure.
## Options
:compression - sets the compression level of the generated
BERT. Possible values are integers in the range of 0..9, where
0 (the default) is no compression and 9 is maximum compression.
"""
@spec encode!(any) :: binary
@spec encode!(any, compression: 0..9) :: binary
def encode!(term, opts \\ []) do
term
|> :erlang.term_to_binary(compressed: Keyword.get(opts, :compression, 0), minor_version: 1)
end
@doc """
Decodes input from BERT.
Returns {:ok, term} on success, {:error, reason} on failure.
## Options
:safe_atoms - true (default) will prevent the creation of atoms
which don't already exist. This should always be enabled when
decoding untrusted data.
:safe_values - true (default) will prevent the creation of functions,
pid's, ports, and references. This should always be enabled when
decoding untrusted data.
"""
@spec decode(binary) :: {:ok, any} | {:error, any}
@spec decode(binary, safe_atoms: boolean, safe_values: boolean) :: {:ok, any} | {:error, any}
def decode(binary, opts \\ []) do
{:ok, decode!(binary, opts)}
rescue
e in ArgumentError -> {:error, e}
end
@doc """
Decodes input from BERT.
Returns term on on success, raises on failure.
## Options
:safe_atoms - true (default) will prevent the creation of atoms
which don't already exist. This should always be enabled when
decoding untrusted data.
:safe_values - true (default) will prevent the creation of functions,
pid's, ports, and references. This should always be enabled when
decoding untrusted data.
"""
@spec decode!(binary) :: any
@spec decode!(binary, safe_atoms: boolean, safe_values: boolean) :: any
def decode!(binary, opts \\ []) do
binary
|> bert_decode(
Keyword.get(opts, :safe_atoms, true),
Keyword.get(opts, :safe_values, true)
)
end
@spec bert_decode(binary, false, false) :: any
defp bert_decode(binary, false, false) do
binary
|> :erlang.binary_to_term()
end
@spec bert_decode(binary, true, false) :: any
defp bert_decode(binary, true, false) do
binary
|> :erlang.binary_to_term([:safe])
end
@spec bert_decode(binary, false, true) :: atom | bitstring | list | number | tuple | map
defp bert_decode(binary, false, true) do
binary
|> safe_bert_decode()
end
@spec bert_decode(binary, true, true) :: atom | bitstring | list | number | tuple | map
defp bert_decode(binary, true, true) do
binary
|> safe_bert_decode([:safe])
end
@typep safe :: atom | bitstring | number | tuple | map
@typep safe_list :: safe | [safe]
@typep safe_bert :: safe_list | [safe_list]
@spec safe_bert_decode(binary, [:safe]) :: safe_bert
defp safe_bert_decode(binary, opts \\ []) do
binary
|> :erlang.binary_to_term(opts)
|> safe_terms()
end
@spec safe_terms(x) :: x when x: list
defp safe_terms([h | t]) do
[safe_terms(h) | safe_terms(t)]
end
defp safe_terms([]) do
[]
end
@spec safe_terms(x) :: x when x: tuple
defp safe_terms(tuple) when is_tuple(tuple) do
tuple
|> Tuple.to_list()
|> Enum.map(&safe_terms(&1))
|> List.to_tuple()
end
@spec safe_terms(x) :: x when x: map
defp safe_terms(map) when is_map(map) do
map
|> Map.to_list()
|> safe_terms()
|> Map.new()
end
@spec safe_terms(x) :: x when x: atom | number | bitstring
defp safe_terms(value)
when is_atom(value) or is_number(value) or is_bitstring(value) do
value
end
@spec safe_terms(fun | port | pid | reference) :: none
defp safe_terms(_unsafe) do
raise ArgumentError, "binary contains unsafe terms"
end
end