Created
May 25, 2015 17:36
-
-
Save mprymek/c3c3cac7e25404811001 to your computer and use it in GitHub Desktop.
Untrusted code compilation experiment in Elixir
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule SafeCode do | |
def compile code do | |
# we can use [existing_atoms_only: true] here | |
{:ok, qcode} = code |> Code.string_to_quoted() | |
unless safe? qcode do | |
raise "Code is unsafe" | |
else | |
qcode |> Code.compile_quoted | |
end | |
end | |
def safe?({:defmodule, _meta, items}) do | |
items |> Enum.all?(&safe?/1) | |
end | |
def safe?([do: x]), do: safe?(x) | |
# @TODO: do we have to check args? | |
def safe?({:def, _meta1, [{:main, _meta2, _args},body]}), do: safe?(body) | |
def safe?({:__block__, _meta, items}) do | |
items |> Enum.all?(&safe?/1) | |
end | |
def safe?({:unless, _meta, [condition, [do: body1, else: body2]]}) do | |
safe?(condition) and safe?(body1) and safe?(body2) | |
end | |
def safe?({:__aliases__, _meta, _args}), do: true | |
# variable | |
def safe?({x,_meta,nil}) when is_atom(x) do true end | |
# unary operators | |
def safe?({op,_meta,[arg]}) when op in [:!] do | |
safe? arg | |
end | |
# binary operators | |
def safe?({op,_meta,[arg1,arg2]}) when op in [:>,:<,:>=,:<=,:==,:===,:!=,:!==,:+,:-,:/,:*,:++,:--,:<>] do | |
safe?(arg1) and safe?(arg2) | |
end | |
# safe unary functions | |
@safe_functions_1 [:length] | |
def safe?({op,_meta,[arg1]}) when op in @safe_functions_1 do | |
safe? arg1 | |
end | |
# lambda | |
# @TODO: do we have to check args? | |
def safe?({:fn, _meta1, [{:->, _meta2, [_args, body]}]}) do | |
safe? body | |
end | |
@safe_modfuns [{:Enum,:map}] | |
def safe?({{:., _meta1, [{:__aliases__, _meta2, [mod]}, fun]}, _meta3, args}) when {mod,fun} in @safe_modfuns do | |
args |> Enum.all?(&safe?/1) | |
end | |
def safe?(l) when is_list(l) do | |
l |> Enum.all?(&safe?/1) | |
end | |
def safe?(x) when is_atom(x) do true end | |
def safe?(x) when is_integer(x) do true end | |
def safe?(x) when is_binary(x) do true end | |
end | |
code = """ | |
defmodule Main do | |
def main(x) do | |
unless length(x)<10 do | |
x | |
else | |
"x too large!" | |
end | |
end | |
end | |
""" | |
result = code |> SafeCode.compile | |
IO.puts inspect(result) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment