Current section

Files

Jump to
sobelow lib sobelow misc file_path.ex
Raw

lib/sobelow/misc/file_path.ex

defmodule Sobelow.Misc.FilePath do
@moduledoc """
# Insecure use of `File` and `Path`
In Elixir, `File` methods are null-terminated, while `Path`
functions are not. This may cause security issues in certain
situations. For example:
```
user_input = "/var/www/secret.txt\0/name"
path = Path.dirname(user_input)
public_file = path <> "/public.txt"
File.read(public_file)
```
Because `Path` functions are not null-terminated, this
will attempt to read the file, "/var/www/secret.txt\0/public.txt".
However, due to the null-byte termination of `File` functions
"secret.txt" will ultimately be read.
`File/Path` checks can be ignored with the following command:
$ mix sobelow -i Misc.FilePath
"""
alias Sobelow.Utils
use Sobelow.Finding
def run(fun, filename) do
{vars, params, {fun_name, [{_, line_no}]}} = parse_def(fun)
Enum.each vars, fn var ->
if Enum.member?(params, var) || var === "conn.params" do
add_finding(line_no, filename, fun_name, fun, var, :medium)
else
add_finding(line_no, filename, fun_name, fun, var, :low)
end
end
end
defp add_finding(line_no, filename, fun_name, fun, var, severity) do
type = "Insecure use of `File` and `Path`"
case Sobelow.format() do
"json" ->
finding = [
type: type,
file: filename,
function: "#{fun_name}:#{line_no}",
variable: var
]
Sobelow.log_finding(finding, severity)
_ ->
Sobelow.log_finding(type, severity)
IO.puts Utils.finding_header(type, severity)
IO.puts Utils.finding_file_metadata(filename, fun_name, line_no)
IO.puts Utils.finding_variable(var)
Utils.maybe_print_file_path_code(fun, var)
IO.puts Utils.finding_break()
end
end
def parse_def(fun) do
{params, {fun_name, line_no}} = Utils.get_fun_declaration(fun)
file_assigns = Utils.get_assigns_from(fun, [:File])
path_assigns = Utils.get_assigns_from(fun, [:Path])
path_vars = Utils.get_funs_by_module(fun, [:Path])
|> Enum.map(&Utils.extract_opts(&1, 0))
|> List.flatten
file_vars = Utils.get_funs_by_module(fun, [:File])
|> Enum.map(&Utils.extract_opts(&1, 0))
|> List.flatten
shared_path =
Enum.filter(path_vars, fn var ->
Enum.member?(file_assigns, var)
end)
shared_file =
Enum.filter(file_vars, fn var ->
Enum.member?(path_assigns, var)
end)
vars = Enum.filter(file_vars, fn var ->
Enum.member?(path_vars, var)
end)
{vars ++ shared_file ++ shared_path, params, {fun_name, line_no}}
end
end