Skip to content

Commit 826b597

Browse files
committed
Address bug in piped functions
1 parent b7ba5b6 commit 826b597

File tree

2 files changed

+112
-3
lines changed

2 files changed

+112
-3
lines changed

lib/sobelow/utils.ex

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -407,21 +407,21 @@ defmodule Sobelow.Utils do
407407
defp get_funs_from_pipe(fun, type, nil) do
408408
get_pipe_funs(fun)
409409
|> Enum.map(fn {_, _, opts} -> Enum.at(opts, 1) end)
410-
|> Enum.flat_map(&get_funs_of_type(&1, type))
410+
|> Enum.flat_map(&get_piped_funs_of_type(&1, type))
411411
|> Enum.uniq()
412412
end
413413

414414
defp get_funs_from_pipe(fun, type, module) do
415415
get_pipe_funs(fun)
416416
|> Enum.map(fn {_, _, opts} -> Enum.at(opts, 1) end)
417-
|> Enum.flat_map(&get_aliased_funs_of_type(&1, type, module))
417+
|> Enum.flat_map(&get_piped_aliased_funs_of_type(&1, type, module))
418418
|> Enum.uniq()
419419
end
420420

421421
def get_erlang_funs_from_pipe(fun, type, module) do
422422
get_pipe_funs(fun)
423423
|> Enum.map(fn {_, _, opts} -> Enum.at(opts, 1) end)
424-
|> Enum.flat_map(&get_erlang_aliased_funs_of_type(&1, type, module))
424+
|> Enum.flat_map(&get_piped_erlang_aliased_funs_of_type(&1, type, module))
425425
|> Enum.uniq()
426426
end
427427

@@ -483,6 +483,16 @@ defmodule Sobelow.Utils do
483483
acc
484484
end
485485

486+
def get_piped_erlang_aliased_funs_of_type(ast, type, module) do
487+
case ast do
488+
{{:., _, [^module, ^type]}, _, _} ->
489+
[ast]
490+
491+
_ ->
492+
[]
493+
end
494+
end
495+
486496
def get_funs_by_module(ast, module) do
487497
{_, acc} = Macro.prewalk(ast, [], &contains_module(&1, &2, module))
488498
acc
@@ -579,6 +589,30 @@ defmodule Sobelow.Utils do
579589
{ast, acc}
580590
end
581591

592+
def get_piped_aliased_funs_of_type(ast, type, module) when is_list(module) do
593+
case ast do
594+
{{:., _, [{:__aliases__, _, ^module}, ^type]}, _, _} ->
595+
[ast]
596+
597+
_ ->
598+
[]
599+
end
600+
end
601+
602+
def get_piped_aliased_funs_of_type(ast, type, module) do
603+
case ast do
604+
{{:., _, [{:__aliases__, _, aliases}, ^type]}, _, _} ->
605+
if List.last(aliases) === module do
606+
[ast]
607+
else
608+
[]
609+
end
610+
611+
_ ->
612+
[]
613+
end
614+
end
615+
582616
def get_funs_of_type(ast, type) do
583617
{_, acc} = Macro.prewalk(ast, [], &get_funs_of_type(&1, &2, type))
584618
acc
@@ -603,6 +637,16 @@ defmodule Sobelow.Utils do
603637

604638
def get_funs_of_type(ast, acc, _type), do: {ast, acc}
605639

640+
def get_piped_funs_of_type(ast, type) do
641+
case ast do
642+
{^type, _, _} ->
643+
[ast]
644+
645+
_ ->
646+
[]
647+
end
648+
end
649+
606650
defp create_fun_cap(fun, meta, idx) do
607651
opts = Enum.map(1..idx, fn i -> {:&, [], [i]} end)
608652
{fun, meta, opts}

test/pipe_test.exs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ defmodule SobelowTest.PipeTest do
22
use ExUnit.Case
33
import Sobelow, only: [is_vuln?: 1]
44
alias Sobelow.DOS.StringToAtom
5+
alias Sobelow.Misc.BinToTerm
56

67
test "Simple Pipe" do
78
func = """
@@ -66,4 +67,68 @@ defmodule SobelowTest.PipeTest do
6667

6768
assert StringToAtom.parse_def(ast) |> is_vuln?
6869
end
70+
71+
test "Unpiped in piped function" do
72+
func = """
73+
def show(conn, %{"page" => page}) do
74+
conn
75+
|> render(String.to_atom(page))
76+
end
77+
"""
78+
79+
{_, ast} = Code.string_to_quoted(func)
80+
81+
assert StringToAtom.parse_def(ast) |> is_vuln?
82+
end
83+
84+
test "Unpiped anonymous function in piped function" do
85+
func = """
86+
def show(conn, %{"pages" => pages}) do
87+
pages
88+
|> Enum.each(&String.to_atom/1)
89+
end
90+
"""
91+
92+
{_, ast} = Code.string_to_quoted(func)
93+
94+
assert StringToAtom.parse_def(ast) |> is_vuln?
95+
end
96+
97+
test "Pipe to erlang module" do
98+
func = """
99+
def show(conn, %{"data" => data}) do
100+
data |> :erlang.binary_to_term()
101+
end
102+
"""
103+
104+
{_, ast} = Code.string_to_quoted(func)
105+
106+
assert BinToTerm.parse_def(ast) |> is_vuln?
107+
end
108+
109+
test "Unpiped erlang module in piped function" do
110+
func = """
111+
def show(conn, %{"page" => page}) do
112+
conn
113+
|> func(:erlang.binary_to_term(page))
114+
end
115+
"""
116+
117+
{_, ast} = Code.string_to_quoted(func)
118+
119+
assert BinToTerm.parse_def(ast) |> is_vuln?
120+
end
121+
122+
test "Safe unpiped in piped function" do
123+
func = """
124+
def show(conn, %{"page" => page}) do
125+
conn
126+
|> render(String.to_atom("index"))
127+
end
128+
"""
129+
130+
{_, ast} = Code.string_to_quoted(func)
131+
132+
refute StringToAtom.parse_def(ast) |> is_vuln?
133+
end
69134
end

0 commit comments

Comments
 (0)