Packages

Validate quoted Elixir AST against a function whitelist

Current section

6 Versions

Jump to

Compare versions

6 files changed
+47 additions
-6 deletions
  @@ -36,6 +36,6 @@ in `mix.exs`:
36 36
37 37 ```elixir
38 38 def deps do
39 - [{:loppers, "~> 0.1.0"}]
39 + [{:loppers, "~> 0.1.2"}]
40 40 end
41 41 ```
  @@ -12,4 +12,4 @@
12 12 {<<"maintainers">>,[<<"Moritz Schmale">>]}.
13 13 {<<"name">>,<<"loppers">>}.
14 14 {<<"requirements">>,[]}.
15 - {<<"version">>,<<"0.1.1">>}.
15 + {<<"version">>,<<"0.1.2">>}.
  @@ -54,12 +54,13 @@ defmodule Loppers do
54 54 {:error, [error]}
55 55 def validate(quoted, opts) do
56 56 {quoted, _acc} = Walk.walk(quoted, {%{}, [{Kernel, []}]}, &Walk.gen_meta/2)
57 + {quoted, _acc} = Walk.walk(quoted, [], &Walk.module_functions/2)
57 58 whitelist = Keyword.get(opts, :whitelist, nil)
58 59 blacklist = Keyword.get(opts, :blacklist, [])
59 - acc = Validate.validate(quoted, [], fn ast, acc ->
60 + acc = Validate.validate(quoted, [], fn {_, meta, _} = ast, acc ->
60 61 # IO.inspect ast
61 62 if Match.is_fn?(ast) do
62 - if (whitelist == nil or List.in_list?(ast, whitelist))
63 + if (Keyword.get(meta, :allow, false) or whitelist == nil or List.in_list?(ast, whitelist))
63 64 and not List.in_list?(ast, blacklist) do
64 65 acc
65 66 else
  @@ -86,7 +87,9 @@ defmodule Loppers do
86 87 {Kernel, :/},
87 88 {Kernel, :-},
88 89 {Kernel, :<<>>},
89 - {Kernel, :<>}
90 + {Kernel, :<>},
91 + :"->",
92 + :<-
90 93 ]
91 94 end
  @@ -17,6 +17,13 @@ defmodule Loppers.Validate do
17 17 validator.(ast, acc)
18 18 end
19 19
20 + # special case for binary pattern match
21 + def validate({:=, _meta, [{:<<>>, _meta, _args} = lhs, rhs]} = ast, acc, validator) do
22 + acc = validator.(ast, acc)
23 + acc = validator.(lhs, acc)
24 + validate(rhs, acc, validator)
25 + end
26 +
20 27 def validate({fun, _meta, args} = ast, acc, validator) do
21 28 acc = validator.(ast, acc)
22 29 acc = validate(fun, acc, validator)
  @@ -85,6 +85,37 @@ defmodule Loppers.Walk do
85 85 {ast, {aliases, imports}}
86 86 end
87 87
88 + @defs ~w[def defp defmacro defmacrop]a
89 +
90 + def module_functions({:defmodule, _, [_aliases, args]} = module, _current_functions) do
91 + contents = case Keyword.get(args, :do, {:__block__, [], []}) do
92 + {:__block__, _, contents} -> contents
93 + {defs, _, _} = content when defs in @defs -> [content]
94 + end
95 + functions =
96 + contents
97 + |> Enum.filter(fn
98 + {defs, _, _} when defs in @defs -> true
99 + _ -> false
100 + end)
101 + |> Enum.map(fn {_, _, [{name, _, _} | _]} -> name end)
102 +
103 + {module, functions}
104 + end
105 +
106 + def module_functions({fun, meta, args}, current_functions) do
107 + meta = if fun in current_functions and
108 + (!Keyword.get(meta, :alias) && !Keyword.get(meta, :import)) do
109 + Keyword.put(meta, :allow, true)
110 + else
111 + meta
112 + end
113 +
114 + {{fun, meta, args}, current_functions}
115 + end
116 +
117 + def module_functions(ast, acc), do: {ast, acc}
118 +
88 119 def function_imported?({module, opts}, function, arity) do
89 120 excluded = {function, arity} in Keyword.get(opts, :except, [])
90 121 included = case Keyword.get(opts, :only) do
Loading more files…