Skip to content

Commit 8d8cd05

Browse files
committed
Python: Add basic support for database threat-model
1 parent 7483075 commit 8d8cd05

File tree

2 files changed

+39
-0
lines changed

2 files changed

+39
-0
lines changed

python/ql/lib/semmle/python/frameworks/PEP249.qll

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,24 @@ module PEP249 {
8181
}
8282
}
8383

84+
/** A call to a method that fetches rows from a previous execution. */
85+
private class FetchMethodCall extends ThreatModelSource::Range, API::CallNode {
86+
FetchMethodCall() {
87+
exists(API::Node start |
88+
start instanceof DatabaseCursor or start instanceof DatabaseConnection
89+
|
90+
// note: since we can't currently provide accesspaths for sources, these are all
91+
// lumped together, although clearly the fetchmany/fetchall returns a
92+
// list/iterable with rows.
93+
this = start.getMember(["fetchone", "fetchmany", "fetchall"]).getACall()
94+
)
95+
}
96+
97+
override string getThreatModel() { result = "database" }
98+
99+
override string getSourceType() { result = "cursor.fetch*()" }
100+
}
101+
84102
// ---------------------------------------------------------------------------
85103
// asyncio implementations
86104
// ---------------------------------------------------------------------------

python/ql/test/library-tests/frameworks/psycopg/pep249.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,24 @@
1212
with conn.cursor() as cursor:
1313
cursor.execute("some sql", (42,)) # $ getSql="some sql"
1414
cursor.executemany("some sql", [(42,)]) # $ getSql="some sql"
15+
16+
17+
### test of threat-model sources
18+
row = cursor.fetchone() # $ threatModelSource[database]=cursor.fetchone()
19+
rows_many = cursor.fetchmany(10) # $ threatModelSource[database]=cursor.fetchmany(..)
20+
rows_all = cursor.fetchall() # $ threatModelSource[database]=cursor.fetchall()
21+
22+
ensure_tainted(
23+
row[0], # $ tainted
24+
rows_many[0][0], # $ tainted
25+
rows_all[0][0], # $ tainted
26+
27+
# pretending we created cursor to return dictionary results
28+
row["column"], # $ tainted
29+
rows_many[0]["column"], # $ tainted
30+
rows_all[0]["column"], # $ tainted
31+
)
32+
for row in rows_many:
33+
ensure_tainted(row[0], row["column"]) # $ tainted
34+
for row in rows_all:
35+
ensure_tainted(row[0], row["column"]) # tainted

0 commit comments

Comments
 (0)