Merge branch 'main' of github.com:github/codeql into python-port-sql-injection

This commit is contained in:
Rasmus Lerchedahl Petersen
2020-10-21 14:38:02 +02:00
821 changed files with 21785 additions and 11182 deletions

View File

@@ -292,7 +292,7 @@ private Node update(Node node) {
* `y`. There is a dataflow step from `**{"y": 1, "a": 3}` to `[**d]` to transfer the content and
* a clearing of content at key `y` for node `[**d]`, since that value has been unpacked.
*/
private module ArgumentPassing {
module ArgumentPassing {
/**
* Holds if `call` represents a `DataFlowCall` to a `DataFlowCallable` represented by `callable`.
*
@@ -798,6 +798,29 @@ predicate jumpStep(Node nodeFrom, Node nodeTo) {
or
// Module variable write
nodeFrom = nodeTo.(ModuleVariableNode).getAWrite()
or
// Read of module attribute:
exists(AttrRead r, ModuleValue mv |
r.getObject().asCfgNode().pointsTo(mv) and
module_export(mv.getScope(), r.getAttributeName(), nodeFrom) and
nodeTo = r
)
}
/**
* Holds if the module `m` defines a name `name` by assigning `defn` to it. This is an
* overapproximation, as `name` may not in fact be exported (e.g. by defining an `__all__` that does
* not include `name`).
*/
private predicate module_export(Module m, string name, CfgNode defn) {
exists(EssaVariable v |
v.getName() = name and
v.getAUse() = m.getANormalExit()
|
defn.getNode() = v.getDefinition().(AssignmentDefinition).getValue()
or
defn.getNode() = v.getDefinition().(ArgumentRefinement).getArgument()
)
}
//--------

View File

@@ -49,6 +49,7 @@ newtype TNode =
TKwOverflowNode(CallNode call, CallableValue callable) {
exists(getKeywordOverflowArg(call, callable, _))
or
ArgumentPassing::connects(call, callable) and
exists(call.getNode().getKwargs()) and
callable.getScope().hasKwArg()
} or

View File

@@ -192,8 +192,16 @@ module HTTP {
* extend `RouteSetup` instead.
*/
abstract class Range extends DataFlow::Node {
/** Gets the argument used to set the URL pattern. */
abstract DataFlow::Node getUrlPatternArg();
/** Gets the URL pattern for this route, if it can be statically determined. */
abstract string getUrlPattern();
string getUrlPattern() {
exists(StrConst str |
DataFlow::localFlow(DataFlow::exprNode(str), this.getUrlPatternArg()) and
result = str.getText()
)
}
/** Gets a function that will handle incoming requests for this route, if any. */
abstract Function getARouteHandler();

View File

@@ -4,6 +4,7 @@
private import experimental.semmle.python.frameworks.Dill
private import experimental.semmle.python.frameworks.Django
private import experimental.semmle.python.frameworks.Fabric
private import experimental.semmle.python.frameworks.Flask
private import experimental.semmle.python.frameworks.Invoke
private import experimental.semmle.python.frameworks.Stdlib

View File

@@ -1,11 +1,14 @@
/**
* Provides classes modeling security-relevant aspects of the `django` package.
* Provides classes modeling security-relevant aspects of the `django` PyPI package.
* See https://www.djangoproject.com/.
*/
private import python
private import experimental.dataflow.DataFlow
private import experimental.dataflow.RemoteFlowSources
private import experimental.dataflow.TaintTracking
private import experimental.semmle.python.Concepts
private import semmle.python.regex
/**
* Provides models for the `django` PyPI package.
@@ -31,7 +34,7 @@ private module Django {
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node django_attr(DataFlow::TypeTracker t, string attr_name) {
attr_name in ["db"] and
attr_name in ["db", "urls", "http"] and
(
t.start() and
result = DataFlow::importNode("django" + "." + attr_name)
@@ -295,71 +298,485 @@ private module Django {
}
}
}
/**
* A call to the `django.db.connection.cursor.execute` function.
*
* See
* - https://docs.djangoproject.com/en/3.1/topics/db/sql/#executing-custom-sql-directly
* - https://docs.djangoproject.com/en/3.1/topics/db/sql/#connections-and-cursors
*/
private class DbConnectionExecute extends SqlExecution::Range, DataFlow::CfgNode {
override CallNode node;
DbConnectionExecute() { node.getFunction() = django::db::execute().asCfgNode() }
override DataFlow::Node getSql() {
result.asCfgNode() in [node.getArg(0), node.getArgByName("sql")]
}
}
/**
* A call to the `annotate` function on a model using a `RawSQL` argument.
*
* See https://docs.djangoproject.com/en/3.1/ref/models/querysets/#annotate
*/
private class ObjectsAnnotate extends SqlExecution::Range, DataFlow::CfgNode {
override CallNode node;
ControlFlowNode sql;
ObjectsAnnotate() {
node.getFunction() = django::db::models::objects_attr("annotate").asCfgNode() and
django::db::models::expressions::RawSQL::classInstance(sql).asCfgNode() in [node.getArg(_),
node.getArgByName(_)]
}
override DataFlow::Node getSql() { result.asCfgNode() = sql }
}
/**
* A call to the `raw` function on a model.
*
* See
* - https://docs.djangoproject.com/en/3.1/topics/db/sql/#django.db.models.Manager.raw
* - https://docs.djangoproject.com/en/3.1/ref/models/querysets/#raw
*/
private class ObjectsRaw extends SqlExecution::Range, DataFlow::CfgNode {
override CallNode node;
ObjectsRaw() { node.getFunction() = django::db::models::objects_attr("raw").asCfgNode() }
override DataFlow::Node getSql() { result.asCfgNode() = node.getArg(0) }
}
/**
* A call to the `extra` function on a model.
*
* See https://docs.djangoproject.com/en/3.1/ref/models/querysets/#extra
*/
private class ObjectsExtra extends SqlExecution::Range, DataFlow::CfgNode {
override CallNode node;
ObjectsExtra() { node.getFunction() = django::db::models::objects_attr("extra").asCfgNode() }
override DataFlow::Node getSql() {
result.asCfgNode() =
[node.getArg([0, 1, 3, 4]), node.getArgByName(["select", "where", "tables", "order_by"])]
}
}
// -------------------------------------------------------------------------
// django.urls
// -------------------------------------------------------------------------
/** Gets a reference to the `django.urls` module. */
DataFlow::Node urls() { result = django_attr("urls") }
/** Provides models for the `django.urls` module */
module urls {
/**
* Gets a reference to the attribute `attr_name` of the `urls` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node urls_attr(DataFlow::TypeTracker t, string attr_name) {
attr_name in ["path", "re_path"] and
(
t.start() and
result = DataFlow::importNode("django.urls" + "." + attr_name)
or
t.startInAttr(attr_name) and
result = DataFlow::importNode("django.urls")
or
t.startInAttr(attr_name) and
result = django::urls()
)
or
// Due to bad performance when using normal setup with `urls_attr(t2, attr_name).track(t2, t)`
// we have inlined that code and forced a join
exists(DataFlow::TypeTracker t2 |
exists(DataFlow::StepSummary summary |
urls_attr_first_join(t2, attr_name, result, summary) and
t = t2.append(summary)
)
)
}
pragma[nomagic]
private predicate urls_attr_first_join(
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res,
DataFlow::StepSummary summary
) {
DataFlow::StepSummary::step(urls_attr(t2, attr_name), res, summary)
}
/**
* Gets a reference to the attribute `attr_name` of the `urls` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node urls_attr(string attr_name) {
result = urls_attr(DataFlow::TypeTracker::end(), attr_name)
}
/**
* Gets a reference to the `django.urls.path` function.
* See https://docs.djangoproject.com/en/3.0/ref/urls/#path
*/
DataFlow::Node path() { result = urls_attr("path") }
/**
* Gets a reference to the `django.urls.re_path` function.
* See https://docs.djangoproject.com/en/3.0/ref/urls/#re_path
*/
DataFlow::Node re_path() { result = urls_attr("re_path") }
}
// -------------------------------------------------------------------------
// django.http
// -------------------------------------------------------------------------
/** Gets a reference to the `django.http` module. */
DataFlow::Node http() { result = django_attr("http") }
/** Provides models for the `django.http` module */
module http {
/**
* Gets a reference to the attribute `attr_name` of the `django.http` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node http_attr(DataFlow::TypeTracker t, string attr_name) {
attr_name in ["request", "HttpRequest"] and
(
t.start() and
result = DataFlow::importNode("django.http" + "." + attr_name)
or
t.startInAttr(attr_name) and
result = django::http()
)
or
// Due to bad performance when using normal setup with `http_attr(t2, attr_name).track(t2, t)`
// we have inlined that code and forced a join
exists(DataFlow::TypeTracker t2 |
exists(DataFlow::StepSummary summary |
http_attr_first_join(t2, attr_name, result, summary) and
t = t2.append(summary)
)
)
}
pragma[nomagic]
private predicate http_attr_first_join(
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res,
DataFlow::StepSummary summary
) {
DataFlow::StepSummary::step(http_attr(t2, attr_name), res, summary)
}
/**
* Gets a reference to the attribute `attr_name` of the `django.http` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node http_attr(string attr_name) {
result = http_attr(DataFlow::TypeTracker::end(), attr_name)
}
// ---------------------------------------------------------------------------
// django.http.request
// ---------------------------------------------------------------------------
/** Gets a reference to the `django.http.request` module. */
DataFlow::Node request() { result = http_attr("request") }
/** Provides models for the `django.http.request` module. */
module request {
/**
* Gets a reference to the attribute `attr_name` of the `django.http.request` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node request_attr(DataFlow::TypeTracker t, string attr_name) {
attr_name in ["HttpRequest"] and
(
t.start() and
result = DataFlow::importNode("django.http.request" + "." + attr_name)
or
t.startInAttr(attr_name) and
result = django::http::request()
)
or
// Due to bad performance when using normal setup with `request_attr(t2, attr_name).track(t2, t)`
// we have inlined that code and forced a join
exists(DataFlow::TypeTracker t2 |
exists(DataFlow::StepSummary summary |
request_attr_first_join(t2, attr_name, result, summary) and
t = t2.append(summary)
)
)
}
pragma[nomagic]
private predicate request_attr_first_join(
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res,
DataFlow::StepSummary summary
) {
DataFlow::StepSummary::step(request_attr(t2, attr_name), res, summary)
}
/**
* Gets a reference to the attribute `attr_name` of the `django.http.request` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node request_attr(string attr_name) {
result = request_attr(DataFlow::TypeTracker::end(), attr_name)
}
/**
* Provides models for the `django.http.request.HttpRequest` class
*
* See https://docs.djangoproject.com/en/3.0/ref/request-response/#httprequest-objects
*/
module HttpRequest {
/** Gets a reference to the `django.http.request.HttpRequest` class. */
private DataFlow::Node classRef(DataFlow::TypeTracker t) {
t.start() and
result = request_attr("HttpRequest")
or
// handle django.http.HttpRequest alias
t.start() and
result = http_attr("HttpRequest")
or
exists(DataFlow::TypeTracker t2 | result = classRef(t2).track(t2, t))
}
/** Gets a reference to the `django.http.request.HttpRequest` class. */
DataFlow::Node classRef() { result = classRef(DataFlow::TypeTracker::end()) }
/**
* A source of an instance of `django.http.request.HttpRequest`.
*
* This can include instantiation of the class, return value from function
* calls, or a special parameter that will be set when functions are call by external
* library.
*
* Use `django::http::request::HttpRequest::instance()` predicate to get
* references to instances of `django.http.request.HttpRequest`.
*/
abstract class InstanceSource extends DataFlow::Node { }
/** Gets a reference to an instance of `django.http.request.HttpRequest`. */
private DataFlow::Node instance(DataFlow::TypeTracker t) {
t.start() and
result instanceof InstanceSource
or
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
}
/** Gets a reference to an instance of `django.http.request.HttpRequest`. */
DataFlow::Node instance() { result = instance(DataFlow::TypeTracker::end()) }
}
}
}
}
// ---------------------------------------------------------------------------
// routing modeling
// ---------------------------------------------------------------------------
/**
* Gets a reference to the Function `func`.
*
* The idea is that this function should be used as a route handler when setting up a
* route, but currently it just tracks all functions, since we can't do type-tracking
* backwards yet (TODO).
*/
private DataFlow::Node djangoRouteHandlerFunctionTracker(DataFlow::TypeTracker t, Function func) {
t.start() and
result = DataFlow::exprNode(func.getDefinition())
or
exists(DataFlow::TypeTracker t2 |
result = djangoRouteHandlerFunctionTracker(t2, func).track(t2, t)
)
}
/**
* A call to the `django.db.connection.cursor.execute` function.
* Gets a reference to the Function `func`.
*
* See
* - https://docs.djangoproject.com/en/3.1/topics/db/sql/#executing-custom-sql-directly
* - https://docs.djangoproject.com/en/3.1/topics/db/sql/#connections-and-cursors
* The idea is that this function should be used as a route handler when setting up a
* route, but currently it just tracks all functions, since we can't do type-tracking
* backwards yet (TODO).
*/
private class DbConnectionExecute extends SqlExecution::Range, DataFlow::CfgNode {
private DataFlow::Node djangoRouteHandlerFunctionTracker(Function func) {
result = djangoRouteHandlerFunctionTracker(DataFlow::TypeTracker::end(), func)
}
/**
* A function that is used as a django route handler.
*/
private class DjangoRouteHandler extends Function {
DjangoRouteHandler() { exists(djangoRouteHandlerFunctionTracker(this)) }
/** Gets the index of the request parameter. */
int getRequestParamIndex() {
not this.isMethod() and
result = 0
or
this.isMethod() and
result = 1
}
/** Gets the request parameter. */
Parameter getRequestParam() { result = this.getArg(this.getRequestParamIndex()) }
}
abstract private class DjangoRouteSetup extends HTTP::Server::RouteSetup::Range, DataFlow::CfgNode {
abstract override DjangoRouteHandler getARouteHandler();
}
/**
* Gets the regex that is used by django to find routed parameters when using `django.urls.path`.
*
* Taken from https://github.com/django/django/blob/7d1bf29977bb368d7c28e7c6eb146db3b3009ae7/django/urls/resolvers.py#L199
*/
private string pathRoutedParameterRegex() {
result = "<(?:(?<converter>[^>:]+):)?(?<parameter>\\w+)>"
}
/**
* A call to `django.urls.path`.
*
* See https://docs.djangoproject.com/en/3.0/ref/urls/#path
*/
private class DjangoUrlsPathCall extends DjangoRouteSetup {
override CallNode node;
DbConnectionExecute() { node.getFunction() = django::db::execute().asCfgNode() }
DjangoUrlsPathCall() { node.getFunction() = django::urls::path().asCfgNode() }
override DataFlow::Node getSql() {
result.asCfgNode() in [node.getArg(0), node.getArgByName("sql")]
override DataFlow::Node getUrlPatternArg() {
result.asCfgNode() = [node.getArg(0), node.getArgByName("route")]
}
override DjangoRouteHandler getARouteHandler() {
exists(DataFlow::Node viewArg |
viewArg.asCfgNode() in [node.getArg(1), node.getArgByName("view")] and
djangoRouteHandlerFunctionTracker(result) = viewArg
)
}
override Parameter getARoutedParameter() {
// If we don't know the URL pattern, we simply mark all parameters as a routed
// parameter. This should give us more RemoteFlowSources but could also lead to
// more FPs. If this turns out to be the wrong tradeoff, we can always change our mind.
exists(DjangoRouteHandler routeHandler | routeHandler = this.getARouteHandler() |
not exists(this.getUrlPattern()) and
result in [routeHandler.getArg(_), routeHandler.getArgByName(_)] and
not result = any(int i | i <= routeHandler.getRequestParamIndex() | routeHandler.getArg(i))
)
or
exists(string name |
result = this.getARouteHandler().getArgByName(name) and
exists(string match |
match = this.getUrlPattern().regexpFind(pathRoutedParameterRegex(), _, _) and
name = match.regexpCapture(pathRoutedParameterRegex(), 2)
)
)
}
}
/**
* A call to the `annotate` function on a model using a `RawSQL` argument.
* A regex that is used in a call to `django.urls.re_path`.
*
* See https://docs.djangoproject.com/en/3.1/ref/models/querysets/#annotate
* Needs this subclass to be considered a RegexString.
*/
private class ObjectsAnnotate extends SqlExecution::Range, DataFlow::CfgNode {
override CallNode node;
ControlFlowNode sql;
private class DjangoUrlsRePathRegex extends RegexString {
DjangoUrlsRePathCall rePathCall;
ObjectsAnnotate() {
node.getFunction() = django::db::models::objects_attr("annotate").asCfgNode() and
django::db::models::expressions::RawSQL::classInstance(sql).asCfgNode() in [node.getArg(_),
node.getArgByName(_)]
DjangoUrlsRePathRegex() {
this instanceof StrConst and
DataFlow::localFlow(DataFlow::exprNode(this), rePathCall.getUrlPatternArg())
}
override DataFlow::Node getSql() { result.asCfgNode() = sql }
DjangoUrlsRePathCall getRePathCall() { result = rePathCall }
}
/**
* A call to the `raw` function on a model.
* A call to `django.urls.re_path`.
*
* See
* - https://docs.djangoproject.com/en/3.1/topics/db/sql/#django.db.models.Manager.raw
* - https://docs.djangoproject.com/en/3.1/ref/models/querysets/#raw
* See https://docs.djangoproject.com/en/3.0/ref/urls/#re_path
*/
private class ObjectsRaw extends SqlExecution::Range, DataFlow::CfgNode {
private class DjangoUrlsRePathCall extends DjangoRouteSetup {
override CallNode node;
ObjectsRaw() { node.getFunction() = django::db::models::objects_attr("raw").asCfgNode() }
DjangoUrlsRePathCall() { node.getFunction() = django::urls::re_path().asCfgNode() }
override DataFlow::Node getSql() { result.asCfgNode() = node.getArg(0) }
override DataFlow::Node getUrlPatternArg() {
result.asCfgNode() = [node.getArg(0), node.getArgByName("route")]
}
override DjangoRouteHandler getARouteHandler() {
exists(DataFlow::Node viewArg |
viewArg.asCfgNode() in [node.getArg(1), node.getArgByName("view")] and
djangoRouteHandlerFunctionTracker(result) = viewArg
)
}
override Parameter getARoutedParameter() {
// If we don't know the URL pattern, we simply mark all parameters as a routed
// parameter. This should give us more RemoteFlowSources but could also lead to
// more FPs. If this turns out to be the wrong tradeoff, we can always change our mind.
exists(DjangoRouteHandler routeHandler | routeHandler = this.getARouteHandler() |
not exists(this.getUrlPattern()) and
result in [routeHandler.getArg(_), routeHandler.getArgByName(_)] and
not result = any(int i | i <= routeHandler.getRequestParamIndex() | routeHandler.getArg(i))
)
or
exists(DjangoRouteHandler routeHandler, DjangoUrlsRePathRegex regex |
routeHandler = this.getARouteHandler() and
regex.getRePathCall() = this
|
// either using named capture groups (passed as keyword arguments) or using
// unnamed capture groups (passed as positional arguments)
not exists(regex.getGroupName(_, _)) and
// first group will have group number 1
result =
routeHandler.getArg(routeHandler.getRequestParamIndex() + regex.getGroupNumber(_, _))
or
result = routeHandler.getArgByName(regex.getGroupName(_, _))
)
}
}
/**
* A call to the `extra` function on a model.
*
* See https://docs.djangoproject.com/en/3.1/ref/models/querysets/#extra
*/
private class ObjectsExtra extends SqlExecution::Range, DataFlow::CfgNode {
override CallNode node;
// ---------------------------------------------------------------------------
// HttpRequest taint modeling
// ---------------------------------------------------------------------------
class DjangoRouteHandlerRequestParam extends django::http::request::HttpRequest::InstanceSource,
RemoteFlowSource::Range, DataFlow::ParameterNode {
DjangoRouteHandlerRequestParam() {
this.getParameter() = any(DjangoRouteSetup setup).getARouteHandler().getRequestParam()
}
ObjectsExtra() { node.getFunction() = django::db::models::objects_attr("extra").asCfgNode() }
override string getSourceType() { result = "django.http.request.HttpRequest" }
}
override DataFlow::Node getSql() {
result.asCfgNode() =
[node.getArg([0, 1, 3, 4]), node.getArgByName(["select", "where", "tables", "order_by"])]
private class DjangoHttpRequstAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
nodeFrom = django::http::request::HttpRequest::instance() and
exists(DataFlow::AttrRead read | nodeTo = read and read.getObject() = nodeFrom |
read.getAttributeName() in ["body",
// str / bytes
"path", "path_info", "method", "encoding", "content_type",
// django.http.QueryDict
// TODO: Model QueryDict
"GET", "POST",
// dict[str, str]
"content_params", "COOKIES",
// dict[str, Any]
"META",
// HttpHeaders (case insensitive dict-like)
"headers",
// MultiValueDict[str, UploadedFile]
// TODO: Model MultiValueDict
// TODO: Model UploadedFile
"FILES",
// django.urls.ResolverMatch
// TODO: Model ResolverMatch
"resolver_match"]
// TODO: Handle calls to methods
// TODO: Handle that a HttpRequest is iterable
)
}
}
}

View File

@@ -0,0 +1,578 @@
/**
* Provides classes modeling security-relevant aspects of the `fabric` PyPI package, for
* both version 1.x and 2.x.
*
* See
* - http://docs.fabfile.org/en/1.14/tutorial.html and
* - http://docs.fabfile.org/en/2.5/getting-started.html
*/
private import python
private import experimental.dataflow.DataFlow
private import experimental.dataflow.RemoteFlowSources
private import experimental.semmle.python.Concepts
/**
* Provides classes modeling security-relevant aspects of the `fabric` PyPI package, for
* version 1.x.
*
* See http://docs.fabfile.org/en/1.14/tutorial.html.
*/
private module FabricV1 {
/** Gets a reference to the `fabric` module. */
private DataFlow::Node fabric(DataFlow::TypeTracker t) {
t.start() and
result = DataFlow::importNode("fabric")
or
exists(DataFlow::TypeTracker t2 | result = fabric(t2).track(t2, t))
}
/** Gets a reference to the `fabric` module. */
DataFlow::Node fabric() { result = fabric(DataFlow::TypeTracker::end()) }
/**
* Gets a reference to the attribute `attr_name` of the `fabric` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node fabric_attr(DataFlow::TypeTracker t, string attr_name) {
attr_name in ["api"] and
(
t.start() and
result = DataFlow::importNode("fabric" + "." + attr_name)
or
t.startInAttr(attr_name) and
result = fabric()
)
or
// Due to bad performance when using normal setup with `fabric_attr(t2, attr_name).track(t2, t)`
// we have inlined that code and forced a join
exists(DataFlow::TypeTracker t2 |
exists(DataFlow::StepSummary summary |
fabric_attr_first_join(t2, attr_name, result, summary) and
t = t2.append(summary)
)
)
}
pragma[nomagic]
private predicate fabric_attr_first_join(
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res, DataFlow::StepSummary summary
) {
DataFlow::StepSummary::step(fabric_attr(t2, attr_name), res, summary)
}
/**
* Gets a reference to the attribute `attr_name` of the `fabric` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node fabric_attr(string attr_name) {
result = fabric_attr(DataFlow::TypeTracker::end(), attr_name)
}
/** Provides models for the `fabric` module. */
module fabric {
// -------------------------------------------------------------------------
// fabric.api
// -------------------------------------------------------------------------
/** Gets a reference to the `fabric.api` module. */
DataFlow::Node api() { result = fabric_attr("api") }
/** Provides models for the `fabric.api` module */
module api {
/**
* Gets a reference to the attribute `attr_name` of the `fabric.api` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node api_attr(DataFlow::TypeTracker t, string attr_name) {
attr_name in ["run", "local", "sudo"] and
(
t.start() and
result = DataFlow::importNode("fabric.api" + "." + attr_name)
or
t.startInAttr(attr_name) and
result = api()
)
or
// Due to bad performance when using normal setup with `api_attr(t2, attr_name).track(t2, t)`
// we have inlined that code and forced a join
exists(DataFlow::TypeTracker t2 |
exists(DataFlow::StepSummary summary |
api_attr_first_join(t2, attr_name, result, summary) and
t = t2.append(summary)
)
)
}
pragma[nomagic]
private predicate api_attr_first_join(
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res,
DataFlow::StepSummary summary
) {
DataFlow::StepSummary::step(api_attr(t2, attr_name), res, summary)
}
/**
* Gets a reference to the attribute `attr_name` of the `fabric.api` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node api_attr(string attr_name) {
result = api_attr(DataFlow::TypeTracker::end(), attr_name)
}
/**
* A call to either
* - `fabric.api.local`
* - `fabric.api.run`
* - `fabric.api.sudo`
* See
* - https://docs.fabfile.org/en/1.14/api/core/operations.html#fabric.operations.local
* - https://docs.fabfile.org/en/1.14/api/core/operations.html#fabric.operations.run
* - https://docs.fabfile.org/en/1.14/api/core/operations.html#fabric.operations.sudo
*/
private class FabricApiLocalRunSudoCall extends SystemCommandExecution::Range,
DataFlow::CfgNode {
override CallNode node;
FabricApiLocalRunSudoCall() {
node.getFunction() = api_attr(["local", "run", "sudo"]).asCfgNode()
}
override DataFlow::Node getCommand() {
result.asCfgNode() = [node.getArg(0), node.getArgByName("command")]
}
}
}
}
}
/**
* Provides classes modeling security-relevant aspects of the `fabric` PyPI package, for
* version 2.x.
*
* See http://docs.fabfile.org/en/2.5/getting-st arted.html.
*/
private module FabricV2 {
/** Gets a reference to the `fabric` module. */
private DataFlow::Node fabric(DataFlow::TypeTracker t) {
t.start() and
result = DataFlow::importNode("fabric")
or
exists(DataFlow::TypeTracker t2 | result = fabric(t2).track(t2, t))
}
/** Gets a reference to the `fabric` module. */
DataFlow::Node fabric() { result = fabric(DataFlow::TypeTracker::end()) }
/**
* Gets a reference to the attribute `attr_name` of the `fabric` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node fabric_attr(DataFlow::TypeTracker t, string attr_name) {
attr_name in ["connection",
// connection.py
"Connection",
// group.py
"group", "SerialGroup", "ThreadingGroup",
// tasks.py
"tasks", "task"] and
(
t.start() and
result = DataFlow::importNode("fabric" + "." + attr_name)
or
t.startInAttr(attr_name) and
result = fabric()
)
or
// Due to bad performance when using normal setup with `fabric_attr(t2, attr_name).track(t2, t)`
// we have inlined that code and forced a join
exists(DataFlow::TypeTracker t2 |
exists(DataFlow::StepSummary summary |
fabric_attr_first_join(t2, attr_name, result, summary) and
t = t2.append(summary)
)
)
}
pragma[nomagic]
private predicate fabric_attr_first_join(
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res, DataFlow::StepSummary summary
) {
DataFlow::StepSummary::step(fabric_attr(t2, attr_name), res, summary)
}
/**
* Gets a reference to the attribute `attr_name` of the `fabric` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node fabric_attr(string attr_name) {
result = fabric_attr(DataFlow::TypeTracker::end(), attr_name)
}
/** Provides models for the `fabric` module. */
module fabric {
// -------------------------------------------------------------------------
// fabric.connection
// -------------------------------------------------------------------------
/** Gets a reference to the `fabric.connection` module. */
DataFlow::Node connection() { result = fabric_attr("connection") }
/** Provides models for the `fabric.connection` module */
module connection {
/**
* Gets a reference to the attribute `attr_name` of the `fabric.connection` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node connection_attr(DataFlow::TypeTracker t, string attr_name) {
attr_name in ["Connection"] and
(
t.start() and
result = DataFlow::importNode("fabric.connection" + "." + attr_name)
or
t.startInAttr(attr_name) and
result = connection()
)
or
// Due to bad performance when using normal setup with `connection_attr(t2, attr_name).track(t2, t)`
// we have inlined that code and forced a join
exists(DataFlow::TypeTracker t2 |
exists(DataFlow::StepSummary summary |
connection_attr_first_join(t2, attr_name, result, summary) and
t = t2.append(summary)
)
)
}
pragma[nomagic]
private predicate connection_attr_first_join(
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res,
DataFlow::StepSummary summary
) {
DataFlow::StepSummary::step(connection_attr(t2, attr_name), res, summary)
}
/**
* Gets a reference to the attribute `attr_name` of the `fabric.connection` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node connection_attr(string attr_name) {
result = connection_attr(DataFlow::TypeTracker::end(), attr_name)
}
/**
* Provides models for the `fabric.connection.Connection` class
*
* See https://docs.fabfile.org/en/2.5/api/connection.html#fabric.connection.Connection.
*/
module Connection {
/** Gets a reference to the `fabric.connection.Connection` class. */
private DataFlow::Node classRef(DataFlow::TypeTracker t) {
t.start() and
result = connection_attr("Connection")
or
// handle `fabric.Connection` alias
t.start() and
result = fabric_attr("Connection")
or
exists(DataFlow::TypeTracker t2 | result = classRef(t2).track(t2, t))
}
/** Gets a reference to the `fabric.connection.Connection` class. */
DataFlow::Node classRef() { result = classRef(DataFlow::TypeTracker::end()) }
/**
* A source of an instance of `fabric.connection.Connection`.
*
* This can include instantiation of the class, return value from function
* calls, or a special parameter that will be set when functions are called by an external
* library.
*
* Use `Connection::instance()` predicate to get references to instances of `fabric.connection.Connection`.
*/
abstract class InstanceSource extends DataFlow::Node { }
private class ClassInstantiation extends InstanceSource, DataFlow::CfgNode {
override CallNode node;
ClassInstantiation() { node.getFunction() = classRef().asCfgNode() }
}
/** Gets a reference to an instance of `fabric.connection.Connection`. */
private DataFlow::Node instance(DataFlow::TypeTracker t) {
t.start() and
result instanceof InstanceSource
or
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
}
/** Gets a reference to an instance of `fabric.connection.Connection`. */
DataFlow::Node instance() { result = instance(DataFlow::TypeTracker::end()) }
/**
* Gets a reference to either `run`, `sudo`, or `local` method on a
* `fabric.connection.Connection` instance.
*
* See
* - https://docs.fabfile.org/en/2.5/api/connection.html#fabric.connection.Connection.run
* - https://docs.fabfile.org/en/2.5/api/connection.html#fabric.connection.Connection.sudo
* - https://docs.fabfile.org/en/2.5/api/connection.html#fabric.connection.Connection.local
*/
private DataFlow::Node instanceRunMethods(DataFlow::TypeTracker t) {
t.startInAttr(["run", "sudo", "local"]) and
result = instance()
or
exists(DataFlow::TypeTracker t2 | result = instanceRunMethods(t2).track(t2, t))
}
/**
* Gets a reference to either `run`, `sudo`, or `local` method on a
* `fabric.connection.Connection` instance.
*
* See
* - https://docs.fabfile.org/en/2.5/api/connection.html#fabric.connection.Connection.run
* - https://docs.fabfile.org/en/2.5/api/connection.html#fabric.connection.Connection.sudo
* - https://docs.fabfile.org/en/2.5/api/connection.html#fabric.connection.Connection.local
*/
DataFlow::Node instanceRunMethods() {
result = instanceRunMethods(DataFlow::TypeTracker::end())
}
}
}
/**
* A call to either `run`, `sudo`, or `local` on a `fabric.connection.Connection` instance.
* See
* - https://docs.fabfile.org/en/2.5/api/connection.html#fabric.connection.Connection.run
* - https://docs.fabfile.org/en/2.5/api/connection.html#fabric.connection.Connection.sudo
* - https://docs.fabfile.org/en/2.5/api/connection.html#fabric.connection.Connection.local
*/
private class FabricConnectionRunSudoLocalCall extends SystemCommandExecution::Range,
DataFlow::CfgNode {
override CallNode node;
FabricConnectionRunSudoLocalCall() {
node.getFunction() = fabric::connection::Connection::instanceRunMethods().asCfgNode()
}
override DataFlow::Node getCommand() {
result.asCfgNode() = [node.getArg(0), node.getArgByName("command")]
}
}
// -------------------------------------------------------------------------
// fabric.tasks
// -------------------------------------------------------------------------
/** Gets a reference to the `fabric.tasks` module. */
DataFlow::Node tasks() { result = fabric_attr("tasks") }
/** Provides models for the `fabric.tasks` module */
module tasks {
/** Gets a reference to the `fabric.tasks.task` decorator. */
private DataFlow::Node task(DataFlow::TypeTracker t) {
t.start() and
result = DataFlow::importNode("fabric.tasks.task")
or
t.startInAttr("task") and
result = tasks()
or
// Handle `fabric.task` alias
t.startInAttr("task") and
result = fabric()
or
exists(DataFlow::TypeTracker t2 | result = task(t2).track(t2, t))
}
/** Gets a reference to the `fabric.tasks.task` decorator. */
DataFlow::Node task() { result = task(DataFlow::TypeTracker::end()) }
}
class FabricTaskFirstParamConnectionInstance extends fabric::connection::Connection::InstanceSource,
DataFlow::ParameterNode {
FabricTaskFirstParamConnectionInstance() {
exists(Function func |
func.getADecorator() = fabric::tasks::task().asExpr() and
this.getParameter() = func.getArg(0)
)
}
}
// -------------------------------------------------------------------------
// fabric.group
// -------------------------------------------------------------------------
/** Gets a reference to the `fabric.group` module. */
DataFlow::Node group() { result = fabric_attr("group") }
/** Provides models for the `fabric.group` module */
module group {
/**
* Gets a reference to the attribute `attr_name` of the `fabric.group` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node group_attr(DataFlow::TypeTracker t, string attr_name) {
attr_name in ["SerialGroup", "ThreadingGroup"] and
(
t.start() and
result = DataFlow::importNode("fabric.group" + "." + attr_name)
or
t.startInAttr(attr_name) and
result = group()
)
or
// Due to bad performance when using normal setup with `group_attr(t2, attr_name).track(t2, t)`
// we have inlined that code and forced a join
exists(DataFlow::TypeTracker t2 |
exists(DataFlow::StepSummary summary |
group_attr_first_join(t2, attr_name, result, summary) and
t = t2.append(summary)
)
)
}
pragma[nomagic]
private predicate group_attr_first_join(
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res,
DataFlow::StepSummary summary
) {
DataFlow::StepSummary::step(group_attr(t2, attr_name), res, summary)
}
/**
* Gets a reference to the attribute `attr_name` of the `fabric.group` module.
* WARNING: Only holds for a few predefined attributes.
*/
private DataFlow::Node group_attr(string attr_name) {
result = group_attr(DataFlow::TypeTracker::end(), attr_name)
}
/**
* Provides models for the `fabric.group.Group` class and its subclasses.
*
* `fabric.group.Group` is an abstract class, that has concrete implementations
* `SerialGroup` and `ThreadingGroup`.
*
* See
* - https://docs.fabfile.org/en/2.5/api/group.html#fabric.group.Group
* - https://docs.fabfile.org/en/2.5/api/group.html#fabric.group.SerialGroup
* - https://docs.fabfile.org/en/2.5/api/group.html#fabric.group.ThreadingGroup
*/
module Group {
/**
* A source of an instance of a subclass of `fabric.group.Group`
*
* This can include instantiation of a class, return value from function
* calls, or a special parameter that will be set when functions are call by external
* library.
*
* Use `Group::subclassInstance()` predicate to get references to an instance of a subclass of `fabric.group.Group`.
*/
abstract class SubclassInstanceSource extends DataFlow::Node { }
/** Gets a reference to an instance of a subclass of `fabric.group.Group`. */
private DataFlow::Node subclassInstance(DataFlow::TypeTracker t) {
t.start() and
result instanceof SubclassInstanceSource
or
exists(DataFlow::TypeTracker t2 | result = subclassInstance(t2).track(t2, t))
}
/** Gets a reference to an instance of a subclass of `fabric.group.Group`. */
DataFlow::Node subclassInstance() {
result = subclassInstance(DataFlow::TypeTracker::end())
}
/**
* Gets a reference to the `run` method on an instance of a subclass of `fabric.group.Group`.
*
* See https://docs.fabfile.org/en/2.5/api/group.html#fabric.group.Group.run
*/
private DataFlow::Node subclassInstanceRunMethod(DataFlow::TypeTracker t) {
t.startInAttr("run") and
result = subclassInstance()
or
exists(DataFlow::TypeTracker t2 | result = subclassInstanceRunMethod(t2).track(t2, t))
}
/**
* Gets a reference to the `run` method on an instance of a subclass of `fabric.group.Group`.
*
* See https://docs.fabfile.org/en/2.5/api/group.html#fabric.group.Group.run
*/
DataFlow::Node subclassInstanceRunMethod() {
result = subclassInstanceRunMethod(DataFlow::TypeTracker::end())
}
}
/**
* A call to `run` on an instance of a subclass of `fabric.group.Group`.
*
* See https://docs.fabfile.org/en/2.5/api/group.html#fabric.group.Group.run
*/
private class FabricGroupRunCall extends SystemCommandExecution::Range, DataFlow::CfgNode {
override CallNode node;
FabricGroupRunCall() {
node.getFunction() = fabric::group::Group::subclassInstanceRunMethod().asCfgNode()
}
override DataFlow::Node getCommand() {
result.asCfgNode() = [node.getArg(0), node.getArgByName("command")]
}
}
/**
* Provides models for the `fabric.group.SerialGroup` class
*
* See https://docs.fabfile.org/en/2.5/api/group.html#fabric.group.SerialGroup.
*/
module SerialGroup {
/** Gets a reference to the `fabric.group.SerialGroup` class. */
private DataFlow::Node classRef(DataFlow::TypeTracker t) {
t.start() and
result = group_attr("SerialGroup")
or
// Handle `fabric.SerialGroup` alias
t.start() and
result = fabric_attr("SerialGroup")
or
exists(DataFlow::TypeTracker t2 | result = classRef(t2).track(t2, t))
}
/** Gets a reference to the `fabric.group.SerialGroup` class. */
private DataFlow::Node classRef() { result = classRef(DataFlow::TypeTracker::end()) }
private class ClassInstantiation extends Group::SubclassInstanceSource, DataFlow::CfgNode {
override CallNode node;
ClassInstantiation() { node.getFunction() = classRef().asCfgNode() }
}
}
/**
* Provides models for the `fabric.group.ThreadingGroup` class
*
* See https://docs.fabfile.org/en/2.5/api/group.html#fabric.group.ThreadingGroup.
*/
module ThreadingGroup {
/** Gets a reference to the `fabric.group.ThreadingGroup` class. */
private DataFlow::Node classRef(DataFlow::TypeTracker t) {
t.start() and
result = group_attr("ThreadingGroup")
or
// Handle `fabric.ThreadingGroup` alias
t.start() and
result = fabric_attr("ThreadingGroup")
or
exists(DataFlow::TypeTracker t2 | result = classRef(t2).track(t2, t))
}
/** Gets a reference to the `fabric.group.ThreadingGroup` class. */
DataFlow::Node classRef() { result = classRef(DataFlow::TypeTracker::end()) }
private class ClassInstantiation extends Group::SubclassInstanceSource, DataFlow::CfgNode {
override CallNode node;
ClassInstantiation() { node.getFunction() = classRef().asCfgNode() }
}
}
}
}
}

View File

@@ -131,16 +131,6 @@ private module Flask {
)
)
}
/** Gets the argument used to pass in the URL pattern. */
abstract DataFlow::Node getUrlPatternArg();
override string getUrlPattern() {
exists(StrConst str |
DataFlow::localFlow(DataFlow::exprNode(str), this.getUrlPatternArg()) and
result = str.getText()
)
}
}
/**