Skip to content

Instantly share code, notes, and snippets.

@fuelen
Created June 10, 2021 13:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fuelen/4e1d32abb0f61807b1bd163993c255b9 to your computer and use it in GitHub Desktop.
Save fuelen/4e1d32abb0f61807b1bd163993c255b9 to your computer and use it in GitHub Desktop.
defmodule SafeEval do
def valid_data do
"""
%{
recipient: %{first_name: "John", age: 18},
employments: [%{start_year: 2020}, %{start_year: 2020}],
valid: true,
status: :archived,
date: ~D[2011-01-01],
datetime: ~U[2021-06-10 10:57:51.929284Z],
edited_by: {:user, %{first_name: "Dick"}, "director" <> "!"}
}
"""
end
def invalid_data do
"""
%{
hi: File.exists?("/etc/resolv.conf"),
lambda: &(&1),
lambda2: &Function.identity/1,
concat: Kernel.<>("1", "2")
}
"""
end
def invalid_syntax do
"%{"
end
def ensure_ast_safe(ast) do
Macro.prewalk(
ast,
[],
fn
{fn_name, _, _} = node, acc when fn_name in [:%{}, :sigil_D, :=, :sigil_U, :<<>>, :{}, :<>] ->
{node, acc}
{atom, _} = node, acc when is_atom(atom) ->
{node, acc}
node, acc when is_atom(node) or is_binary(node) or is_integer(node) or is_list(node) ->
{node, acc}
node, acc ->
{nil, [node | acc]}
end
)
|> elem(1)
|> case do
[] ->
:ok
expressions ->
{:error, {:unsupported_expressions, expressions}}
end
end
def eval(data) do
with {:ok, ast} <- Code.string_to_quoted(data),
:ok <- ensure_ast_safe(ast) do
{result, _} = Code.eval_quoted(ast)
{:ok, result}
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment