Packages
sobelow
0.2.4
0.14.1
0.14.0
0.13.0
0.12.2
0.12.1
0.12.0
0.11.1
0.11.0
0.10.6
0.10.5
0.10.4
0.10.3
0.10.2
0.10.1
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.8.0
0.7.8
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.12
0.3.11
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
Security-focused static analysis for Elixir & the Phoenix framework
Current section
Files
Jump to
Current section
Files
lib/sobelow/traversal/send_file.ex
defmodule Sobelow.Traversal.SendFile do
alias Sobelow.Utils
use Sobelow.Finding
def run(fun, filename) do
severity = if String.ends_with?(filename, "_controller.ex"), do: false, else: :low
# If the file is a controller, check for `send_file` as well as
# `Plug.Conn.send_file`. If it is not a controller, look for
# `Plug.Conn.send_file` only.
if String.ends_with?(filename, "_controller.ex") do
{vars, params, {fun_name, [{_, line_no}]}} = parse_send_file_def(fun)
Enum.each vars, fn var ->
if Enum.member?(params, var) || var === "conn.params" do
print_finding(line_no, filename, fun_name, fun, var, severity || :high)
else
print_finding(line_no, filename, fun_name, fun, var, severity || :medium)
end
end
else
{vars, params, {fun_name, [{_, line_no}]}} = parse_aliased_send_file_def(fun)
Enum.each vars, fn var ->
if Enum.member?(params, var) || var === "conn.params" do
print_finding(line_no, filename, fun_name, fun, var, severity || :high)
else
print_finding(line_no, filename, fun_name, fun, var, severity || :medium)
end
end
end
end
## send_file(conn, status, file, offset \\ 0, length \\ :all)
##
## send_file has optional params, so the parameter we care about
## for traversal won't be at a definite location. This is a
## simple solution to the problem.
defp parse_send_file_def(fun) do
{params, {fun_name, line_no}} = Utils.get_fun_declaration(fun)
pipefuns = Utils.get_pipe_funs(fun)
|> Enum.map(fn {_, _, opts} -> Enum.at(opts, 1) end)
|> Enum.flat_map(&Utils.get_funs_of_type(&1, :send_file))
pipefiles = pipefuns
|> Enum.map(&Utils.extract_opts(&1, 1))
|> List.flatten
files = Utils.get_funs_of_type(fun, :send_file) -- pipefuns
|> Enum.map(&Utils.extract_opts(&1, 2))
|> List.flatten
{aliased_files, _, _} = parse_aliased_send_file_def(fun)
{files ++ pipefiles ++ aliased_files, params, {fun_name, line_no}}
end
defp parse_aliased_send_file_def(fun) do
{params, {fun_name, line_no}} = Utils.get_fun_declaration(fun)
pipefuns = Utils.get_pipe_funs(fun)
|> Enum.map(fn {_, _, opts} -> Enum.at(opts, 1) end)
|> Enum.flat_map(&Utils.get_aliased_funs_of_type(&1, :send_file, [:Plug, :Conn]))
pipefiles = pipefuns
|> Enum.map(&Utils.extract_opts(&1, 1))
|> List.flatten
aliased_files = Utils.get_aliased_funs_of_type(fun, :send_file, [:Plug, :Conn]) -- pipefuns
|> Enum.map(&Utils.extract_opts(&1, 2))
|> List.flatten
{aliased_files ++ pipefiles, params, {fun_name, line_no}}
end
def print_finding(line_no, con, fun_name, fun, var, severity) do
{color, confidence} = case severity do
:high -> {IO.ANSI.red(), "High"}
:medium -> {IO.ANSI.yellow(), "Medium"}
:low -> {IO.ANSI.green(), "Low"}
end
IO.puts color <> "Directory Traversal in `send_file` - #{confidence} Confidence" <> IO.ANSI.reset()
IO.puts "File: #{con} - #{fun_name}:#{line_no}"
IO.puts "Variable: #{var}"
if Sobelow.get_env(:with_code), do: Utils.print_code(fun, var, :send_file)
IO.puts "\n-----------------------------------------------\n"
end
def get_details() do
Sobelow.Traversal.details()
end
end