Skip to content

Commit af75d85

Browse files
committed
ClickHouseSQLInjection.qll : add tests
1 parent 470e3eb commit af75d85

File tree

5 files changed

+49
-15
lines changed

5 files changed

+49
-15
lines changed

python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.py

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,14 @@ def dummy(self):
99

1010
def show_user(request, username):
1111

12-
# BAD -- async library 'aioch'
12+
# BAD -- Untrusted user input is directly injected into the sql query using async library 'aioch'
1313
aclient = aiochClient("localhost")
1414
progress = await aclient.execute_with_progress("SELECT * FROM users WHERE username = '%s'" % username)
1515

16-
# BAD -- client excute
17-
client = Client('localhost')
18-
client.execute("SELECT * FROM users WHERE username = '%s'" % username)
19-
20-
# BAD -- client excute oneliner
16+
# BAD -- Untrusted user input is directly injected into the sql query using native client of library 'clickhouse_driver'
2117
Client('localhost').execute("SELECT * FROM users WHERE username = '%s'" % username)
2218

23-
# GOOD -- send username through params
19+
# GOOD -- query uses prepared statements
2420
query = "SELECT * FROM users WHERE username = %(username)s"
2521
Client('localhost').execute(query, {"username": username})
2622

@@ -29,7 +25,4 @@ def show_user(request, username):
2925
cursor = conn.cursor()
3026
cursor.execute("SELECT * FROM users WHERE username = '%s'" % username)
3127

32-
# BAD -- MyClient is a subclass of Client
33-
MyClient('localhost').execute("SELECT * FROM users WHERE username = '%s'" % username)
34-
3528
urlpatterns = [url(r'^users/(?P<username>[^/]+)$', show_user)]

python/ql/src/experimental/Security/CWE-089/ClickHouseSQLInjection.qhelp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,9 @@ or prepared statements.
2121

2222
<example>
2323
<p>
24-
In the following snippet, a user is fetched from the ClickHouse database
25-
using five different queries. In bad patterns query is build by formatting
26-
string with user-controlled data. In a qood pattern user-supplied parameter
27-
is send through a second argument (params) which is dict. Following cases
28-
show different types of communication with ClickHouse database.
24+
In the following snippet, a user is fetched from a <code>ClickHouse</code> database
25+
using five different queries. In the "BAD" cases the query is built directly from user-controlled data.
26+
In the "GOOD" case the user-controlled data is safely embedded into the query by using query parameters.
2927
</p>
3028

3129
<p>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
| ClickHouseDriver.py:15:22:15:106 | ControlFlowNode for Attribute() | ClickHouseDriver.py:15:52:15:105 | ControlFlowNode for BinaryExpr |
2+
| ClickHouseDriver.py:18:5:18:87 | ControlFlowNode for Attribute() | ClickHouseDriver.py:18:33:18:86 | ControlFlowNode for BinaryExpr |
3+
| ClickHouseDriver.py:22:5:22:62 | ControlFlowNode for Attribute() | ClickHouseDriver.py:22:33:22:37 | ControlFlowNode for query |
4+
| ClickHouseDriver.py:27:5:27:74 | ControlFlowNode for Attribute() | ClickHouseDriver.py:27:20:27:73 | ControlFlowNode for BinaryExpr |
5+
| ClickHouseDriver.py:30:5:30:89 | ControlFlowNode for Attribute() | ClickHouseDriver.py:30:35:30:88 | ControlFlowNode for BinaryExpr |
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from django.conf.urls import url
2+
from clickhouse_driver import Client
3+
from clickhouse_driver import connect
4+
from aioch import Client as aiochClient
5+
6+
# Dummy Client subclass
7+
class MyClient(Client):
8+
def dummy(self):
9+
return None
10+
11+
def show_user(request, username):
12+
13+
# BAD -- Untrusted user input is directly injected into the sql query using async library 'aioch'
14+
aclient = aiochClient("localhost")
15+
progress = await aclient.execute_with_progress("SELECT * FROM users WHERE username = '%s'" % username)
16+
17+
# BAD -- Untrusted user input is directly injected into the sql query using native client of library 'clickhouse_driver'
18+
Client('localhost').execute("SELECT * FROM users WHERE username = '%s'" % username)
19+
20+
# GOOD -- query uses prepared statements
21+
query = "SELECT * FROM users WHERE username = %(username)s"
22+
Client('localhost').execute(query, {"username": username})
23+
24+
# BAD -- Untrusted user input is directly injected into the sql query using PEP249 interface
25+
conn = connect('clickhouse://localhost')
26+
cursor = conn.cursor()
27+
cursor.execute("SELECT * FROM users WHERE username = '%s'" % username)
28+
29+
# BAD -- Untrusted user input is directly injected into the sql query using MyClient, which is a subclass of Client
30+
MyClient('localhost').execute("SELECT * FROM users WHERE username = '%s'" % username)
31+
32+
urlpatterns = [url(r'^users/(?P<username>[^/]+)$', show_user)]
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import python
2+
import experimental.semmle.python.frameworks.ClickHouseDriver
3+
import semmle.python.Concepts
4+
5+
from SqlExecution::Range s
6+
select s, s.getSql()

0 commit comments

Comments
 (0)