Packages
sobelow
0.3.7
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/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