Repository navigation
Expand file tree
/
Copy pathrule.py
More file actions
50 lines (46 loc) · 1.52 KB
/
Copy pathrule.py
File metadata and controls
50 lines (46 loc) · 1.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
from codepathfinder.python_decorators import python_rule
from codepathfinder import calls, flows, QueryType
from codepathfinder.presets import PropagationPresets
class DBCursor(QueryType):
fqns = ["sqlite3.Cursor", "mysql.connector.cursor.MySQLCursor",
"psycopg2.extensions.cursor", "pymysql.cursors.Cursor"]
patterns = ["*Cursor"]
match_subclasses = True
# Common Django request sources
_DJANGO_SOURCES = [
calls("request.GET.get"),
calls("request.POST.get"),
calls("request.GET"),
calls("request.POST"),
calls("request.COOKIES.get"),
calls("request.FILES.get"),
calls("*.GET.get"),
calls("*.POST.get"),
]
@python_rule(
id="PYTHON-DJANGO-SEC-001",
name="Django SQL Injection via cursor.execute()",
severity="CRITICAL",
category="django",
cwe="CWE-89",
tags="python,django,sql-injection,cursor,OWASP-A03,CWE-89",
message="User input flows to cursor.execute() without parameterization. Use %s placeholders.",
owasp="A03:2021",
)
def detect_django_cursor_sqli():
"""Detects request data flowing to cursor.execute()."""
return flows(
from_sources=_DJANGO_SOURCES,
to_sinks=[
DBCursor.method("execute", "executemany").tracks(0),
calls("cursor.execute"),
calls("*.execute"),
],
sanitized_by=[
calls("escape"),
calls("escape_string"),
calls("escape_sql"),
],
propagates_through=PropagationPresets.standard(),
scope="global",
)