Merge branch 'main' into fastapi

This commit is contained in:
Rasmus Wriedt Larsen
2021-10-28 11:37:40 +02:00
706 changed files with 146534 additions and 1757 deletions

View File

@@ -27,6 +27,7 @@ private import semmle.python.frameworks.Psycopg2
private import semmle.python.frameworks.Pydantic
private import semmle.python.frameworks.PyMySQL
private import semmle.python.frameworks.Rsa
private import semmle.python.frameworks.RuamelYaml
private import semmle.python.frameworks.Simplejson
private import semmle.python.frameworks.SqlAlchemy
private import semmle.python.frameworks.Starlette

View File

@@ -0,0 +1,57 @@
/**
* Provides classes modeling security-relevant aspects of the `ruamel.yaml` PyPI package
*
* See
* - https://pypi.org/project/ruamel.yaml/
*/
private import python
private import semmle.python.dataflow.new.DataFlow
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
/**
* Provides models for the `ruamel.yaml` PyPI package.
*
* See
* - https://pypi.org/project/ruamel.yaml/
*/
private module RuamelYaml {
// Note: `ruamel.yaml` is a fork of the `PyYAML` PyPI package, so that's why the
// interface is so similar.
/**
* A call to any of the loading functions in `yaml` (`load`, `load_all`, `safe_load`, `safe_load_all`)
*
* See https://pyyaml.org/wiki/PyYAMLDocumentation (you will have to scroll down).
*/
private class RuamelYamlLoadCall extends Decoding::Range, DataFlow::CallCfgNode {
string func_name;
RuamelYamlLoadCall() {
func_name in ["load", "load_all", "safe_load", "safe_load_all"] and
this = API::moduleImport("ruamel").getMember("yaml").getMember(func_name).getACall()
}
override predicate mayExecuteInput() {
func_name in ["load", "load_all"] and
// If the `Loader` argument is not set, the default loader will be used, which is
// not safe. The only safe loaders are `SafeLoader` or `BaseLoader` (and their
// variants with C implementation).
not exists(DataFlow::Node loader_arg |
loader_arg in [this.getArg(1), this.getArgByName("Loader")]
|
loader_arg =
API::moduleImport("ruamel")
.getMember("yaml")
.getMember(["SafeLoader", "BaseLoader", "CSafeLoader", "CBaseLoader"])
.getAUse()
)
}
override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("stream")] }
override DataFlow::Node getOutput() { result = this }
override string getFormat() { result = "YAML" }
}
}

View File

@@ -9,7 +9,6 @@
private import python
private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
@@ -41,11 +40,17 @@ private module Yaml {
}
/**
* This function was thought safe from the 5.1 release in 2017, when the default loader was changed to `FullLoader`.
* In 2020 new exploits were found, meaning it's not safe. The Current plan is to change the default to `SafeLoader` in release 6.0
* (as explained in https://github.com/yaml/pyyaml/issues/420#issuecomment-696752389).
* Until 6.0 is released, we will mark `yaml.load` as possibly leading to arbitrary code execution.
* See https://github.com/yaml/pyyaml/wiki/PyYAML-yaml.load(input)-Deprecation for more details.
* This function was thought safe from the 5.1 release in 2017, when the default
* loader was changed to `FullLoader` (see
* https://github.com/yaml/pyyaml/wiki/PyYAML-yaml.load(input)-Deprecation).
*
* In 2020 new exploits were found, meaning it's not safe. With the 6.0 release (see
* https://github.com/yaml/pyyaml/commit/8cdff2c80573b8be8e8ad28929264a913a63aa33),
* when using `load` and `load_all` you are now required to specify a Loader. But
* from what I (@RasmusWL) can gather, `FullLoader` is not to be considered safe,
* although known exploits have been mitigated (is at least my impression). Also see
* https://github.com/yaml/pyyaml/issues/420#issuecomment-696752389 for more
* details.
*/
override predicate mayExecuteInput() {
func_name in ["full_load", "full_load_all", "unsafe_load", "unsafe_load_all"]
@@ -63,7 +68,7 @@ private module Yaml {
)
}
override DataFlow::Node getAnInput() { result = this.getArg(0) }
override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("stream")] }
override DataFlow::Node getOutput() { result = this }

View File

@@ -102,15 +102,7 @@ string mode_from_node(DataFlow::Node node) { node = re_flag_tracker(result) }
* Gets a regular expression mode flag associated with the given value.
*/
deprecated string mode_from_mode_object(Value obj) {
(
result = "DEBUG" or
result = "IGNORECASE" or
result = "LOCALE" or
result = "MULTILINE" or
result = "DOTALL" or
result = "UNICODE" or
result = "VERBOSE"
) and
result in ["DEBUG", "IGNORECASE", "LOCALE", "MULTILINE", "DOTALL", "UNICODE", "VERBOSE"] and
exists(int flag |
flag = Value::named("sre_constants.SRE_FLAG_" + result).(OI::ObjectInternal).intValue() and
obj.(OI::ObjectInternal).intValue().bitAnd(flag) = flag
@@ -611,14 +603,7 @@ abstract class RegexString extends Expr {
this.getChar(start + 1) = "?" and
end = start + 3 and
c = this.getChar(start + 2) and
(
c = "i" or
c = "L" or
c = "m" or
c = "s" or
c = "u" or
c = "x"
)
c in ["i", "L", "m", "s", "u", "x"]
}
/**

View File

@@ -74,13 +74,10 @@ class ExceptionInfoSequence extends SequenceKind {
class CallToTracebackFunction extends ErrorInfoSource {
CallToTracebackFunction() {
exists(string name |
name = "extract_tb" or
name = "extract_stack" or
name = "format_list" or
name = "format_exception_only" or
name = "format_exception" or
name = "format_tb" or
name = "format_stack"
name in [
"extract_tb", "extract_stack", "format_list", "format_exception_only", "format_exception",
"format_tb", "format_stack"
]
|
this = traceback_function(name).getACall()
)

View File

@@ -112,14 +112,7 @@ class BottleRoutePointToExtension extends PointsToExtension {
/* Python 3.6+ regex module constants */
string short_flag(string flag) {
(
flag = "ASCII" or
flag = "IGNORECASE" or
flag = "LOCALE" or
flag = "UNICODE" or
flag = "MULTILINE" or
flag = "TEMPLATE"
) and
flag in ["ASCII", "IGNORECASE", "LOCALE", "UNICODE", "MULTILINE", "TEMPLATE"] and
result = flag.prefix(1)
or
flag = "DOTALL" and result = "S"

View File

@@ -183,6 +183,7 @@ class PyFunctionObject extends FunctionObject {
}
/** Factored out to help join ordering */
pragma[noinline]
private predicate implicitlyReturns(Object none_, ClassObject noneType) {
noneType = theNoneType() and
not this.getFunction().isGenerator() and

View File

@@ -1,13 +1,5 @@
/** Gets an HTTP verb, in upper case */
string httpVerb() {
result = "GET" or
result = "POST" or
result = "PUT" or
result = "PATCH" or
result = "DELETE" or
result = "OPTIONS" or
result = "HEAD"
}
string httpVerb() { result in ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"] }
/** Gets an HTTP verb, in lower case */
string httpVerbLower() { result = httpVerb().toLowerCase() }

View File

@@ -15,31 +15,11 @@ class DjangoDbTableObjects extends TaintKind {
override TaintKind getTaintOfMethodResult(string name) {
result = this and
(
name = "filter" or
name = "exclude" or
name = "annotate" or
name = "order_by" or
name = "reverse" or
name = "distinct" or
name = "values" or
name = "values_list" or
name = "dates" or
name = "datetimes" or
name = "none" or
name = "all" or
name = "union" or
name = "intersection" or
name = "difference" or
name = "select_related" or
name = "prefetch_related" or
name = "extra" or
name = "defer" or
name = "only" or
name = "using" or
name = "select_for_update" or
name = "raw"
)
name in [
"filter", "exclude", "none", "all", "union", "intersection", "difference", "select_related",
"prefetch_related", "extra", "defer", "only", "annotate", "using", "select_for_update",
"raw", "order_by", "reverse", "distinct", "values", "values_list", "dates", "datetimes"
]
}
}

View File

@@ -12,13 +12,7 @@ class FalconRequest extends TaintKind {
name = "env" and result instanceof WsgiEnvironment
or
result instanceof ExternalStringKind and
(
name = "uri" or
name = "url" or
name = "forwarded_uri" or
name = "relative_uri" or
name = "query_string"
)
name in ["uri", "url", "forwarded_uri", "relative_uri", "query_string"]
or
result instanceof ExternalStringDictKind and
(name = "cookies" or name = "params")

View File

@@ -32,12 +32,7 @@ class FlaskRequestData extends HttpRequestTaintSource {
class FlaskRequestArgs extends HttpRequestTaintSource {
FlaskRequestArgs() {
exists(string attr | flask_request_attr(this, attr) |
attr = "args" or
attr = "form" or
attr = "values" or
attr = "files" or
attr = "headers" or
attr = "json"
attr in ["args", "form", "values", "files", "headers", "json"]
)
}