Skip to content

Commit 40def2a

Browse files
authored
Merge pull request github#3311 from RasmusWL/python-parse_qs
Python: Propagate taint through parse_qs
2 parents 606a114 + e569d7a commit 40def2a

File tree

4 files changed

+275
-205
lines changed

4 files changed

+275
-205
lines changed

python/ql/src/semmle/python/security/strings/External.qll

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ abstract class ExternalStringKind extends StringKind {
2222
urlsplit(fromnode, tonode) and result.(ExternalUrlSplitResult).getItem() = this
2323
or
2424
urlparse(fromnode, tonode) and result.(ExternalUrlParseResult).getItem() = this
25+
or
26+
parse_qs(fromnode, tonode) and result.(ExternalStringDictKind).getValue() = this
27+
or
28+
parse_qsl(fromnode, tonode) and result.(SequenceKind).getItem().(SequenceKind).getItem() = this
2529
}
2630
}
2731

@@ -181,6 +185,58 @@ private predicate urlparse(ControlFlowNode fromnode, CallNode tonode) {
181185
)
182186
}
183187

188+
private predicate parse_qs(ControlFlowNode fromnode, CallNode tonode) {
189+
// This could be implemented as `exists(FunctionValue` without the explicit six part,
190+
// but then our tests will need to import +100 modules, so for now this slightly
191+
// altered version gets to live on.
192+
exists(Value parse_qs |
193+
(
194+
parse_qs = Value::named("six.moves.urllib.parse.parse_qs")
195+
or
196+
// Python 2
197+
parse_qs = Value::named("urlparse.parse_qs")
198+
or
199+
// Python 2 deprecated version of `urlparse.parse_qs`
200+
parse_qs = Value::named("cgi.parse_qs")
201+
or
202+
// Python 3
203+
parse_qs = Value::named("urllib.parse.parse_qs")
204+
) and
205+
tonode = parse_qs.getACall() and
206+
(
207+
tonode.getArg(0) = fromnode
208+
or
209+
tonode.getArgByName("qs") = fromnode
210+
)
211+
)
212+
}
213+
214+
private predicate parse_qsl(ControlFlowNode fromnode, CallNode tonode) {
215+
// This could be implemented as `exists(FunctionValue` without the explicit six part,
216+
// but then our tests will need to import +100 modules, so for now this slightly
217+
// altered version gets to live on.
218+
exists(Value parse_qsl |
219+
(
220+
parse_qsl = Value::named("six.moves.urllib.parse.parse_qsl")
221+
or
222+
// Python 2
223+
parse_qsl = Value::named("urlparse.parse_qsl")
224+
or
225+
// Python 2 deprecated version of `urlparse.parse_qsl`
226+
parse_qsl = Value::named("cgi.parse_qsl")
227+
or
228+
// Python 3
229+
parse_qsl = Value::named("urllib.parse.parse_qsl")
230+
) and
231+
tonode = parse_qsl.getACall() and
232+
(
233+
tonode.getArg(0) = fromnode
234+
or
235+
tonode.getArgByName("qs") = fromnode
236+
)
237+
)
238+
}
239+
184240
/** A kind of "taint", representing an open file-like object from an external source. */
185241
class ExternalFileObject extends TaintKind {
186242
ExternalStringKind valueKind;

0 commit comments

Comments
 (0)