Skip to content

Commit fe00d1c

Browse files
authored
Merge pull request github#2888 from RasmusWL/python-tarslip-sanitizer
Python: Improve tarslip sanitizer
2 parents 4c9a6b7 + dcfc9a8 commit fe00d1c

File tree

3 files changed

+49
-2
lines changed

3 files changed

+49
-2
lines changed

python/ql/src/Security/CWE-022/TarSlip.ql

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,19 @@ class ExtractMembersSink extends TaintSink {
121121
class TarFileInfoSanitizer extends Sanitizer {
122122
TarFileInfoSanitizer() { this = "TarInfo sanitizer" }
123123

124+
/** The test `if <path_sanitizing_test>:` clears taint on its `false` edge. */
124125
override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) {
125-
path_sanitizing_test(test.getTest()) and
126-
taint instanceof TarFileInfo
126+
taint instanceof TarFileInfo and
127+
clears_taint_on_false_edge(test.getTest(), test.getSense())
128+
}
129+
130+
private predicate clears_taint_on_false_edge(ControlFlowNode test, boolean sense) {
131+
path_sanitizing_test(test) and
132+
sense = false
133+
or
134+
// handle `not` (also nested)
135+
test.(UnaryExprNode).getNode().getOp() instanceof Not and
136+
clears_taint_on_false_edge(test.(UnaryExprNode).getOperand(), sense.booleanNot())
127137
}
128138
}
129139

python/ql/test/query-tests/Security/CWE-022/TarSlip.expected

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,15 @@ edges
1515
| tarslip.py:34:14:34:16 | tarfile.open | tarslip.py:34:1:34:17 | tarfile.entry |
1616
| tarslip.py:40:7:40:39 | tarfile.open | tarslip.py:41:24:41:26 | tarfile.open |
1717
| tarslip.py:40:7:40:39 | tarfile.open | tarslip.py:41:24:41:26 | tarfile.open |
18+
| tarslip.py:56:7:56:39 | tarfile.open | tarslip.py:57:14:57:16 | tarfile.open |
19+
| tarslip.py:56:7:56:39 | tarfile.open | tarslip.py:57:14:57:16 | tarfile.open |
20+
| tarslip.py:57:1:57:17 | tarfile.entry | tarslip.py:59:21:59:25 | tarfile.entry |
21+
| tarslip.py:57:1:57:17 | tarfile.entry | tarslip.py:59:21:59:25 | tarfile.entry |
22+
| tarslip.py:57:14:57:16 | tarfile.open | tarslip.py:57:1:57:17 | tarfile.entry |
23+
| tarslip.py:57:14:57:16 | tarfile.open | tarslip.py:57:1:57:17 | tarfile.entry |
1824
#select
1925
| tarslip.py:13:1:13:3 | tar | tarslip.py:12:7:12:39 | tarfile.open | tarslip.py:13:1:13:3 | tarfile.open | Extraction of tarfile from $@ | tarslip.py:12:7:12:39 | Attribute() | a potentially untrusted source |
2026
| tarslip.py:18:17:18:21 | entry | tarslip.py:16:7:16:39 | tarfile.open | tarslip.py:18:17:18:21 | tarfile.entry | Extraction of tarfile from $@ | tarslip.py:16:7:16:39 | Attribute() | a potentially untrusted source |
2127
| tarslip.py:37:17:37:21 | entry | tarslip.py:33:7:33:39 | tarfile.open | tarslip.py:37:17:37:21 | tarfile.entry | Extraction of tarfile from $@ | tarslip.py:33:7:33:39 | Attribute() | a potentially untrusted source |
2228
| tarslip.py:41:24:41:26 | tar | tarslip.py:40:7:40:39 | tarfile.open | tarslip.py:41:24:41:26 | tarfile.open | Extraction of tarfile from $@ | tarslip.py:40:7:40:39 | Attribute() | a potentially untrusted source |
29+
| tarslip.py:59:21:59:25 | entry | tarslip.py:56:7:56:39 | tarfile.open | tarslip.py:59:21:59:25 | tarfile.entry | Extraction of tarfile from $@ | tarslip.py:56:7:56:39 | Attribute() | a potentially untrusted source |

python/ql/test/query-tests/Security/CWE-022/tarslip.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,33 @@ def safemembers(members):
5050

5151
tar = tarfile.open(unsafe_filename_tar)
5252
tar.extractall(members=safemembers(tar))
53+
54+
55+
# Wrong sanitizer (is missing not)
56+
tar = tarfile.open(unsafe_filename_tar)
57+
for entry in tar:
58+
if os.path.isabs(entry.name) or ".." in entry.name:
59+
tar.extract(entry, "/tmp/unpack/")
60+
61+
62+
# OK Sanitized using not
63+
tar = tarfile.open(unsafe_filename_tar)
64+
for entry in tar:
65+
if not (os.path.isabs(entry.name) or ".." in entry.name):
66+
tar.extract(entry, "/tmp/unpack/")
67+
68+
# The following two variants are included by purpose, since by default there is a
69+
# difference in handling `not x` and `not (x or False)` when overriding
70+
# Sanitizer.sanitizingEdge. We want to ensure we handle both consistently.
71+
72+
# Not reported, although vulnerable to '..'
73+
tar = tarfile.open(unsafe_filename_tar)
74+
for entry in tar:
75+
if not (os.path.isabs(entry.name) or False):
76+
tar.extract(entry, "/tmp/unpack/")
77+
78+
# Not reported, although vulnerable to '..'
79+
tar = tarfile.open(unsafe_filename_tar)
80+
for entry in tar:
81+
if not os.path.isabs(entry.name):
82+
tar.extract(entry, "/tmp/unpack/")

0 commit comments

Comments
 (0)