Skip to content

Commit c755ba7

Browse files
authored
Catch SQL.query! (#155)
* Create failing unit tests to cover the SQL.query! function #153 * Update Sobelow.SQL.Query to test for both query() and query!(), similar to Sobelow.Traversal.FileModule #153 * update the Query moduledoc
1 parent 30b17cc commit c755ba7

File tree

2 files changed

+56
-40
lines changed

2 files changed

+56
-40
lines changed

lib/sobelow/sql/query.ex

+18-12
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ defmodule Sobelow.SQL.Query do
33
# SQL Injection in Query
44
55
This submodule of the `SQL` module checks for SQL injection
6-
vulnerabilities through usage of the `Ecto.Adapters.SQL.query`.
6+
vulnerabilities through usage of the `Ecto.Adapters.SQL.query`
7+
and `Ecto.Adapters.SQL.query!`.
78
89
Ensure that the query is parameterized and not user-controlled.
910
@@ -13,27 +14,32 @@ defmodule Sobelow.SQL.Query do
1314
"""
1415
@uid 17
1516
@finding_type "SQL.Query: SQL injection"
17+
@query_funcs [:query, :query!]
1618

1719
use Sobelow.Finding
1820

1921
def run(fun, meta_file) do
2022
confidence = if !meta_file.is_controller?, do: :low
2123

22-
Finding.init(@finding_type, meta_file.filename, confidence)
23-
|> Finding.multi_from_def(fun, parse_sql_def(fun))
24-
|> Enum.each(&Print.add_finding(&1))
25-
26-
Finding.init(@finding_type, meta_file.filename, confidence)
27-
|> Finding.multi_from_def(fun, parse_repo_query_def(fun))
28-
|> Enum.each(&Print.add_finding(&1))
24+
Enum.each(@query_funcs, fn query_func ->
25+
Finding.init(@finding_type, meta_file.filename, confidence)
26+
|> Finding.multi_from_def(fun, parse_sql_def(fun, query_func))
27+
|> Enum.each(&Print.add_finding(&1))
28+
end)
29+
30+
Enum.each(@query_funcs, fn query_func ->
31+
Finding.init(@finding_type, meta_file.filename, confidence)
32+
|> Finding.multi_from_def(fun, parse_repo_query_def(fun, query_func))
33+
|> Enum.each(&Print.add_finding(&1))
34+
end)
2935
end
3036

3137
## query(repo, sql, params \\ [], opts \\ [])
32-
def parse_sql_def(fun) do
33-
Parse.get_fun_vars_and_meta(fun, 1, :query, :SQL)
38+
def parse_sql_def(fun, type) do
39+
Parse.get_fun_vars_and_meta(fun, 1, type, :SQL)
3440
end
3541

36-
def parse_repo_query_def(fun) do
37-
Parse.get_fun_vars_and_meta(fun, 0, :query, :Repo)
42+
def parse_repo_query_def(fun, type) do
43+
Parse.get_fun_vars_and_meta(fun, 0, type, :Repo)
3844
end
3945
end

test/sql/query_test.exs

+38-28
Original file line numberDiff line numberDiff line change
@@ -3,51 +3,61 @@ defmodule SobelowTest.SQL.QueryTest do
33
import Sobelow, only: [is_vuln?: 1]
44
alias Sobelow.SQL.Query
55

6+
@query_funcs [:query, :query!]
7+
68
test "SQL injection in `SQL`" do
7-
func = """
8-
def query(%{"sql" => sql}) do
9-
SQL.query(Repo, sql, [])
10-
end
11-
"""
9+
Enum.each(@query_funcs, fn query_func ->
10+
func = """
11+
def query(%{"sql" => sql}) do
12+
SQL.#{query_func}(Repo, sql, [])
13+
end
14+
"""
1215

13-
{_, ast} = Code.string_to_quoted(func)
16+
{_, ast} = Code.string_to_quoted(func)
1417

15-
assert Query.parse_sql_def(ast) |> is_vuln?
18+
assert Query.parse_sql_def(ast, query_func) |> is_vuln?
19+
end)
1620
end
1721

1822
test "Safe `SQL`" do
19-
func = """
20-
def query(%{"sql" => sql}) do
21-
SQL.query(Repo, "SELECT * FROM users", [])
22-
end
23-
"""
23+
Enum.each(@query_funcs, fn query_func ->
24+
func = """
25+
def query(%{"sql" => sql}) do
26+
SQL.#{query_func}(Repo, "SELECT * FROM users", [])
27+
end
28+
"""
2429

25-
{_, ast} = Code.string_to_quoted(func)
30+
{_, ast} = Code.string_to_quoted(func)
2631

27-
refute Query.parse_sql_def(ast) |> is_vuln?
32+
refute Query.parse_sql_def(ast, query_func) |> is_vuln?
33+
end)
2834
end
2935

3036
test "SQL injection in `Repo`" do
31-
func = """
32-
def query(%{"sql" => sql}) do
33-
Repo.query(sql)
34-
end
35-
"""
37+
Enum.each(@query_funcs, fn query_func ->
38+
func = """
39+
def query(%{"sql" => sql}) do
40+
Repo.#{query_func}(sql)
41+
end
42+
"""
3643

37-
{_, ast} = Code.string_to_quoted(func)
44+
{_, ast} = Code.string_to_quoted(func)
3845

39-
assert Query.parse_repo_query_def(ast) |> is_vuln?
46+
assert Query.parse_repo_query_def(ast, query_func) |> is_vuln?
47+
end)
4048
end
4149

4250
test "safe `Repo`" do
43-
func = """
44-
def query(%{"sql" => sql}) do
45-
Repo.query("SELECT * FROM users")
46-
end
47-
"""
51+
Enum.each(@query_funcs, fn query_func ->
52+
func = """
53+
def query(%{"sql" => sql}) do
54+
Repo.#{query_func}("SELECT * FROM users")
55+
end
56+
"""
4857

49-
{_, ast} = Code.string_to_quoted(func)
58+
{_, ast} = Code.string_to_quoted(func)
5059

51-
refute Query.parse_repo_query_def(ast) |> is_vuln?
60+
refute Query.parse_repo_query_def(ast, query_func) |> is_vuln?
61+
end)
5262
end
5363
end

0 commit comments

Comments
 (0)