Skip to content

Commit d2efe0b

Browse files
committed
Python: Normalize additional taint steps for modeled classes
Such that it should be next to the other class-related predicates (such as `instance()`), the class is called `AdditionalTaintStep`, and it marked private. I also moved any modeling of attributes as well, while I was at it.
1 parent be1cad8 commit d2efe0b

File tree

8 files changed

+209
-205
lines changed

8 files changed

+209
-205
lines changed

python/ql/src/semmle/python/frameworks/Aiohttp.qll

Lines changed: 68 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,73 @@ module AiohttpWebModel {
293293

294294
/** Gets a reference to an instance of `aiohttp.web.Request`. */
295295
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
296+
297+
/**
298+
* Taint propagation for `aiohttp.web.Request`.
299+
*
300+
* See https://docs.aiohttp.org/en/stable/web_reference.html#request-and-base-request
301+
*/
302+
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
303+
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
304+
// normal (non-async) methods
305+
nodeFrom = Request::instance() and
306+
nodeTo.(DataFlow::MethodCallNode).calls(nodeFrom, ["clone", "get_extra_info"])
307+
or
308+
// async methods
309+
exists(DataFlow::MethodCallNode call, Await await |
310+
nodeTo.asExpr() = await and
311+
nodeFrom = Request::instance()
312+
|
313+
await.getValue() = any(DataFlow::Node awaitable | call.flowsTo(awaitable)).asExpr() and
314+
call.calls(nodeFrom, ["read", "text", "json", "multipart", "post"])
315+
)
316+
or
317+
// Attributes
318+
nodeFrom = Request::instance() and
319+
nodeTo.(DataFlow::AttrRead).getObject() = nodeFrom and
320+
nodeTo.(DataFlow::AttrRead).getAttributeName() in [
321+
"url", "rel_url", "forwarded", "host", "remote", "path", "path_qs", "raw_path", "query",
322+
"headers", "transport", "cookies", "content", "_payload", "content_type", "charset",
323+
"http_range", "if_modified_since", "if_unmodified_since", "if_range", "match_info"
324+
]
325+
}
326+
}
327+
328+
/** An attribute read on an `aiohttp.web.Request` that is a `MultiDictProxy` instance. */
329+
class AiohttpRequestMultiDictProxyInstances extends Multidict::MultiDictProxy::InstanceSource {
330+
AiohttpRequestMultiDictProxyInstances() {
331+
this.(DataFlow::AttrRead).getObject() = Request::instance() and
332+
this.(DataFlow::AttrRead).getAttributeName() in ["query", "headers"]
333+
or
334+
// Handle the common case of `x = await request.post()`
335+
// but don't try to handle anything else, since we don't have an easy way to do this yet.
336+
// TODO: more complete handling of `await request.post()`
337+
exists(Await await, DataFlow::CallCfgNode call, DataFlow::AttrRead read |
338+
this.asExpr() = await
339+
|
340+
read.(DataFlow::AttrRead).getObject() = Request::instance() and
341+
read.(DataFlow::AttrRead).getAttributeName() = "post" and
342+
call.getFunction() = read and
343+
await.getValue() = call.asExpr()
344+
)
345+
}
346+
}
347+
348+
/** An attribute read on an `aiohttp.web.Request` that is a `yarl.URL` instance. */
349+
class AiohttpRequestYarlUrlInstances extends Yarl::Url::InstanceSource {
350+
AiohttpRequestYarlUrlInstances() {
351+
this.(DataFlow::AttrRead).getObject() = Request::instance() and
352+
this.(DataFlow::AttrRead).getAttributeName() in ["url", "rel_url"]
353+
}
354+
}
355+
356+
/** An attribute read on an `aiohttp.web.Request` that is a `aiohttp.StreamReader` instance. */
357+
class AiohttpRequestStreamReaderInstances extends StreamReader::InstanceSource {
358+
AiohttpRequestStreamReaderInstances() {
359+
this.(DataFlow::AttrRead).getObject() = Request::instance() and
360+
this.(DataFlow::AttrRead).getAttributeName() in ["content", "_payload"]
361+
}
362+
}
296363
}
297364

298365
/**
@@ -357,7 +424,7 @@ module AiohttpWebModel {
357424
/**
358425
* Taint propagation for `aiohttp.StreamReader`.
359426
*/
360-
private class AiohttpStreamReaderAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
427+
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
361428
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
362429
// normal (non-async) methods
363430
nodeFrom = instance() and
@@ -425,73 +492,6 @@ module AiohttpWebModel {
425492
}
426493
}
427494

428-
/**
429-
* Taint propagation for `aiohttp.web.Request`.
430-
*
431-
* See https://docs.aiohttp.org/en/stable/web_reference.html#request-and-base-request
432-
*/
433-
private class AiohttpRequestAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
434-
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
435-
// normal (non-async) methods
436-
nodeFrom = Request::instance() and
437-
nodeTo.(DataFlow::MethodCallNode).calls(nodeFrom, ["clone", "get_extra_info"])
438-
or
439-
// async methods
440-
exists(DataFlow::MethodCallNode call, Await await |
441-
nodeTo.asExpr() = await and
442-
nodeFrom = Request::instance()
443-
|
444-
await.getValue() = any(DataFlow::Node awaitable | call.flowsTo(awaitable)).asExpr() and
445-
call.calls(nodeFrom, ["read", "text", "json", "multipart", "post"])
446-
)
447-
or
448-
// Attributes
449-
nodeFrom = Request::instance() and
450-
nodeTo.(DataFlow::AttrRead).getObject() = nodeFrom and
451-
nodeTo.(DataFlow::AttrRead).getAttributeName() in [
452-
"url", "rel_url", "forwarded", "host", "remote", "path", "path_qs", "raw_path", "query",
453-
"headers", "transport", "cookies", "content", "_payload", "content_type", "charset",
454-
"http_range", "if_modified_since", "if_unmodified_since", "if_range", "match_info"
455-
]
456-
}
457-
}
458-
459-
/** An attribute read on an `aiohttp.web.Request` that is a `MultiDictProxy` instance. */
460-
class AiohttpRequestMultiDictProxyInstances extends Multidict::MultiDictProxy::InstanceSource {
461-
AiohttpRequestMultiDictProxyInstances() {
462-
this.(DataFlow::AttrRead).getObject() = Request::instance() and
463-
this.(DataFlow::AttrRead).getAttributeName() in ["query", "headers"]
464-
or
465-
// Handle the common case of `x = await request.post()`
466-
// but don't try to handle anything else, since we don't have an easy way to do this yet.
467-
// TODO: more complete handling of `await request.post()`
468-
exists(Await await, DataFlow::CallCfgNode call, DataFlow::AttrRead read |
469-
this.asExpr() = await
470-
|
471-
read.(DataFlow::AttrRead).getObject() = Request::instance() and
472-
read.(DataFlow::AttrRead).getAttributeName() = "post" and
473-
call.getFunction() = read and
474-
await.getValue() = call.asExpr()
475-
)
476-
}
477-
}
478-
479-
/** An attribute read on an `aiohttp.web.Request` that is a `yarl.URL` instance. */
480-
class AiohttpRequestYarlUrlInstances extends Yarl::Url::InstanceSource {
481-
AiohttpRequestYarlUrlInstances() {
482-
this.(DataFlow::AttrRead).getObject() = Request::instance() and
483-
this.(DataFlow::AttrRead).getAttributeName() in ["url", "rel_url"]
484-
}
485-
}
486-
487-
/** An attribute read on an `aiohttp.web.Request` that is a `aiohttp.StreamReader` instance. */
488-
class AiohttpRequestStreamReaderInstances extends StreamReader::InstanceSource {
489-
AiohttpRequestStreamReaderInstances() {
490-
this.(DataFlow::AttrRead).getObject() = Request::instance() and
491-
this.(DataFlow::AttrRead).getAttributeName() in ["content", "_payload"]
492-
}
493-
}
494-
495495
// ---------------------------------------------------------------------------
496496
// aiohttp.web Response modeling
497497
// ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)