Skip to content

Commit 9dd6439

Browse files
authored
Merge pull request #4749 from RasmusWL/command-injection-tests
Python: Add some command injection tests
2 parents 8a44405 + e5e8ec6 commit 9dd6439

File tree

4 files changed

+147
-40
lines changed

4 files changed

+147
-40
lines changed

python/ql/test/experimental/dataflow/tainttracking/customSanitizer/TestTaint.expected

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@ test_taint
3434
| test_logical.py:128 | ok | test_nesting_not_with_and_true | s |
3535
| test_logical.py:137 | fail | test_with_return | s |
3636
| test_logical.py:146 | fail | test_with_exception | s |
37+
| test_reference.py:31 | fail | test_basic | s2 |
38+
| test_reference.py:31 | ok | test_basic | s |
39+
| test_reference.py:33 | ok | test_basic | s |
40+
| test_reference.py:33 | ok | test_basic | s2 |
41+
| test_reference.py:41 | fail | test_identical_call | s.strip() |
42+
| test_reference.py:43 | ok | test_identical_call | s.strip() |
43+
| test_reference.py:56 | fail | test_class_attribute_access | c.foo |
44+
| test_reference.py:58 | ok | test_class_attribute_access | c.foo |
3745
isSanitizer
3846
| TestTaintTrackingConfiguration | test.py:21:39:21:39 | ControlFlowNode for s |
3947
| TestTaintTrackingConfiguration | test.py:50:10:50:29 | ControlFlowNode for emulated_escaping() |
@@ -48,3 +56,6 @@ isSanitizerGuard
4856
| TestTaintTrackingConfiguration | test_logical.py:115:12:115:21 | ControlFlowNode for is_safe() |
4957
| TestTaintTrackingConfiguration | test_logical.py:120:16:120:25 | ControlFlowNode for is_safe() |
5058
| TestTaintTrackingConfiguration | test_logical.py:125:20:125:29 | ControlFlowNode for is_safe() |
59+
| TestTaintTrackingConfiguration | test_reference.py:30:8:30:17 | ControlFlowNode for is_safe() |
60+
| TestTaintTrackingConfiguration | test_reference.py:40:8:40:25 | ControlFlowNode for is_safe() |
61+
| TestTaintTrackingConfiguration | test_reference.py:55:8:55:21 | ControlFlowNode for is_safe() |
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import sys; import os; sys.path.append(os.path.dirname(os.path.dirname((__file__))))
2+
from taintlib import *
3+
4+
# This has no runtime impact, but allows autocomplete to work
5+
from typing import TYPE_CHECKING
6+
if TYPE_CHECKING:
7+
from ..taintlib import *
8+
9+
10+
# Actual tests
11+
12+
"""Testing logical constructs not/and/or works out of the box.
13+
"""
14+
15+
import random
16+
17+
18+
def random_choice():
19+
return bool(random.randint(0, 1))
20+
21+
22+
def is_safe(arg):
23+
return arg == "safe"
24+
25+
26+
def test_basic():
27+
s = TAINTED_STRING
28+
s2 = s
29+
30+
if is_safe(s):
31+
ensure_not_tainted(s, s2)
32+
else:
33+
ensure_tainted(s, s2)
34+
35+
36+
def test_identical_call():
37+
"""This code pattern is being used in real world code"""
38+
s = TAINTED_STRING
39+
40+
if is_safe(s.strip()):
41+
ensure_not_tainted(s.strip())
42+
else:
43+
ensure_tainted(s.strip())
44+
45+
46+
class C(object):
47+
def __init__(self, value):
48+
self.foo = value
49+
50+
51+
def test_class_attribute_access():
52+
s = TAINTED_STRING
53+
c = C(s)
54+
55+
if is_safe(c.foo):
56+
ensure_not_tainted(c.foo)
57+
else:
58+
ensure_tainted(c.foo)
59+
60+
61+
# Make tests runable
62+
63+
test_basic()
64+
test_identical_call()
65+
test_class_attribute_access()
Lines changed: 47 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,50 @@
11
edges
2-
| command_injection.py:10:13:10:24 | ControlFlowNode for Attribute | command_injection.py:12:15:12:27 | ControlFlowNode for BinaryExpr |
3-
| command_injection.py:17:13:17:24 | ControlFlowNode for Attribute | command_injection.py:19:22:19:34 | ControlFlowNode for BinaryExpr |
4-
| command_injection.py:24:11:24:22 | ControlFlowNode for Attribute | command_injection.py:25:23:25:25 | ControlFlowNode for cmd |
5-
| command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | command_injection.py:32:14:32:26 | ControlFlowNode for BinaryExpr |
6-
| command_injection.py:36:15:36:26 | ControlFlowNode for Attribute | command_injection.py:39:15:39:21 | ControlFlowNode for command |
7-
| command_injection.py:36:15:36:26 | ControlFlowNode for Attribute | command_injection.py:40:15:40:21 | ControlFlowNode for command |
8-
| command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | command_injection.py:53:15:53:21 | ControlFlowNode for command |
9-
| command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | command_injection.py:54:14:54:20 | ControlFlowNode for command |
10-
| command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | command_injection.py:55:21:55:27 | ControlFlowNode for command |
11-
| command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | command_injection.py:56:27:56:33 | ControlFlowNode for command |
12-
| command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | command_injection.py:57:20:57:26 | ControlFlowNode for command |
2+
| command_injection.py:11:13:11:24 | ControlFlowNode for Attribute | command_injection.py:13:15:13:27 | ControlFlowNode for BinaryExpr |
3+
| command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:20:22:20:34 | ControlFlowNode for BinaryExpr |
4+
| command_injection.py:25:11:25:22 | ControlFlowNode for Attribute | command_injection.py:26:23:26:25 | ControlFlowNode for cmd |
5+
| command_injection.py:31:13:31:24 | ControlFlowNode for Attribute | command_injection.py:33:14:33:26 | ControlFlowNode for BinaryExpr |
6+
| command_injection.py:38:15:38:26 | ControlFlowNode for Attribute | command_injection.py:41:15:41:21 | ControlFlowNode for command |
7+
| command_injection.py:38:15:38:26 | ControlFlowNode for Attribute | command_injection.py:42:15:42:21 | ControlFlowNode for command |
8+
| command_injection.py:54:15:54:26 | ControlFlowNode for Attribute | command_injection.py:55:15:55:21 | ControlFlowNode for command |
9+
| command_injection.py:54:15:54:26 | ControlFlowNode for Attribute | command_injection.py:56:14:56:20 | ControlFlowNode for command |
10+
| command_injection.py:54:15:54:26 | ControlFlowNode for Attribute | command_injection.py:57:21:57:27 | ControlFlowNode for command |
11+
| command_injection.py:54:15:54:26 | ControlFlowNode for Attribute | command_injection.py:58:27:58:33 | ControlFlowNode for command |
12+
| command_injection.py:54:15:54:26 | ControlFlowNode for Attribute | command_injection.py:59:20:59:26 | ControlFlowNode for command |
13+
| command_injection.py:71:12:71:23 | ControlFlowNode for Attribute | command_injection.py:73:19:73:30 | ControlFlowNode for BinaryExpr |
14+
| command_injection.py:78:12:78:23 | ControlFlowNode for Attribute | command_injection.py:80:19:80:30 | ControlFlowNode for BinaryExpr |
1315
nodes
14-
| command_injection.py:10:13:10:24 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
15-
| command_injection.py:12:15:12:27 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr |
16-
| command_injection.py:17:13:17:24 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
17-
| command_injection.py:19:22:19:34 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr |
18-
| command_injection.py:24:11:24:22 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
19-
| command_injection.py:25:23:25:25 | ControlFlowNode for cmd | semmle.label | ControlFlowNode for cmd |
20-
| command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
21-
| command_injection.py:32:14:32:26 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr |
22-
| command_injection.py:36:15:36:26 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
23-
| command_injection.py:39:15:39:21 | ControlFlowNode for command | semmle.label | ControlFlowNode for command |
24-
| command_injection.py:40:15:40:21 | ControlFlowNode for command | semmle.label | ControlFlowNode for command |
25-
| command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
26-
| command_injection.py:53:15:53:21 | ControlFlowNode for command | semmle.label | ControlFlowNode for command |
27-
| command_injection.py:54:14:54:20 | ControlFlowNode for command | semmle.label | ControlFlowNode for command |
28-
| command_injection.py:55:21:55:27 | ControlFlowNode for command | semmle.label | ControlFlowNode for command |
29-
| command_injection.py:56:27:56:33 | ControlFlowNode for command | semmle.label | ControlFlowNode for command |
30-
| command_injection.py:57:20:57:26 | ControlFlowNode for command | semmle.label | ControlFlowNode for command |
16+
| command_injection.py:11:13:11:24 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
17+
| command_injection.py:13:15:13:27 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr |
18+
| command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
19+
| command_injection.py:20:22:20:34 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr |
20+
| command_injection.py:25:11:25:22 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
21+
| command_injection.py:26:23:26:25 | ControlFlowNode for cmd | semmle.label | ControlFlowNode for cmd |
22+
| command_injection.py:31:13:31:24 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
23+
| command_injection.py:33:14:33:26 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr |
24+
| command_injection.py:38:15:38:26 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
25+
| command_injection.py:41:15:41:21 | ControlFlowNode for command | semmle.label | ControlFlowNode for command |
26+
| command_injection.py:42:15:42:21 | ControlFlowNode for command | semmle.label | ControlFlowNode for command |
27+
| command_injection.py:54:15:54:26 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
28+
| command_injection.py:55:15:55:21 | ControlFlowNode for command | semmle.label | ControlFlowNode for command |
29+
| command_injection.py:56:14:56:20 | ControlFlowNode for command | semmle.label | ControlFlowNode for command |
30+
| command_injection.py:57:21:57:27 | ControlFlowNode for command | semmle.label | ControlFlowNode for command |
31+
| command_injection.py:58:27:58:33 | ControlFlowNode for command | semmle.label | ControlFlowNode for command |
32+
| command_injection.py:59:20:59:26 | ControlFlowNode for command | semmle.label | ControlFlowNode for command |
33+
| command_injection.py:71:12:71:23 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
34+
| command_injection.py:73:19:73:30 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr |
35+
| command_injection.py:78:12:78:23 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
36+
| command_injection.py:80:19:80:30 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr |
3137
#select
32-
| command_injection.py:12:15:12:27 | ControlFlowNode for BinaryExpr | command_injection.py:10:13:10:24 | ControlFlowNode for Attribute | command_injection.py:12:15:12:27 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:10:13:10:24 | ControlFlowNode for Attribute | a user-provided value |
33-
| command_injection.py:19:22:19:34 | ControlFlowNode for BinaryExpr | command_injection.py:17:13:17:24 | ControlFlowNode for Attribute | command_injection.py:19:22:19:34 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:17:13:17:24 | ControlFlowNode for Attribute | a user-provided value |
34-
| command_injection.py:25:23:25:25 | ControlFlowNode for cmd | command_injection.py:24:11:24:22 | ControlFlowNode for Attribute | command_injection.py:25:23:25:25 | ControlFlowNode for cmd | This command depends on $@. | command_injection.py:24:11:24:22 | ControlFlowNode for Attribute | a user-provided value |
35-
| command_injection.py:32:14:32:26 | ControlFlowNode for BinaryExpr | command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | command_injection.py:32:14:32:26 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:30:13:30:24 | ControlFlowNode for Attribute | a user-provided value |
36-
| command_injection.py:39:15:39:21 | ControlFlowNode for command | command_injection.py:36:15:36:26 | ControlFlowNode for Attribute | command_injection.py:39:15:39:21 | ControlFlowNode for command | This command depends on $@. | command_injection.py:36:15:36:26 | ControlFlowNode for Attribute | a user-provided value |
37-
| command_injection.py:40:15:40:21 | ControlFlowNode for command | command_injection.py:36:15:36:26 | ControlFlowNode for Attribute | command_injection.py:40:15:40:21 | ControlFlowNode for command | This command depends on $@. | command_injection.py:36:15:36:26 | ControlFlowNode for Attribute | a user-provided value |
38-
| command_injection.py:53:15:53:21 | ControlFlowNode for command | command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | command_injection.py:53:15:53:21 | ControlFlowNode for command | This command depends on $@. | command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | a user-provided value |
39-
| command_injection.py:54:14:54:20 | ControlFlowNode for command | command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | command_injection.py:54:14:54:20 | ControlFlowNode for command | This command depends on $@. | command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | a user-provided value |
40-
| command_injection.py:55:21:55:27 | ControlFlowNode for command | command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | command_injection.py:55:21:55:27 | ControlFlowNode for command | This command depends on $@. | command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | a user-provided value |
41-
| command_injection.py:56:27:56:33 | ControlFlowNode for command | command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | command_injection.py:56:27:56:33 | ControlFlowNode for command | This command depends on $@. | command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | a user-provided value |
42-
| command_injection.py:57:20:57:26 | ControlFlowNode for command | command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | command_injection.py:57:20:57:26 | ControlFlowNode for command | This command depends on $@. | command_injection.py:52:15:52:26 | ControlFlowNode for Attribute | a user-provided value |
38+
| command_injection.py:13:15:13:27 | ControlFlowNode for BinaryExpr | command_injection.py:11:13:11:24 | ControlFlowNode for Attribute | command_injection.py:13:15:13:27 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:11:13:11:24 | ControlFlowNode for Attribute | a user-provided value |
39+
| command_injection.py:20:22:20:34 | ControlFlowNode for BinaryExpr | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:20:22:20:34 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | a user-provided value |
40+
| command_injection.py:26:23:26:25 | ControlFlowNode for cmd | command_injection.py:25:11:25:22 | ControlFlowNode for Attribute | command_injection.py:26:23:26:25 | ControlFlowNode for cmd | This command depends on $@. | command_injection.py:25:11:25:22 | ControlFlowNode for Attribute | a user-provided value |
41+
| command_injection.py:33:14:33:26 | ControlFlowNode for BinaryExpr | command_injection.py:31:13:31:24 | ControlFlowNode for Attribute | command_injection.py:33:14:33:26 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:31:13:31:24 | ControlFlowNode for Attribute | a user-provided value |
42+
| command_injection.py:41:15:41:21 | ControlFlowNode for command | command_injection.py:38:15:38:26 | ControlFlowNode for Attribute | command_injection.py:41:15:41:21 | ControlFlowNode for command | This command depends on $@. | command_injection.py:38:15:38:26 | ControlFlowNode for Attribute | a user-provided value |
43+
| command_injection.py:42:15:42:21 | ControlFlowNode for command | command_injection.py:38:15:38:26 | ControlFlowNode for Attribute | command_injection.py:42:15:42:21 | ControlFlowNode for command | This command depends on $@. | command_injection.py:38:15:38:26 | ControlFlowNode for Attribute | a user-provided value |
44+
| command_injection.py:55:15:55:21 | ControlFlowNode for command | command_injection.py:54:15:54:26 | ControlFlowNode for Attribute | command_injection.py:55:15:55:21 | ControlFlowNode for command | This command depends on $@. | command_injection.py:54:15:54:26 | ControlFlowNode for Attribute | a user-provided value |
45+
| command_injection.py:56:14:56:20 | ControlFlowNode for command | command_injection.py:54:15:54:26 | ControlFlowNode for Attribute | command_injection.py:56:14:56:20 | ControlFlowNode for command | This command depends on $@. | command_injection.py:54:15:54:26 | ControlFlowNode for Attribute | a user-provided value |
46+
| command_injection.py:57:21:57:27 | ControlFlowNode for command | command_injection.py:54:15:54:26 | ControlFlowNode for Attribute | command_injection.py:57:21:57:27 | ControlFlowNode for command | This command depends on $@. | command_injection.py:54:15:54:26 | ControlFlowNode for Attribute | a user-provided value |
47+
| command_injection.py:58:27:58:33 | ControlFlowNode for command | command_injection.py:54:15:54:26 | ControlFlowNode for Attribute | command_injection.py:58:27:58:33 | ControlFlowNode for command | This command depends on $@. | command_injection.py:54:15:54:26 | ControlFlowNode for Attribute | a user-provided value |
48+
| command_injection.py:59:20:59:26 | ControlFlowNode for command | command_injection.py:54:15:54:26 | ControlFlowNode for Attribute | command_injection.py:59:20:59:26 | ControlFlowNode for command | This command depends on $@. | command_injection.py:54:15:54:26 | ControlFlowNode for Attribute | a user-provided value |
49+
| command_injection.py:73:19:73:30 | ControlFlowNode for BinaryExpr | command_injection.py:71:12:71:23 | ControlFlowNode for Attribute | command_injection.py:73:19:73:30 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:71:12:71:23 | ControlFlowNode for Attribute | a user-provided value |
50+
| command_injection.py:80:19:80:30 | ControlFlowNode for BinaryExpr | command_injection.py:78:12:78:23 | ControlFlowNode for Attribute | command_injection.py:80:19:80:30 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:78:12:78:23 | ControlFlowNode for Attribute | a user-provided value |

python/ql/test/query-tests/Security/CWE-078/command_injection.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
1+
import re
22
import os
33
import subprocess
44

55
from flask import Flask, request
66
app = Flask(__name__)
77

8+
89
@app.route("/command1")
910
def command_injection1():
1011
files = request.args.get('files', '')
@@ -31,6 +32,7 @@ def others():
3132
# Don't let files be `; rm -rf /`
3233
os.popen("ls " + files)
3334

35+
3436
@app.route("/multiple")
3537
def multiple():
3638
command = request.args.get('command', '')
@@ -55,3 +57,24 @@ def not_into_sink_impl():
5557
subprocess.call(command)
5658
subprocess.check_call(command)
5759
subprocess.run(command)
60+
61+
62+
@app.route("/path-exists-not-sanitizer")
63+
def path_exists_not_sanitizer():
64+
"""os.path.exists is not a sanitizer
65+
66+
This small example is inspired by real world code. Initially, it seems like a good
67+
sanitizer. However, if you are able to create files, you can make the
68+
`os.path.exists` check succeed, and still be able to run commands. An example is
69+
using the filename `not-there || echo pwned`.
70+
"""
71+
path = request.args.get('path', '')
72+
if os.path.exists(path):
73+
os.system("ls " + path) # NOT OK
74+
75+
76+
@app.route("/restricted-characters")
77+
def restricted_characters():
78+
path = request.args.get('path', '')
79+
if re.match(r'^[a-zA-Z0-9_-]+$', path):
80+
os.system("ls " + path) # OK (TODO: Currently FP)

0 commit comments

Comments
 (0)