mirror of
https://github.com/github/codeql.git
synced 2026-05-01 11:45:14 +02:00
Merge branch 'master' into python-mutable-default-with-flow
This commit is contained in:
@@ -14,8 +14,13 @@ import python
|
||||
|
||||
predicate mutates_descriptor(ClassObject cls, SelfAttributeStore s) {
|
||||
cls.isDescriptorType() and
|
||||
exists(PyFunctionObject f |
|
||||
cls.lookupAttribute(_) = f and
|
||||
exists(PyFunctionObject f, PyFunctionObject get_set |
|
||||
exists(string name |
|
||||
cls.lookupAttribute(name) = get_set |
|
||||
name = "__get__" or name = "__set__" or name = "__delete__"
|
||||
) and
|
||||
cls.lookupAttribute(_) = f and
|
||||
get_set.getACallee*() = f and
|
||||
not f.getName() = "__init__" and
|
||||
s.getScope() = f.getFunction()
|
||||
)
|
||||
|
||||
@@ -67,9 +67,9 @@ predicate subscript(Stmt s) {
|
||||
predicate encode_decode(Expr ex, ClassObject type) {
|
||||
exists(string name |
|
||||
ex.(Call).getFunc().(Attribute).getName() = name |
|
||||
name = "encode" and type = builtin_object("UnicodeEncodeError")
|
||||
name = "encode" and type = Object::builtin("UnicodeEncodeError")
|
||||
or
|
||||
name = "decode" and type = builtin_object("UnicodeDecodeError")
|
||||
name = "decode" and type = Object::builtin("UnicodeDecodeError")
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@ import python
|
||||
|
||||
/** Holds if `notimpl` refers to `NotImplemented` or `NotImplemented()` in the `raise` statement */
|
||||
predicate use_of_not_implemented_in_raise(Raise raise, Expr notimpl) {
|
||||
notimpl.refersTo(theNotImplementedObject()) and
|
||||
notimpl.refersTo(Object::notImplemented()) and
|
||||
(
|
||||
notimpl = raise.getException() or
|
||||
notimpl = raise.getException() or
|
||||
notimpl = raise.getException().(Call).getFunc()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -13,11 +13,11 @@
|
||||
import python
|
||||
|
||||
FunctionObject iter() {
|
||||
result = builtin_object("iter")
|
||||
result = Object::builtin("iter")
|
||||
}
|
||||
|
||||
FunctionObject next() {
|
||||
result = builtin_object("next")
|
||||
result = Object::builtin("next")
|
||||
}
|
||||
|
||||
predicate call_to_iter(CallNode call, EssaVariable sequence) {
|
||||
|
||||
@@ -107,7 +107,7 @@ private predicate brace_pair(PossibleAdvancedFormatString fmt, int start, int en
|
||||
private predicate advanced_format_call(Call format_expr, PossibleAdvancedFormatString fmt, int args) {
|
||||
exists(CallNode call |
|
||||
call = format_expr.getAFlowNode() |
|
||||
call.getFunction().refersTo(theFormatFunction()) and call.getArg(0).refersTo(_, fmt.getAFlowNode()) and
|
||||
call.getFunction().refersTo(Object::builtin("format")) and call.getArg(0).refersTo(_, fmt.getAFlowNode()) and
|
||||
args = count(format_expr.getAnArg()) - 1
|
||||
or
|
||||
call.getFunction().(AttrNode).getObject("format").refersTo(_, fmt.getAFlowNode()) and
|
||||
|
||||
@@ -19,7 +19,7 @@ import python
|
||||
|
||||
predicate numpy_array_type(ClassObject na) {
|
||||
exists(ModuleObject np | np.getName() = "numpy" or np.getName() = "numpy.core" |
|
||||
na.getAnImproperSuperType() = np.getAttribute("ndarray")
|
||||
na.getAnImproperSuperType() = np.attr("ndarray")
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -13,5 +13,5 @@ import python
|
||||
|
||||
from CallNode call, ControlFlowNode func
|
||||
where
|
||||
major_version() = 2 and call.getFunction() = func and func.refersTo(theApplyFunction())
|
||||
major_version() = 2 and call.getFunction() = func and func.refersTo(Object::builtin("apply"))
|
||||
select call, "Call to the obsolete builtin function 'apply'."
|
||||
|
||||
@@ -14,5 +14,5 @@ import python
|
||||
|
||||
from CallNode call, Context context, ControlFlowNode func
|
||||
where
|
||||
context.getAVersion().includes(2, _) and call.getFunction() = func and func.refersTo(context, theInputFunction(), _, _)
|
||||
context.getAVersion().includes(2, _) and call.getFunction() = func and func.refersTo(context, Object::builtin("input"), _, _)
|
||||
select call, "The unsafe built-in function 'input' is used."
|
||||
|
||||
@@ -70,11 +70,11 @@ predicate correct_raise(string name, ClassObject ex) {
|
||||
predicate preferred_raise(string name, ClassObject ex) {
|
||||
attribute_method(name) and ex = theAttributeErrorType()
|
||||
or
|
||||
indexing_method(name) and ex = builtin_object("LookupError")
|
||||
indexing_method(name) and ex = Object::builtin("LookupError")
|
||||
or
|
||||
ordering_method(name) and ex = theTypeErrorType()
|
||||
or
|
||||
arithmetic_method(name) and ex = builtin_object("ArithmeticError")
|
||||
arithmetic_method(name) and ex = Object::builtin("ArithmeticError")
|
||||
}
|
||||
|
||||
predicate no_need_to_raise(string name, string message) {
|
||||
|
||||
@@ -128,7 +128,7 @@ predicate function_should_close_parameter(Function func) {
|
||||
}
|
||||
|
||||
predicate function_opens_file(FunctionObject f) {
|
||||
f = theOpenFunction()
|
||||
f = Object::builtin("open")
|
||||
or
|
||||
exists(EssaVariable v, Return ret |
|
||||
ret.getScope() = f.getFunction() |
|
||||
|
||||
@@ -15,7 +15,7 @@ import python
|
||||
ClassObject jinja2EnvironmentOrTemplate() {
|
||||
exists(ModuleObject jinja2, string name |
|
||||
jinja2.getName() = "jinja2" and
|
||||
jinja2.getAttribute(name) = result |
|
||||
jinja2.attr(name) = result |
|
||||
name = "Environment" or
|
||||
name = "Template"
|
||||
)
|
||||
|
||||
@@ -17,7 +17,7 @@ import semmle.python.web.Http
|
||||
FunctionObject requestFunction() {
|
||||
exists(ModuleObject req |
|
||||
req.getName() = "requests" and
|
||||
result = req.getAttribute(httpVerbLower())
|
||||
result = req.attr(httpVerbLower())
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ int minimumSecureKeySize(string algo) {
|
||||
|
||||
predicate dsaRsaKeySizeArg(FunctionObject obj, string algorithm, string arg) {
|
||||
exists(ModuleObject mod |
|
||||
mod.getAttribute(_) = obj |
|
||||
mod.attr(_) = obj |
|
||||
algorithm = "DSA" and
|
||||
(
|
||||
mod.getName() = "cryptography.hazmat.primitives.asymmetric.dsa" and arg = "key_size"
|
||||
@@ -44,7 +44,7 @@ predicate dsaRsaKeySizeArg(FunctionObject obj, string algorithm, string arg) {
|
||||
|
||||
predicate ecKeySizeArg(FunctionObject obj, string arg) {
|
||||
exists(ModuleObject mod |
|
||||
mod.getAttribute(_) = obj |
|
||||
mod.attr(_) = obj |
|
||||
mod.getName() = "cryptography.hazmat.primitives.asymmetric.ec" and arg = "curve"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -13,11 +13,11 @@
|
||||
import python
|
||||
|
||||
FunctionObject ssl_wrap_socket() {
|
||||
result = any(ModuleObject ssl | ssl.getName() = "ssl").getAttribute("wrap_socket")
|
||||
result = any(ModuleObject ssl | ssl.getName() = "ssl").attr("wrap_socket")
|
||||
}
|
||||
|
||||
ClassObject ssl_Context_class() {
|
||||
result = any(ModuleObject ssl | ssl.getName() = "ssl").getAttribute("SSLContext")
|
||||
result = any(ModuleObject ssl | ssl.getName() = "ssl").attr("SSLContext")
|
||||
}
|
||||
|
||||
CallNode unsafe_call(string method_name) {
|
||||
|
||||
@@ -12,11 +12,11 @@
|
||||
import python
|
||||
|
||||
FunctionObject ssl_wrap_socket() {
|
||||
result = the_ssl_module().getAttribute("wrap_socket")
|
||||
result = the_ssl_module().attr("wrap_socket")
|
||||
}
|
||||
|
||||
ClassObject ssl_Context_class() {
|
||||
result = the_ssl_module().getAttribute("SSLContext")
|
||||
result = the_ssl_module().attr("SSLContext")
|
||||
}
|
||||
|
||||
string insecure_version_name() {
|
||||
@@ -69,20 +69,20 @@ predicate unsafe_ssl_wrap_socket_call(CallNode call, string method_name, string
|
||||
insecure_version = insecure_version_name()
|
||||
and
|
||||
(
|
||||
call.getArgByName("ssl_version").refersTo(the_ssl_module().getAttribute(insecure_version))
|
||||
call.getArgByName("ssl_version").refersTo(the_ssl_module().attr(insecure_version))
|
||||
or
|
||||
probable_insecure_ssl_constant(call, insecure_version)
|
||||
)
|
||||
}
|
||||
|
||||
ClassObject the_pyOpenSSL_Context_class() {
|
||||
result = any(ModuleObject m | m.getName() = "pyOpenSSL.SSL").getAttribute("Context")
|
||||
result = any(ModuleObject m | m.getName() = "pyOpenSSL.SSL").attr("Context")
|
||||
}
|
||||
|
||||
predicate unsafe_pyOpenSSL_Context_call(CallNode call, string insecure_version) {
|
||||
call = the_pyOpenSSL_Context_class().getACall() and
|
||||
insecure_version = insecure_version_name() and
|
||||
call.getArg(0).refersTo(the_pyOpenSSL_module().getAttribute(insecure_version))
|
||||
call.getArg(0).refersTo(the_pyOpenSSL_module().attr(insecure_version))
|
||||
}
|
||||
|
||||
from CallNode call, string method_name, string insecure_version
|
||||
|
||||
7
python/ql/src/Security/CWE-377/InsecureTemporaryFile.py
Normal file
7
python/ql/src/Security/CWE-377/InsecureTemporaryFile.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from tempfile import mktemp
|
||||
|
||||
def write_results(results):
|
||||
filename = mktemp()
|
||||
with open(filename, "w+") as f:
|
||||
f.write(results)
|
||||
print("Results written to", filename)
|
||||
52
python/ql/src/Security/CWE-377/InsecureTemporaryFile.qhelp
Normal file
52
python/ql/src/Security/CWE-377/InsecureTemporaryFile.qhelp
Normal file
@@ -0,0 +1,52 @@
|
||||
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
Functions that create temporary file names (such as <code>tempfile.mktemp</code>
|
||||
and <code>os.tempnam</code>) are fundamentally insecure, as they do not
|
||||
ensure exclusive access to a file with the temporary name they return.
|
||||
The file name returned by these functions is guaranteed to be unique on
|
||||
creation but the file must be opened in a separate operation. There is no
|
||||
guarantee that the creation and open operations will happen atomically. This
|
||||
provides an opportunity for an attacker to interfere with the file before it is
|
||||
opened.
|
||||
</p>
|
||||
<p>
|
||||
Note that <code>mktemp</code> has been deprecated since Python 2.3.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Replace the use of <code>mktemp</code> with some of the more secure functions
|
||||
in the <code>tempfile</code> module, such as <code>TemporaryFile</code>. If the
|
||||
file is intended to be accessed from other processes, consider using the
|
||||
<code>NamedTemporaryFile</code> function.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>
|
||||
The following piece of code opens a temporary file and writes a set of results
|
||||
to it. Because the file name is created using <code>mktemp</code>, another
|
||||
process may access this file before it is opened using <code>open</code>.
|
||||
</p>
|
||||
<sample src="InsecureTemporaryFile.py" />
|
||||
|
||||
<p>
|
||||
By changing the code to use <code>NamedTemporaryFile</code> instead, the file is
|
||||
opened immediately.
|
||||
</p>
|
||||
<sample src="SecureTemporaryFile.py" />
|
||||
|
||||
</example>
|
||||
|
||||
<references>
|
||||
|
||||
<li>
|
||||
Python Standard Library: <a href="https://docs.python.org/3/library/tempfile.html#tempfile.mktemp">tempfile.mktemp</a>.
|
||||
</li>
|
||||
</references>
|
||||
|
||||
</qhelp>
|
||||
31
python/ql/src/Security/CWE-377/InsecureTemporaryFile.ql
Normal file
31
python/ql/src/Security/CWE-377/InsecureTemporaryFile.ql
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* @name Insecure temporary file
|
||||
* @description Creating a temporary file using this method may be insecure.
|
||||
* @id py/insecure-temporary-file
|
||||
* @problem.severity error
|
||||
* @sub-severity high
|
||||
* @precision high
|
||||
* @tags external/cwe/cwe-377
|
||||
* security
|
||||
*/
|
||||
|
||||
import python
|
||||
|
||||
FunctionObject temporary_name_function(string mod, string function) {
|
||||
(
|
||||
mod = "tempfile" and function = "mktemp"
|
||||
or
|
||||
mod = "os" and
|
||||
(
|
||||
function = "tmpnam"
|
||||
or
|
||||
function = "tempnam"
|
||||
)
|
||||
) and
|
||||
result = any(ModuleObject m | m.getName() = mod).getAttribute(function)
|
||||
}
|
||||
|
||||
from Call c, string mod, string function
|
||||
where
|
||||
temporary_name_function(mod, function).getACall().getNode() = c
|
||||
select c, "Call to deprecated function " + mod + "." + function + " may be insecure."
|
||||
6
python/ql/src/Security/CWE-377/SecureTemporaryFile.py
Normal file
6
python/ql/src/Security/CWE-377/SecureTemporaryFile.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from tempfile import NamedTemporaryFile
|
||||
|
||||
def write_results(results):
|
||||
with NamedTemporaryFile(mode="w+", delete=False) as f:
|
||||
f.write(results)
|
||||
print("Results written to", f.name)
|
||||
@@ -35,12 +35,12 @@ string permissive_permission(int p) {
|
||||
}
|
||||
|
||||
predicate chmod_call(CallNode call, FunctionObject chmod, NumericObject num) {
|
||||
any(ModuleObject os | os.getName() = "os").getAttribute("chmod") = chmod and
|
||||
any(ModuleObject os | os.getName() = "os").attr("chmod") = chmod and
|
||||
chmod.getACall() = call and call.getArg(1).refersTo(num)
|
||||
}
|
||||
|
||||
predicate open_call(CallNode call, FunctionObject open, NumericObject num) {
|
||||
any(ModuleObject os | os.getName() = "os").getAttribute("open") = open and
|
||||
any(ModuleObject os | os.getName() = "os").attr("open") = open and
|
||||
open.getACall() = call and call.getArg(2).refersTo(num)
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ predicate fewer_characters_than(StrConst str, string char, float fraction) {
|
||||
}
|
||||
|
||||
predicate possible_reflective_name(string name) {
|
||||
exists(any(ModuleObject m).getAttribute(name))
|
||||
exists(any(ModuleObject m).attr(name))
|
||||
or
|
||||
exists(any(ClassObject c).lookupAttribute(name))
|
||||
or
|
||||
@@ -42,7 +42,7 @@ predicate possible_reflective_name(string name) {
|
||||
or
|
||||
any(ModuleObject m).getName() = name
|
||||
or
|
||||
exists(builtin_object(name))
|
||||
exists(Object::builtin(name))
|
||||
}
|
||||
|
||||
int char_count(StrConst str) {
|
||||
|
||||
@@ -39,14 +39,14 @@ predicate maybe_defined_in_outer_scope(Name n) {
|
||||
}
|
||||
|
||||
Variable relevant_var(Name n) {
|
||||
n.getVariable() = result and
|
||||
(corresponding(n, _) or corresponding(_, n))
|
||||
n.getVariable() = result and
|
||||
(corresponding(n, _) or corresponding(_, n))
|
||||
}
|
||||
|
||||
predicate same_name(Name n1, Name n2) {
|
||||
corresponding(n1, n2) and
|
||||
relevant_var(n1) = relevant_var(n2) and
|
||||
not exists(builtin_object(n1.getId())) and
|
||||
not exists(Object::builtin(n1.getId())) and
|
||||
not maybe_defined_in_outer_scope(n2)
|
||||
}
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ predicate in_notebook(Expr e) {
|
||||
}
|
||||
|
||||
FunctionObject assertRaises() {
|
||||
result = ModuleObject::named("unittest").getAttribute("TestCase").(ClassObject).lookupAttribute("assertRaises")
|
||||
result = ModuleObject::named("unittest").attr("TestCase").(ClassObject).lookupAttribute("assertRaises")
|
||||
}
|
||||
|
||||
/** Holds if expression `e` is in a `with` block that tests for exceptions being raised. */
|
||||
|
||||
@@ -12,5 +12,5 @@
|
||||
import python
|
||||
|
||||
from CallNode call, string name
|
||||
where call.getFunction().refersTo(quitterObject(name))
|
||||
where call.getFunction().refersTo(Object::quitter(name))
|
||||
select call, "The '" + name + "' site.Quitter object may not exist if the 'site' module is not loaded or is modified."
|
||||
|
||||
@@ -15,7 +15,7 @@ predicate monkey_patched_builtin(string name) {
|
||||
bltn.refersTo(theBuiltinModuleObject()) and
|
||||
call.getArg(1).getNode() = s and
|
||||
s.getText() = name and
|
||||
call.getFunction().refersTo(builtin_object("setattr"))
|
||||
call.getFunction().refersTo(Object::builtin("setattr"))
|
||||
)
|
||||
or
|
||||
exists(AttrNode attr |
|
||||
|
||||
@@ -45,7 +45,7 @@ predicate white_list(string name) {
|
||||
predicate shadows(Name d, string name, Scope scope, int line) {
|
||||
exists(LocalVariable l | d.defines(l) and scope instanceof Function and
|
||||
l.getId() = name and
|
||||
exists(builtin_object(l.getId()))
|
||||
exists(Object::builtin(l.getId()))
|
||||
) and
|
||||
d.getScope() = scope and
|
||||
d.getLocation().getStartLine() = line and
|
||||
|
||||
@@ -21,7 +21,7 @@ predicate shadows(Name d, GlobalVariable g, Scope scope, int line) {
|
||||
not exists(Import il, Import ig, Name gd | il.contains(d) and gd.defines(g) and ig.contains(gd)) and
|
||||
not exists(Assign a | a.getATarget() = d and a.getValue() = g.getAnAccess())
|
||||
) and
|
||||
not exists(builtin_object(g.getId())) and
|
||||
not exists(Object::builtin(g.getId())) and
|
||||
d.getScope() = scope and
|
||||
d.getLocation().getStartLine() = line and
|
||||
exists(Name defn | defn.defines(g) |
|
||||
|
||||
@@ -47,8 +47,8 @@ predicate one_item_only(For f) {
|
||||
predicate points_to_call_to_range(ControlFlowNode f) {
|
||||
/* (x)range is a function in Py2 and a class in Py3, so we must treat it as a plain object */
|
||||
exists(Object range, Object call |
|
||||
range = builtin_object("range") or
|
||||
range = builtin_object("xrange")
|
||||
range = Object::builtin("range") or
|
||||
range = Object::builtin("xrange")
|
||||
|
|
||||
f.refersTo(call) and
|
||||
call.(CallNode).getFunction().refersTo(range)
|
||||
|
||||
@@ -24,7 +24,7 @@ predicate declaredInAll(Module m, StrConst name)
|
||||
|
||||
predicate mutates_globals(PythonModuleObject m) {
|
||||
exists(CallNode globals |
|
||||
globals = theGlobalsFunction().(FunctionObject).getACall() and
|
||||
globals = Object::builtin("globals").(FunctionObject).getACall() and
|
||||
globals.getScope() = m.getModule() |
|
||||
exists(AttrNode attr | attr.getObject() = globals)
|
||||
or
|
||||
|
||||
@@ -90,8 +90,8 @@ predicate use_of_exec(Module m) {
|
||||
or
|
||||
exists(CallNode call, FunctionObject exec |
|
||||
exec.getACall() = call and call.getScope() = m |
|
||||
exec = builtin_object("exec") or
|
||||
exec = builtin_object("execfile")
|
||||
exec = Object::builtin("exec") or
|
||||
exec = Object::builtin("execfile")
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ private Object attribute_in_scope(Object obj, string name) {
|
||||
or
|
||||
exists(ModuleObject mod |
|
||||
mod = obj |
|
||||
mod.getAttribute(name) = result and result.(ControlFlowNode).getScope() = mod.getModule()
|
||||
mod.attr(name) = result and result.(ControlFlowNode).getScope() = mod.getModule()
|
||||
and not result.(ControlFlowNode).isEntryNode()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -396,7 +396,7 @@ private predicate attribute_assignment_jump_to_defn_attribute(AttributeAssignmen
|
||||
private predicate sets_attribute(ArgumentRefinement def, string name) {
|
||||
exists(CallNode call |
|
||||
call = def.getDefiningNode() and
|
||||
call.getFunction().refersTo(builtin_object("setattr")) and
|
||||
call.getFunction().refersTo(Object::builtin("setattr")) and
|
||||
def.getInput().getAUse() = call.getArg(0) and
|
||||
call.getArg(1).getNode().(StrConst).getText() = name
|
||||
)
|
||||
|
||||
@@ -162,14 +162,14 @@ predicate function_object_sanity(string clsname, string problem, string what) {
|
||||
|
||||
predicate multiple_origins_per_object(Object obj) {
|
||||
not obj.isC() and not obj instanceof ModuleObject and
|
||||
exists(ControlFlowNode use | strictcount(ControlFlowNode orig | use.refersTo(obj, orig)) > 1)
|
||||
exists(ControlFlowNode use, Context ctx | strictcount(ControlFlowNode orig | use.refersTo(ctx, obj, _, orig)) > 1)
|
||||
}
|
||||
|
||||
predicate intermediate_origins(ControlFlowNode use, ControlFlowNode inter, Object obj) {
|
||||
exists(ControlFlowNode orig |
|
||||
exists(ControlFlowNode orig, Context ctx |
|
||||
not inter = orig |
|
||||
use.refersTo(obj, inter) and
|
||||
inter.refersTo(obj, orig) and
|
||||
use.refersTo(ctx, obj, _, inter) and
|
||||
inter.refersTo(ctx, obj, _, orig) and
|
||||
// It can sometimes happen that two different modules (e.g. cPickle and Pickle)
|
||||
// have the same attribute, but different origins.
|
||||
not strictcount(Object val | inter.(AttrNode).getObject().refersTo(val)) > 1
|
||||
|
||||
@@ -391,10 +391,14 @@ class Location extends @location {
|
||||
|
||||
/** Gets the file for this location */
|
||||
File getFile() {
|
||||
result = this.getPath()
|
||||
}
|
||||
|
||||
private Container getPath() {
|
||||
locations_default(this, result, _, _, _, _)
|
||||
or
|
||||
exists(Module m | locations_ast(this, m, _, _, _, _) |
|
||||
result = m.getFile()
|
||||
result = m.getPath()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -423,7 +427,7 @@ class Location extends @location {
|
||||
}
|
||||
|
||||
string toString() {
|
||||
result = this.getFile().getName() + ":" + this.getStartLine().toString()
|
||||
result = this.getPath().getName() + ":" + this.getStartLine().toString()
|
||||
}
|
||||
|
||||
predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) {
|
||||
|
||||
@@ -1023,6 +1023,20 @@ class BasicBlock extends @py_flow_node {
|
||||
predicate likelyReachable() {
|
||||
start_bb_likely_reachable(this)
|
||||
}
|
||||
|
||||
/** Gets the `ConditionBlock`, if any, that controls this block and
|
||||
* does not control any other `ConditionBlock`s that control this block.
|
||||
* That is the `ConditionBlock` that is closest dominator.
|
||||
*/
|
||||
ConditionBlock getImmediatelyControllingBlock() {
|
||||
result = this.nonControllingImmediateDominator*().getImmediateDominator()
|
||||
}
|
||||
|
||||
private BasicBlock nonControllingImmediateDominator() {
|
||||
result = this.getImmediateDominator() and
|
||||
not result.(ConditionBlock).controls(this, _)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private predicate start_bb_likely_reachable(BasicBlock b) {
|
||||
|
||||
@@ -174,6 +174,14 @@ class Function extends Function_, Scope, AstNode {
|
||||
Scope.super.contains(inner)
|
||||
}
|
||||
|
||||
/** Gets a control flow node for a return value of this function */
|
||||
ControlFlowNode getAReturnValueFlowNode() {
|
||||
exists(Return ret |
|
||||
ret.getScope() = this and
|
||||
ret.getValue() = result.getNode()
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A def statement. Note that FunctionDef extends Assign as a function definition binds the newly created function */
|
||||
|
||||
@@ -115,6 +115,9 @@ class Module extends Module_, Scope, AstNode {
|
||||
|
||||
override Location getLocation() {
|
||||
py_scope_location(result, this)
|
||||
or
|
||||
not py_scope_location(_, this) and
|
||||
locations_ast(result, this, 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
/** Gets a child module or package of this package */
|
||||
|
||||
@@ -201,7 +201,7 @@ private predicate gettext_installed() {
|
||||
}
|
||||
|
||||
private predicate builtin_constant(string name) {
|
||||
exists(builtin_object(name))
|
||||
exists(Object::builtin(name))
|
||||
or
|
||||
name = "WindowsError"
|
||||
or
|
||||
|
||||
@@ -300,6 +300,15 @@ class EscapingGlobalVariable extends ModuleVariable {
|
||||
|
||||
}
|
||||
|
||||
class EscapingAssignmentGlobalVariable extends EscapingGlobalVariable {
|
||||
|
||||
EscapingAssignmentGlobalVariable() {
|
||||
exists(NameNode n | n.defines(this) and not n.getScope() = this.getScope())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class SpecialSsaSourceVariable extends PythonSsaSourceVariable {
|
||||
|
||||
SpecialSsaSourceVariable() {
|
||||
@@ -337,7 +346,7 @@ private predicate class_with_global_metaclass(Class cls, GlobalVariable metaclas
|
||||
|
||||
/** Holds if this variable is implicitly defined */
|
||||
private predicate implicit_definition(Variable v) {
|
||||
v.getId() = "*"
|
||||
v.getId() = "*" or v.getId() = "$"
|
||||
or
|
||||
exists(ImportStar is | is.getScope() = v.getScope())
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ abstract class ExternalPackage extends Object {
|
||||
abstract string getVersion();
|
||||
|
||||
Object getAttribute(string name) {
|
||||
result = this.(ModuleObject).getAttribute(name)
|
||||
result = this.(ModuleObject).attr(name)
|
||||
}
|
||||
|
||||
PackageObject getPackage() {
|
||||
|
||||
@@ -7,7 +7,7 @@ class UnitTestClass extends TestScope {
|
||||
UnitTestClass() {
|
||||
exists(ClassObject c |
|
||||
this = c.getPyClass() |
|
||||
c.getASuperType() = theUnitTestPackage().getAttribute(_)
|
||||
c.getASuperType() = theUnitTestPackage().attr(_)
|
||||
or
|
||||
c.getASuperType().getName().toLowerCase() = "testcase"
|
||||
)
|
||||
|
||||
@@ -8,7 +8,7 @@ class ZopeInterfaceMethod extends PyFunctionObject {
|
||||
/** Holds if this method belongs to a class that sub-classes `zope.interface.Interface` */
|
||||
ZopeInterfaceMethod() {
|
||||
exists(Object interface, ClassObject owner |
|
||||
ModuleObject::named("zope.interface").getAttribute("Interface") = interface and
|
||||
interface = ModuleObject::named("zope.interface").attr("Interface") and
|
||||
owner.declaredAttribute(_) = this and
|
||||
owner.getAnImproperSuperType().getABaseType() = interface
|
||||
)
|
||||
|
||||
@@ -196,7 +196,7 @@ predicate function_can_never_return(FunctionObject func) {
|
||||
not exists(f.getAnExitNode())
|
||||
)
|
||||
or
|
||||
func = theExitFunctionObject()
|
||||
func = ModuleObject::named("sys").attr("exit")
|
||||
}
|
||||
|
||||
/** Python specific sub-class of generic EssaNodeDefinition */
|
||||
@@ -570,13 +570,13 @@ predicate potential_builtin_points_to(NameNode f, Object value, ClassObject cls,
|
||||
(
|
||||
builtin_name_points_to(f.getId(), value, cls)
|
||||
or
|
||||
not exists(builtin_object(f.getId())) and value = unknownValue() and cls = theUnknownType()
|
||||
not exists(Object::builtin(f.getId())) and value = unknownValue() and cls = theUnknownType()
|
||||
)
|
||||
}
|
||||
|
||||
pragma [noinline]
|
||||
predicate builtin_name_points_to(string name, Object value, ClassObject cls) {
|
||||
value = builtin_object(name) and py_cobjecttypes(value, cls)
|
||||
value = Object::builtin(name) and py_cobjecttypes(value, cls)
|
||||
}
|
||||
|
||||
module BaseFlow {
|
||||
|
||||
@@ -31,14 +31,84 @@ private import Filters as BaseFilters
|
||||
import semmle.dataflow.SSA
|
||||
private import MRO
|
||||
|
||||
/** Get a `ControlFlowNode` from an object or `here`.
|
||||
* If the object is a ControlFlowNode then use that, otherwise fall back on `here`
|
||||
*/
|
||||
pragma[inline]
|
||||
private ControlFlowNode origin_from_object_or_here(ObjectOrCfg object, ControlFlowNode here) {
|
||||
result = object
|
||||
or
|
||||
not object instanceof ControlFlowNode and result = here
|
||||
/* Use this version for speed */
|
||||
library class CfgOrigin extends @py_object {
|
||||
|
||||
string toString() {
|
||||
/* Not to be displayed */
|
||||
none()
|
||||
}
|
||||
|
||||
/** Get a `ControlFlowNode` from `this` or `here`.
|
||||
* If `this` is a ControlFlowNode then use that, otherwise fall back on `here`
|
||||
*/
|
||||
pragma[inline]
|
||||
ControlFlowNode asCfgNodeOrHere(ControlFlowNode here) {
|
||||
result = this
|
||||
or
|
||||
not this instanceof ControlFlowNode and result = here
|
||||
}
|
||||
|
||||
ControlFlowNode toCfgNode() {
|
||||
result = this
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
CfgOrigin fix(ControlFlowNode here) {
|
||||
if this = unknownValue() then
|
||||
result = here
|
||||
else
|
||||
result = this
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Use this version for stronger type-checking */
|
||||
//private newtype TCfgOrigin =
|
||||
// TUnknownOrigin()
|
||||
// or
|
||||
// TCfgOrigin(ControlFlowNode f)
|
||||
//
|
||||
//library class CfgOrigin extends TCfgOrigin {
|
||||
//
|
||||
// string toString() {
|
||||
// /* Not to be displayed */
|
||||
// none()
|
||||
// }
|
||||
//
|
||||
// /** Get a `ControlFlowNode` from `this` or `here`.
|
||||
// * If `this` is a ControlFlowNode then use that, otherwise fall back on `here`
|
||||
// */
|
||||
// pragma[inline]
|
||||
// ControlFlowNode asCfgNodeOrHere(ControlFlowNode here) {
|
||||
// this = TUnknownOrigin() and result = here
|
||||
// or
|
||||
// this = TCfgOrigin(result)
|
||||
// }
|
||||
//
|
||||
// ControlFlowNode toCfgNode() {
|
||||
// this = TCfgOrigin(result)
|
||||
// }
|
||||
//
|
||||
// CfgOrigin fix(ControlFlowNode here) {
|
||||
// this = TUnknownOrigin() and result = TCfgOrigin(here)
|
||||
// or
|
||||
// not this = TUnknownOrigin() and result = this
|
||||
// }
|
||||
//}
|
||||
//
|
||||
|
||||
|
||||
module CfgOrigin {
|
||||
|
||||
CfgOrigin fromCfgNode(ControlFlowNode f) {
|
||||
result = f
|
||||
}
|
||||
|
||||
CfgOrigin unknown() {
|
||||
result = unknownValue()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module PointsTo {
|
||||
@@ -114,38 +184,41 @@ module PointsTo {
|
||||
/** INTERNAL -- Do not use.
|
||||
*
|
||||
* Holds if `package.name` points to `(value, cls, origin)`, where `package` is a package object. */
|
||||
cached predicate package_attribute_points_to(PackageObject package, string name, Object value, ClassObject cls, ControlFlowNode origin) {
|
||||
cached predicate package_attribute_points_to(PackageObject package, string name, Object value, ClassObject cls, CfgOrigin origin) {
|
||||
py_module_attributes(package.getInitModule().getModule(), name, value, cls, origin)
|
||||
or
|
||||
exists(Module init |
|
||||
init = package.getInitModule().getModule() |
|
||||
not exists(PythonSsaSourceVariable v | v.getScope() = init | v.getName() = name or v.getName() = "*")
|
||||
or
|
||||
exists(EssaVariable v, PointsToContext imp |
|
||||
v.getScope() = init and v.getName() = "*" and v.getAUse() = init.getANormalExit() |
|
||||
SSA::ssa_variable_named_attribute_points_to(v, imp, name, undefinedVariable(), _, _) and
|
||||
imp.isImport()
|
||||
init = package.getInitModule().getModule() and
|
||||
not exists(EssaVariable var | var.getAUse() = init.getANormalExit() and var.getSourceVariable().getName() = name) and
|
||||
exists(EssaVariable var, Context context |
|
||||
isModuleStateVariable(var) and var.getAUse() = init.getANormalExit() and
|
||||
context.isImport() and
|
||||
SSA::ssa_variable_named_attribute_points_to(var, context, name, undefinedVariable(), _, _) and
|
||||
origin = value and
|
||||
value = package.submodule(name) and
|
||||
cls = theModuleType()
|
||||
)
|
||||
) and explicitly_imported(value) and
|
||||
value = package.submodule(name) and cls = theModuleType() and origin = value
|
||||
)
|
||||
or
|
||||
package.hasNoInitModule() and
|
||||
value = package.submodule(name) and cls = theModuleType() and origin = CfgOrigin::fromCfgNode(value)
|
||||
}
|
||||
|
||||
/** INTERNAL -- `Use m.attributeRefersTo(name, obj, origin)` instead.
|
||||
*
|
||||
* Holds if `m.name` points to `(value, cls, origin)`, where `m` is a (source) module. */
|
||||
cached predicate py_module_attributes(Module m, string name, Object obj, ClassObject cls, ControlFlowNode origin) {
|
||||
exists(EssaVariable var, ControlFlowNode exit, ObjectOrCfg orig, PointsToContext imp |
|
||||
cached predicate py_module_attributes(Module m, string name, Object obj, ClassObject cls, CfgOrigin origin) {
|
||||
exists(EssaVariable var, ControlFlowNode exit, PointsToContext imp |
|
||||
exit = m.getANormalExit() and var.getAUse() = exit and
|
||||
var.getSourceVariable().getName() = name and
|
||||
ssa_variable_points_to(var, imp, obj, cls, orig) and
|
||||
ssa_variable_points_to(var, imp, obj, cls, origin) and
|
||||
imp.isImport() and
|
||||
obj != undefinedVariable() |
|
||||
origin = origin_from_object_or_here(orig, exit)
|
||||
obj != undefinedVariable()
|
||||
)
|
||||
or
|
||||
not exists(EssaVariable var | var.getAUse() = m.getANormalExit() and var.getSourceVariable().getName() = name) and
|
||||
exists(EssaVariable var, PointsToContext imp |
|
||||
var.getAUse() = m.getANormalExit() and var.getName() = "*" |
|
||||
var.getAUse() = m.getANormalExit() and isModuleStateVariable(var) |
|
||||
SSA::ssa_variable_named_attribute_points_to(var, imp, name, obj, cls, origin) and
|
||||
imp.isImport() and obj != undefinedVariable()
|
||||
)
|
||||
@@ -249,7 +322,7 @@ module PointsTo {
|
||||
|
||||
/** Holds if `call` is of the form `getattr(arg, "name")`. */
|
||||
cached predicate getattr(CallNode call, ControlFlowNode arg, string name) {
|
||||
points_to(call.getFunction(), _, builtin_object("getattr"), _, _) and
|
||||
points_to(call.getFunction(), _, Object::builtin("getattr"), _, _) and
|
||||
call.getArg(1).getNode().(StrConst).getText() = name and
|
||||
arg = call.getArg(0)
|
||||
}
|
||||
@@ -262,7 +335,7 @@ module PointsTo {
|
||||
}
|
||||
|
||||
/** Holds if `var` refers to `(value, cls, origin)` given the context `context`. */
|
||||
cached predicate ssa_variable_points_to(EssaVariable var, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
|
||||
cached predicate ssa_variable_points_to(EssaVariable var, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin) {
|
||||
SSA::ssa_definition_points_to(var.getDefinition(), context, value, cls, origin)
|
||||
}
|
||||
|
||||
@@ -374,10 +447,13 @@ module PointsTo {
|
||||
private module Layer {
|
||||
|
||||
/* Holds if BasicBlock `b` is reachable, given the context `context`. */
|
||||
predicate reachableBlock(BasicBlock b, PointsToContext context) {
|
||||
context.appliesToScope(b.getScope()) and
|
||||
forall(ConditionBlock guard |
|
||||
guard.controls(b, _) |
|
||||
predicate reachableBlock(BasicBlock b, PointsToContext context) {
|
||||
context.appliesToScope(b.getScope()) and not exists(ConditionBlock guard | guard.controls(b, _))
|
||||
or
|
||||
exists(ConditionBlock guard |
|
||||
guard = b.getImmediatelyControllingBlock() and
|
||||
reachableBlock(guard, context)
|
||||
|
|
||||
exists(Object value |
|
||||
points_to(guard.getLastNode(), context, value, _, _)
|
||||
|
|
||||
@@ -413,12 +489,12 @@ module PointsTo {
|
||||
}
|
||||
|
||||
/** Holds if `mod.name` points to `(value, cls, origin)`, where `mod` is a module object. */
|
||||
predicate module_attribute_points_to(ModuleObject mod, string name, Object value, ClassObject cls, ObjectOrCfg origin) {
|
||||
predicate module_attribute_points_to(ModuleObject mod, string name, Object value, ClassObject cls, CfgOrigin origin) {
|
||||
py_module_attributes(mod.getModule(), name, value, cls, origin)
|
||||
or
|
||||
package_attribute_points_to(mod, name, value, cls, origin)
|
||||
or
|
||||
builtin_module_attribute(mod, name, value, cls) and origin = value
|
||||
builtin_module_attribute(mod, name, value, cls) and origin = CfgOrigin::unknown()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -457,7 +533,10 @@ module PointsTo {
|
||||
or
|
||||
attribute_load_points_to(f, context, value, cls, origin)
|
||||
or
|
||||
getattr_points_to(f, context, value, cls, origin)
|
||||
exists(CfgOrigin orig |
|
||||
getattr_points_to(f, context, value, cls, orig) and
|
||||
origin = orig.asCfgNodeOrHere(f)
|
||||
)
|
||||
or
|
||||
if_exp_points_to(f, context, value, cls, origin)
|
||||
or
|
||||
@@ -521,7 +600,7 @@ module PointsTo {
|
||||
result.getSourceVariable() instanceof GlobalVariable
|
||||
}
|
||||
|
||||
private predicate use_points_to_maybe_origin(NameNode f, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin_or_obj) {
|
||||
private predicate use_points_to_maybe_origin(NameNode f, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin_or_obj) {
|
||||
ssa_variable_points_to(fast_local_variable(f), context, value, cls, origin_or_obj)
|
||||
or
|
||||
name_lookup_points_to_maybe_origin(f, context, value, cls, origin_or_obj)
|
||||
@@ -535,7 +614,7 @@ module PointsTo {
|
||||
ssa_variable_points_to(name_local_variable(f), context, undefinedVariable(), _, _)
|
||||
}
|
||||
|
||||
private predicate name_lookup_points_to_maybe_origin(NameNode f, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin_or_obj) {
|
||||
private predicate name_lookup_points_to_maybe_origin(NameNode f, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin_or_obj) {
|
||||
exists(EssaVariable var | var = name_local_variable(f) |
|
||||
ssa_variable_points_to(var, context, value, cls, origin_or_obj)
|
||||
)
|
||||
@@ -544,22 +623,26 @@ module PointsTo {
|
||||
global_lookup_points_to_maybe_origin(f, context, value, cls, origin_or_obj)
|
||||
}
|
||||
|
||||
private predicate global_lookup_points_to_maybe_origin(NameNode f, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin_or_obj) {
|
||||
private predicate global_lookup_points_to_maybe_origin(NameNode f, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin_or_obj) {
|
||||
ssa_variable_points_to(global_variable(f), context, value, cls, origin_or_obj)
|
||||
or
|
||||
ssa_variable_points_to(global_variable(f), context, undefinedVariable(), _, _) and
|
||||
potential_builtin_points_to(f, value, cls, origin_or_obj)
|
||||
or
|
||||
not exists(global_variable(f)) and context.appliesToScope(f.getScope()) and
|
||||
potential_builtin_points_to(f, value, cls, origin_or_obj)
|
||||
exists(ControlFlowNode origin |
|
||||
origin_or_obj = CfgOrigin::fromCfgNode(origin)
|
||||
|
|
||||
ssa_variable_points_to(global_variable(f), context, undefinedVariable(), _, _) and
|
||||
potential_builtin_points_to(f, value, cls, origin)
|
||||
or
|
||||
not exists(global_variable(f)) and context.appliesToScope(f.getScope()) and
|
||||
potential_builtin_points_to(f, value, cls, origin)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets an object pointed to by a use (of a variable). */
|
||||
private predicate use_points_to(NameNode f, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) {
|
||||
exists(ObjectOrCfg origin_or_obj |
|
||||
exists(CfgOrigin origin_or_obj |
|
||||
value != undefinedVariable() and
|
||||
use_points_to_maybe_origin(f, context, value, cls, origin_or_obj) |
|
||||
origin = origin_from_object_or_here(origin_or_obj, f)
|
||||
origin = origin_or_obj.asCfgNodeOrHere(f)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -579,12 +662,15 @@ module PointsTo {
|
||||
|
||||
/** Holds if `obj.name` points to `(value, cls, orig)`. */
|
||||
pragma [noinline]
|
||||
private predicate class_or_module_attribute(Object obj, string name, Object value, ClassObject cls, ObjectOrCfg orig) {
|
||||
private predicate class_or_module_attribute(Object obj, string name, Object value, ClassObject cls, CfgOrigin orig) {
|
||||
/* Normal class attributes */
|
||||
Types::class_attribute_lookup(obj, name, value, cls, orig) and cls != theStaticMethodType() and cls != theClassMethodType()
|
||||
or
|
||||
/* Static methods of the class */
|
||||
exists(CallNode sm | Types::class_attribute_lookup(obj, name, sm, theStaticMethodType(), _) and sm.getArg(0) = value and cls = thePyFunctionType() and orig = value)
|
||||
exists(CallNode sm |
|
||||
Types::class_attribute_lookup(obj, name, sm, theStaticMethodType(), _) and sm.getArg(0) = value and
|
||||
cls = thePyFunctionType() and orig = CfgOrigin::fromCfgNode(sm.getArg(0))
|
||||
)
|
||||
or
|
||||
/* Module attributes */
|
||||
Layer::module_attribute_points_to(obj, name, value, cls, orig)
|
||||
@@ -595,7 +681,10 @@ module PointsTo {
|
||||
private predicate instance_attribute_load_points_to(AttrNode f, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) {
|
||||
f.isLoad() and
|
||||
exists(string name |
|
||||
named_attribute_points_to(f.getObject(name), context, name, value, cls, origin)
|
||||
exists(CfgOrigin orig |
|
||||
origin = orig.asCfgNodeOrHere(f) and
|
||||
named_attribute_points_to(f.getObject(name), context, name, value, cls, orig)
|
||||
)
|
||||
or
|
||||
/* Static methods on the class of the instance */
|
||||
exists(CallNode sm, ClassObject icls |
|
||||
@@ -630,10 +719,10 @@ module PointsTo {
|
||||
private predicate attribute_load_points_to(AttrNode f, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) {
|
||||
instance_attribute_load_points_to(f, context, value, cls, origin)
|
||||
or
|
||||
exists(Object cls_or_mod, string name, ObjectOrCfg orig |
|
||||
exists(Object cls_or_mod, string name, CfgOrigin orig |
|
||||
receiver_object(f, context, cls_or_mod, name) and
|
||||
class_or_module_attribute(cls_or_mod, name, value, cls, orig) and
|
||||
origin = origin_from_object_or_here(orig, f)
|
||||
origin = orig.asCfgNodeOrHere(f)
|
||||
)
|
||||
or
|
||||
points_to(f.getObject(), context, unknownValue(), theUnknownType(), origin) and value = unknownValue() and cls = theUnknownType()
|
||||
@@ -661,24 +750,38 @@ module PointsTo {
|
||||
/** Holds if `f` is a "from import" expression, `from mod import x` and points to `(value, cls, origin)`. */
|
||||
pragma [nomagic]
|
||||
private predicate from_import_points_to(ImportMemberNode f, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) {
|
||||
exists(EssaVariable var, ObjectOrCfg orig |
|
||||
live_import_from_dot_in_init(f, var) and
|
||||
ssa_variable_points_to(var, context, value, cls, orig) and
|
||||
origin = origin_from_object_or_here(orig, f)
|
||||
exists(string name, ModuleObject mod, CfgOrigin orig |
|
||||
points_to(f.getModule(name), context, mod, _, _) and
|
||||
origin = orig.asCfgNodeOrHere(f)
|
||||
|
|
||||
mod.getSourceModule() = f.getEnclosingModule() and
|
||||
not exists(EssaVariable var | var.getSourceVariable().getName() = name and var.getAUse() = f) and
|
||||
exists(EssaVariable dollar |
|
||||
isModuleStateVariable(dollar) and dollar.getAUse() = f and
|
||||
SSA::ssa_variable_named_attribute_points_to(dollar, context, name, value, cls, orig)
|
||||
)
|
||||
or
|
||||
not mod.getSourceModule() = f.getEnclosingModule() and
|
||||
Layer::module_attribute_points_to(mod, name, value, cls, orig)
|
||||
)
|
||||
or
|
||||
not live_import_from_dot_in_init(f, _) and
|
||||
exists(string name, ModuleObject mod |
|
||||
points_to(f.getModule(name), context, mod, _, _) |
|
||||
exists(ObjectOrCfg orig |
|
||||
Layer::module_attribute_points_to(mod, name, value, cls, orig) and
|
||||
origin = origin_from_object_or_here(orig, f)
|
||||
)
|
||||
exists(EssaVariable var, CfgOrigin orig |
|
||||
var = ssa_variable_for_module_attribute(f, context) and
|
||||
ssa_variable_points_to(var, context, value, cls, orig) and
|
||||
origin = orig.asCfgNodeOrHere(f)
|
||||
)
|
||||
}
|
||||
|
||||
private EssaVariable ssa_variable_for_module_attribute(ImportMemberNode f, PointsToContext context) {
|
||||
exists(string name, ModuleObject mod, Module m |
|
||||
mod.getSourceModule() = m and m = f.getEnclosingModule() and m = result.getScope() and
|
||||
points_to(f.getModule(name), context, mod, _, _) and
|
||||
result.getSourceVariable().getName() = name and result.getAUse() = f
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `f` is of the form `getattr(x, "name")` and x.name points to `(value, cls, origin)`. */
|
||||
private predicate getattr_points_to(CallNode f, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) {
|
||||
private predicate getattr_points_to(CallNode f, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin) {
|
||||
exists(ControlFlowNode arg, string name |
|
||||
named_attribute_points_to(arg, context, name, value, cls, origin) and
|
||||
getattr(f, arg, name)
|
||||
@@ -966,7 +1069,7 @@ module PointsTo {
|
||||
)
|
||||
}
|
||||
|
||||
predicate named_attribute_points_to(ControlFlowNode f, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) {
|
||||
predicate named_attribute_points_to(ControlFlowNode f, PointsToContext context, string name, Object value, ClassObject cls, CfgOrigin origin) {
|
||||
exists(EssaVariable var |
|
||||
var.getAUse() = f |
|
||||
SSA::ssa_variable_named_attribute_points_to(var, context, name, value, cls, origin)
|
||||
@@ -1010,9 +1113,9 @@ module PointsTo {
|
||||
pragma [noinline]
|
||||
predicate call_points_to_builtin_function(CallNode f, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) {
|
||||
exists(BuiltinCallable b |
|
||||
b != builtin_object("isinstance") and
|
||||
b != builtin_object("issubclass") and
|
||||
b != builtin_object("callable") and
|
||||
b != Object::builtin("isinstance") and
|
||||
b != Object::builtin("issubclass") and
|
||||
b != Object::builtin("callable") and
|
||||
f = get_a_call(b, context) and
|
||||
cls = b.getAReturnType()
|
||||
) and
|
||||
@@ -1372,7 +1475,7 @@ module PointsTo {
|
||||
|
||||
/** Holds if the phi-function `phi` refers to `(value, cls, origin)` given the context `context`. */
|
||||
pragma [noinline]
|
||||
private predicate ssa_phi_points_to(PhiFunction phi, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
|
||||
private predicate ssa_phi_points_to(PhiFunction phi, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin) {
|
||||
exists(EssaVariable input, BasicBlock pred |
|
||||
input = phi.getInput(pred) and
|
||||
ssa_variable_points_to(input, context, value, cls, origin)
|
||||
@@ -1386,10 +1489,13 @@ module PointsTo {
|
||||
}
|
||||
|
||||
/** Holds if the ESSA definition `def` refers to `(value, cls, origin)` given the context `context`. */
|
||||
predicate ssa_definition_points_to(EssaDefinition def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
|
||||
predicate ssa_definition_points_to(EssaDefinition def, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin) {
|
||||
ssa_phi_points_to(def, context, value, cls, origin)
|
||||
or
|
||||
ssa_node_definition_points_to(def, context, value, cls, origin)
|
||||
exists(ControlFlowNode orig |
|
||||
ssa_node_definition_points_to(def, context, value, cls, orig) and
|
||||
origin = CfgOrigin::fromCfgNode(orig)
|
||||
)
|
||||
or
|
||||
Filters::ssa_filter_definition_points_to(def, context, value, cls, origin)
|
||||
or
|
||||
@@ -1397,7 +1503,7 @@ module PointsTo {
|
||||
}
|
||||
|
||||
pragma [nomagic]
|
||||
private predicate ssa_node_definition_points_to_unpruned(EssaNodeDefinition def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
|
||||
private predicate ssa_node_definition_points_to_unpruned(EssaNodeDefinition def, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) {
|
||||
assignment_points_to(def, context, value, cls, origin)
|
||||
or
|
||||
parameter_points_to(def, context, value, cls, origin)
|
||||
@@ -1406,12 +1512,12 @@ module PointsTo {
|
||||
or
|
||||
delete_points_to(def, context, value, cls, origin)
|
||||
or
|
||||
module_name_points_to(def, context, value, cls, origin)
|
||||
or
|
||||
scope_entry_points_to(def, context, value, cls, origin)
|
||||
or
|
||||
implicit_submodule_points_to(def, context, value, cls, origin)
|
||||
or
|
||||
module_name_points_to(def, context, value, cls, origin)
|
||||
or
|
||||
iteration_definition_points_to(def, context, value, cls, origin)
|
||||
/*
|
||||
* No points-to for non-local function entry definitions yet.
|
||||
@@ -1424,13 +1530,13 @@ module PointsTo {
|
||||
}
|
||||
|
||||
pragma [noinline]
|
||||
private predicate ssa_node_definition_points_to(EssaNodeDefinition def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
|
||||
private predicate ssa_node_definition_points_to(EssaNodeDefinition def, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) {
|
||||
reachable_definitions(def) and
|
||||
ssa_node_definition_points_to_unpruned(def, context, value, cls, origin)
|
||||
}
|
||||
|
||||
pragma [noinline]
|
||||
private predicate ssa_node_refinement_points_to(EssaNodeRefinement def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
|
||||
private predicate ssa_node_refinement_points_to(EssaNodeRefinement def, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin) {
|
||||
method_callsite_points_to(def, context, value, cls, origin)
|
||||
or
|
||||
import_star_points_to(def, context, value, cls, origin)
|
||||
@@ -1569,8 +1675,9 @@ module PointsTo {
|
||||
neither_class_nor_static_method(scope)
|
||||
)
|
||||
or
|
||||
exists(EssaVariable obj, PointsToContext caller |
|
||||
ssa_variable_points_to(obj, caller, value, cls, origin) and
|
||||
exists(EssaVariable obj, PointsToContext caller, CfgOrigin orig |
|
||||
origin = orig.asCfgNodeOrHere(def.getDefiningNode()) and
|
||||
ssa_variable_points_to(obj, caller, value, cls, orig) and
|
||||
callsite_self_argument_transfer(obj, caller, def, context)
|
||||
)
|
||||
or
|
||||
@@ -1626,7 +1733,7 @@ module PointsTo {
|
||||
* PointsTo isn't exactly how the interpreter works, but is the best approximation we can manage statically.
|
||||
*/
|
||||
pragma [noinline]
|
||||
private predicate implicit_submodule_points_to(ImplicitSubModuleDefinition def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
|
||||
private predicate implicit_submodule_points_to(ImplicitSubModuleDefinition def, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) {
|
||||
exists(PackageObject package |
|
||||
package.getInitModule().getModule() = def.getDefiningNode().getScope() |
|
||||
value = package.submodule(def.getSourceVariable().getName()) and
|
||||
@@ -1638,7 +1745,7 @@ module PointsTo {
|
||||
|
||||
/** Implicit "definition" of `__name__` at the start of a module. */
|
||||
pragma [noinline]
|
||||
private predicate module_name_points_to(ScopeEntryDefinition def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
|
||||
private predicate module_name_points_to(ScopeEntryDefinition def, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) {
|
||||
def.getVariable().getName() = "__name__" and
|
||||
exists(Module m |
|
||||
m = def.getScope()
|
||||
@@ -1662,24 +1769,25 @@ module PointsTo {
|
||||
|
||||
/** Definition of iteration variable in loop */
|
||||
pragma [noinline]
|
||||
private predicate iteration_definition_points_to(IterationDefinition def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
|
||||
private predicate iteration_definition_points_to(IterationDefinition def, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) {
|
||||
points_to(def.getSequence(), context, unknownValue(), _, _) and
|
||||
value = unknownValue() and cls = theUnknownType() and origin = def.getDefiningNode()
|
||||
}
|
||||
|
||||
/** Points-to for implicit variable declarations at scope-entry. */
|
||||
pragma [noinline]
|
||||
private predicate scope_entry_points_to(ScopeEntryDefinition def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
|
||||
private predicate scope_entry_points_to(ScopeEntryDefinition def, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) {
|
||||
/* Transfer from another scope */
|
||||
exists(EssaVariable var, PointsToContext outer |
|
||||
exists(EssaVariable var, PointsToContext outer, CfgOrigin orig |
|
||||
Flow::scope_entry_value_transfer(var, outer, def, context) and
|
||||
ssa_variable_points_to(var, outer, value, cls, origin)
|
||||
ssa_variable_points_to(var, outer, value, cls, orig) and
|
||||
origin = orig.asCfgNodeOrHere(def.getDefiningNode())
|
||||
)
|
||||
or
|
||||
/* Undefined variable */
|
||||
exists(Scope scope |
|
||||
not def.getVariable().getName() = "__name__" and
|
||||
not def.getVariable().getName() = "*" and
|
||||
not def.getVariable().getName() = "$" and
|
||||
def.getScope() = scope and context.appliesToScope(scope) |
|
||||
def.getSourceVariable() instanceof GlobalVariable and scope instanceof Module
|
||||
or
|
||||
@@ -1693,7 +1801,7 @@ module PointsTo {
|
||||
mod = def.getScope().getEnclosingModule() and
|
||||
context.appliesToScope(def.getScope()) and
|
||||
not exists(EssaVariable v | v.getSourceVariable() = var and v.getScope() = mod) and
|
||||
builtin_name_points_to(var.getId(), value, cls) and origin = value
|
||||
builtin_name_points_to(var.getId(), value, cls) and origin = def.getDefiningNode()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1702,39 +1810,54 @@ module PointsTo {
|
||||
* Where var may be redefined in call to `foo` if `var` escapes (is global or non-local).
|
||||
*/
|
||||
pragma [noinline]
|
||||
private predicate callsite_points_to(CallsiteRefinement def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
|
||||
exists(EssaVariable var, PointsToContext callee |
|
||||
Flow::callsite_exit_value_transfer(var, callee, def, context) and
|
||||
ssa_variable_points_to(var, callee, value, cls, origin)
|
||||
private predicate callsite_points_to(CallsiteRefinement def, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin) {
|
||||
exists(SsaSourceVariable srcvar |
|
||||
srcvar = def.getSourceVariable() |
|
||||
if srcvar instanceof EscapingAssignmentGlobalVariable then (
|
||||
/* If global variable can be reassigned, we need to track it through calls */
|
||||
exists(EssaVariable var, PointsToContext callee |
|
||||
Flow::callsite_exit_value_transfer(var, callee, def, context) and
|
||||
ssa_variable_points_to(var, callee, value, cls, origin)
|
||||
)
|
||||
or
|
||||
callsite_points_to_python(def, context, value, cls, origin)
|
||||
or
|
||||
callsite_points_to_builtin(def, context, value, cls, origin)
|
||||
) else (
|
||||
/* Otherwise we can assume its value (but not those of its attributes or members) has not changed. */
|
||||
ssa_variable_points_to(def.getInput(), context, value, cls, origin)
|
||||
)
|
||||
)
|
||||
or
|
||||
callsite_points_to_python(def, context, value, cls, origin)
|
||||
or
|
||||
callsite_points_to_builtin(def, context, value, cls, origin)
|
||||
}
|
||||
|
||||
pragma [noinline]
|
||||
private predicate callsite_points_to_python(CallsiteRefinement def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
|
||||
ssa_variable_points_to(def.getInput(), context, value, cls, origin) and
|
||||
exists(CallNode call, PythonSsaSourceVariable var |
|
||||
/** Holds if `call`, in `context` is a call to a function that does not modify the refined variable */
|
||||
private predicate call_to_safe_function(CallsiteRefinement def, PointsToContext context) {
|
||||
exists(CallNode call |
|
||||
call = def.getCall() and
|
||||
var = def.getSourceVariable() and
|
||||
context.untrackableCall(call) and
|
||||
exists(PyFunctionObject modifier, Function f |
|
||||
f = modifier.getFunction() and
|
||||
call = get_a_call(modifier, context) and
|
||||
not modifies_escaping_variable(f, var)
|
||||
not modifies_escaping_variable(f, def.getSourceVariable())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate callsite_points_to_python(CallsiteRefinement def, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin) {
|
||||
exists(EssaVariable input |
|
||||
input = def.getInput() and
|
||||
ssa_variable_points_to(input, context, value, cls, origin) and
|
||||
call_to_safe_function(def, context)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate modifies_escaping_variable(Function modifier, PythonSsaSourceVariable var) {
|
||||
exists(var.redefinedAtCallSite()) and
|
||||
modifier.getBody().contains(var.(Variable).getAStore())
|
||||
}
|
||||
|
||||
pragma [noinline]
|
||||
private predicate callsite_points_to_builtin(CallsiteRefinement def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
|
||||
private predicate callsite_points_to_builtin(CallsiteRefinement def, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin) {
|
||||
ssa_variable_points_to(def.getInput(), context, value, cls, origin) and
|
||||
exists(CallNode call |
|
||||
call = def.getCall() |
|
||||
@@ -1744,51 +1867,55 @@ module PointsTo {
|
||||
}
|
||||
|
||||
/** Pass through for `self` for the implicit re-definition of `self` in `self.foo()`. */
|
||||
private predicate method_callsite_points_to(MethodCallsiteRefinement def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
|
||||
private predicate method_callsite_points_to(MethodCallsiteRefinement def, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin) {
|
||||
/* The value of self remains the same, only the attributes may change */
|
||||
ssa_variable_points_to(def.getInput(), context, value, cls, origin)
|
||||
}
|
||||
|
||||
/** Points-to for `from ... import *`. */
|
||||
private predicate import_star_points_to(ImportStarRefinement def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
|
||||
exists(ModuleObject mod, string name |
|
||||
Flow::module_and_name_for_import_star(mod, name, def, context) |
|
||||
/* Attribute from imported module */
|
||||
module_exports(mod, name) and
|
||||
Layer::module_attribute_points_to(mod, name, value, cls, origin)
|
||||
)
|
||||
or
|
||||
exists(EssaVariable var |
|
||||
/* Retain value held before import */
|
||||
Flow::variable_not_redefined_by_import_star(var, context, def) and
|
||||
ssa_variable_points_to(var, context, value, cls, origin)
|
||||
private predicate import_star_points_to(ImportStarRefinement def, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin) {
|
||||
exists(CfgOrigin orig |
|
||||
origin = orig.fix(def.getDefiningNode())
|
||||
|
|
||||
exists(ModuleObject mod, string name |
|
||||
Flow::module_and_name_for_import_star(mod, name, def, context) |
|
||||
/* Attribute from imported module */
|
||||
module_exports(mod, name) and
|
||||
Layer::module_attribute_points_to(mod, name, value, cls, orig)
|
||||
)
|
||||
or
|
||||
exists(EssaVariable var |
|
||||
/* Retain value held before import */
|
||||
Flow::variable_not_redefined_by_import_star(var, context, def) and
|
||||
ssa_variable_points_to(var, context, value, cls, orig)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Attribute assignments have no effect as far as value tracking is concerned, except for `__class__`. */
|
||||
pragma [noinline]
|
||||
private predicate attribute_assignment_points_to(AttributeAssignment def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
|
||||
private predicate attribute_assignment_points_to(AttributeAssignment def, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin) {
|
||||
if def.getName() = "__class__" then
|
||||
ssa_variable_points_to(def.getInput(), context, value, _, _) and points_to(def.getValue(), _, cls, _,_) and
|
||||
origin = def.getDefiningNode()
|
||||
origin = CfgOrigin::fromCfgNode(def.getDefiningNode())
|
||||
else
|
||||
ssa_variable_points_to(def.getInput(), context, value, cls, origin)
|
||||
}
|
||||
|
||||
/** Ignore the effects of calls on their arguments. PointsTo is an approximation, but attempting to improve accuracy would be very expensive for very little gain. */
|
||||
pragma [noinline]
|
||||
private predicate argument_points_to(ArgumentRefinement def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
|
||||
private predicate argument_points_to(ArgumentRefinement def, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin) {
|
||||
ssa_variable_points_to(def.getInput(), context, value, cls, origin)
|
||||
}
|
||||
|
||||
/** Attribute deletions have no effect as far as value tracking is concerned. */
|
||||
pragma [noinline]
|
||||
private predicate attribute_delete_points_to(EssaAttributeDeletion def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
|
||||
private predicate attribute_delete_points_to(EssaAttributeDeletion def, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin) {
|
||||
ssa_variable_points_to(def.getInput(), context, value, cls, origin)
|
||||
}
|
||||
|
||||
/* Data flow for attributes. These mirror the "normal" points-to predicates.
|
||||
* For each points-to predicate `xxx_points_to(XXX def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin)`
|
||||
* For each points-to predicate `xxx_points_to(XXX def, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin)`
|
||||
* There is an equivalent predicate that tracks the values in attributes:
|
||||
* `xxx_named_attribute_points_to(XXX def, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin)`
|
||||
* */
|
||||
@@ -1797,15 +1924,18 @@ module PointsTo {
|
||||
*
|
||||
* Hold if the attribute `name` of the ssa variable `var` refers to `(value, cls, origin)`.
|
||||
*/
|
||||
predicate ssa_variable_named_attribute_points_to(EssaVariable var, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) {
|
||||
predicate ssa_variable_named_attribute_points_to(EssaVariable var, PointsToContext context, string name, Object value, ClassObject cls, CfgOrigin origin) {
|
||||
ssa_definition_named_attribute_points_to(var.getDefinition(), context, name, value, cls, origin)
|
||||
}
|
||||
|
||||
/** Helper for `ssa_variable_named_attribute_points_to`. */
|
||||
private predicate ssa_definition_named_attribute_points_to(EssaDefinition def, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) {
|
||||
private predicate ssa_definition_named_attribute_points_to(EssaDefinition def, PointsToContext context, string name, Object value, ClassObject cls, CfgOrigin origin) {
|
||||
ssa_phi_named_attribute_points_to(def, context, name, value, cls, origin)
|
||||
or
|
||||
ssa_node_definition_named_attribute_points_to(def, context, name, value, cls, origin)
|
||||
exists(ControlFlowNode orig |
|
||||
ssa_node_definition_named_attribute_points_to(def, context, name, value, cls, orig) and
|
||||
origin = CfgOrigin::fromCfgNode(orig)
|
||||
)
|
||||
or
|
||||
ssa_node_refinement_named_attribute_points_to(def, context, name, value, cls, origin)
|
||||
or
|
||||
@@ -1814,7 +1944,7 @@ module PointsTo {
|
||||
|
||||
/** Holds if the attribute `name` of the ssa phi-function definition `phi` refers to `(value, cls, origin)`. */
|
||||
pragma[noinline]
|
||||
private predicate ssa_phi_named_attribute_points_to(PhiFunction phi, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) {
|
||||
private predicate ssa_phi_named_attribute_points_to(PhiFunction phi, PointsToContext context, string name, Object value, ClassObject cls, CfgOrigin origin) {
|
||||
ssa_variable_named_attribute_points_to(phi.getAnInput(), context, name, value, cls, origin)
|
||||
}
|
||||
|
||||
@@ -1832,7 +1962,7 @@ module PointsTo {
|
||||
|
||||
/** Helper for `ssa_definition_named_attribute_points_to`. */
|
||||
pragma[noinline]
|
||||
private predicate ssa_node_refinement_named_attribute_points_to(EssaNodeRefinement def, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) {
|
||||
private predicate ssa_node_refinement_named_attribute_points_to(EssaNodeRefinement def, PointsToContext context, string name, Object value, ClassObject cls, CfgOrigin origin) {
|
||||
attribute_assignment_named_attribute_points_to(def, context, name, value, cls, origin)
|
||||
or
|
||||
attribute_delete_named_attribute_points_to(def, context, name, value, cls, origin)
|
||||
@@ -1846,13 +1976,14 @@ module PointsTo {
|
||||
|
||||
pragma[noinline]
|
||||
private predicate scope_entry_named_attribute_points_to(ScopeEntryDefinition def, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) {
|
||||
exists(EssaVariable var, PointsToContext outer |
|
||||
exists(EssaVariable var, PointsToContext outer, CfgOrigin orig |
|
||||
Flow::scope_entry_value_transfer(var, outer, def, context) and
|
||||
ssa_variable_named_attribute_points_to(var, outer, name, value, cls, origin)
|
||||
ssa_variable_named_attribute_points_to(var, outer, name, value, cls, orig) and
|
||||
origin = orig.asCfgNodeOrHere(def.getDefiningNode())
|
||||
)
|
||||
or
|
||||
origin = def.getDefiningNode() and
|
||||
def.getSourceVariable().getName() = "*" and
|
||||
isModuleStateVariable(def.getVariable()) and
|
||||
context.isImport() and
|
||||
exists(PackageObject package |
|
||||
package.getInitModule().getModule() = def.getScope() |
|
||||
@@ -1864,12 +1995,15 @@ module PointsTo {
|
||||
|
||||
pragma[noinline]
|
||||
private predicate assignment_named_attribute_points_to(AssignmentDefinition def, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) {
|
||||
named_attribute_points_to(def.getValue(), context, name, value, cls, origin)
|
||||
exists(CfgOrigin orig |
|
||||
named_attribute_points_to(def.getValue(), context, name, value, cls, orig) and
|
||||
origin = orig.asCfgNodeOrHere(def.getDefiningNode())
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate attribute_assignment_named_attribute_points_to(AttributeAssignment def, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) {
|
||||
points_to(def.getValue(), context, value, cls, origin) and name = def.getName()
|
||||
private predicate attribute_assignment_named_attribute_points_to(AttributeAssignment def, PointsToContext context, string name, Object value, ClassObject cls, CfgOrigin origin) {
|
||||
points_to(def.getValue(), context, value, cls, origin.toCfgNode()) and name = def.getName()
|
||||
or
|
||||
ssa_variable_named_attribute_points_to(def.getInput(), context, name, value, cls, origin) and not name = def.getName()
|
||||
}
|
||||
@@ -1882,9 +2016,9 @@ module PointsTo {
|
||||
exists(ControlFlowNode func, Object obj |
|
||||
two_args_first_arg_string(def, func, name) and
|
||||
points_to(func, _, obj, _, _) |
|
||||
obj = builtin_object("setattr") and result = true
|
||||
obj = Object::builtin("setattr") and result = true
|
||||
or
|
||||
obj != builtin_object("setattr") and result = false
|
||||
obj != Object::builtin("setattr") and result = false
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1898,10 +2032,10 @@ module PointsTo {
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate argument_named_attribute_points_to(ArgumentRefinement def, PointsToContext context, string name, Object value, ClassObject cls, ObjectOrCfg origin) {
|
||||
private predicate argument_named_attribute_points_to(ArgumentRefinement def, PointsToContext context, string name, Object value, ClassObject cls, CfgOrigin origin) {
|
||||
not two_args_first_arg_string(def, _, name) and ssa_variable_named_attribute_points_to(def.getInput(), context, name, value, cls, origin)
|
||||
or
|
||||
sets_attribute(def, name) = true and points_to(def.getDefiningNode().(CallNode).getArg(2), context, value, cls, origin)
|
||||
sets_attribute(def, name) = true and points_to(def.getDefiningNode().(CallNode).getArg(2), context, value, cls, origin.toCfgNode())
|
||||
or
|
||||
sets_attribute(def, name) = false and ssa_variable_named_attribute_points_to(def.getInput(), context, name, value, cls, origin)
|
||||
}
|
||||
@@ -1919,7 +2053,7 @@ module PointsTo {
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate self_callsite_named_attribute_points_to(SelfCallsiteRefinement def, PointsToContext context, string name, Object value, ClassObject cls, ObjectOrCfg origin) {
|
||||
private predicate self_callsite_named_attribute_points_to(SelfCallsiteRefinement def, PointsToContext context, string name, Object value, ClassObject cls, CfgOrigin origin) {
|
||||
exists(EssaVariable var, PointsToContext callee |
|
||||
ssa_variable_named_attribute_points_to(var, callee, name, value, cls, origin) and
|
||||
callee_self_variable(var, callee, def, context)
|
||||
@@ -1938,17 +2072,28 @@ module PointsTo {
|
||||
)
|
||||
}
|
||||
|
||||
/** Maps the caller object/context to callee parameter/context for self in calls to methods */
|
||||
private predicate self_in_method_call(ControlFlowNode obj, PointsToContext caller, ParameterDefinition self, PointsToContext callee) {
|
||||
self.isSelf() and
|
||||
exists(FunctionObject meth, CallNode call |
|
||||
meth.getFunction() = self.getScope() and
|
||||
callee.fromCall(call, meth, caller) and
|
||||
call.getFunction().(AttrNode).getObject() = obj
|
||||
)
|
||||
}
|
||||
|
||||
pragma [noinline]
|
||||
private predicate self_parameter_named_attribute_points_to(ParameterDefinition def, PointsToContext context, string name, Object value, ClassObject vcls, ControlFlowNode origin) {
|
||||
context.isRuntime() and executes_in_runtime_context(def.getScope()) and
|
||||
ssa_variable_named_attribute_points_to(preceding_self_variable(def), context, name, value, vcls, origin)
|
||||
or
|
||||
exists(FunctionObject meth, CallNode call, PointsToContext caller_context, ControlFlowNode obj |
|
||||
meth.getFunction() = def.getScope() and
|
||||
method_call(meth, caller_context, call) and
|
||||
call.getFunction().(AttrNode).getObject() = obj and
|
||||
context.fromCall(call, meth, caller_context) and
|
||||
named_attribute_points_to(obj, caller_context, name, value, vcls, origin)
|
||||
exists(CfgOrigin orig |
|
||||
origin = orig.toCfgNode()
|
||||
|
|
||||
context.isRuntime() and executes_in_runtime_context(def.getScope()) and
|
||||
ssa_variable_named_attribute_points_to(preceding_self_variable(def), context, name, value, vcls, orig)
|
||||
or
|
||||
exists(PointsToContext caller_context, ControlFlowNode obj |
|
||||
self_in_method_call(obj, caller_context, def, context) and
|
||||
named_attribute_points_to(obj, caller_context, name, value, vcls, orig)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1956,14 +2101,14 @@ module PointsTo {
|
||||
none()
|
||||
}
|
||||
|
||||
private predicate attribute_delete_named_attribute_points_to(EssaAttributeDeletion def, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) {
|
||||
private predicate attribute_delete_named_attribute_points_to(EssaAttributeDeletion def, PointsToContext context, string name, Object value, ClassObject cls, CfgOrigin origin) {
|
||||
none()
|
||||
}
|
||||
|
||||
/* Helper for import_star_named_attribute_points_to */
|
||||
pragma [noinline]
|
||||
private predicate star_variable_import_star_module(ImportStarRefinement def, ImportStarNode imp, PointsToContext context, ModuleObject mod) {
|
||||
def.getSourceVariable().getName() = "*" and
|
||||
isModuleStateVariable(def.getVariable()) and
|
||||
exists(ControlFlowNode fmod |
|
||||
fmod = imp.getModule() and
|
||||
imp = def.getDefiningNode() and
|
||||
@@ -1973,7 +2118,7 @@ module PointsTo {
|
||||
|
||||
/* Helper for import_star_named_attribute_points_to */
|
||||
pragma [noinline, nomagic]
|
||||
private predicate ssa_star_variable_input_points_to(ImportStarRefinement def, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) {
|
||||
private predicate ssa_star_variable_input_points_to(ImportStarRefinement def, PointsToContext context, string name, Object value, ClassObject cls, CfgOrigin origin) {
|
||||
exists(EssaVariable var |
|
||||
ssa_star_import_star_input(def, var) and
|
||||
ssa_variable_named_attribute_points_to(var, context, name, value, cls, origin)
|
||||
@@ -1983,24 +2128,22 @@ module PointsTo {
|
||||
/* Helper for ssa_star_variable_input_points_to */
|
||||
pragma [noinline]
|
||||
private predicate ssa_star_import_star_input(ImportStarRefinement def, EssaVariable var) {
|
||||
def.getSourceVariable().getName() = "*" and var = def.getInput()
|
||||
isModuleStateVariable(def.getVariable()) and var = def.getInput()
|
||||
}
|
||||
|
||||
pragma [noinline]
|
||||
private predicate import_star_named_attribute_points_to(ImportStarRefinement def, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) {
|
||||
exists(ImportStarNode imp, ModuleObject mod |
|
||||
private predicate import_star_named_attribute_points_to(ImportStarRefinement def, PointsToContext context, string name, Object value, ClassObject cls, CfgOrigin origin) {
|
||||
exists(ImportStarNode imp, ModuleObject mod, CfgOrigin orig |
|
||||
origin = orig.fix(imp) and
|
||||
star_variable_import_star_module(def, imp, context, mod) |
|
||||
/* Attribute from imported module */
|
||||
module_exports_boolean(mod, name) = true and
|
||||
exists(ObjectOrCfg obj |
|
||||
Layer::module_attribute_points_to(mod, name, value, cls, obj) and
|
||||
not exists(Variable v | v.getId() = name and v.getScope() = imp.getScope()) and
|
||||
origin = origin_from_object_or_here(obj, imp)
|
||||
)
|
||||
Layer::module_attribute_points_to(mod, name, value, cls, orig) and
|
||||
not exists(Variable v | v.getId() = name and v.getScope() = imp.getScope())
|
||||
or
|
||||
/* Retain value held before import */
|
||||
module_exports_boolean(mod, name) = false and
|
||||
ssa_star_variable_input_points_to(def, context, name, value, cls, origin)
|
||||
ssa_star_variable_input_points_to(def, context, name, value, cls, orig)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2215,7 +2358,7 @@ module PointsTo {
|
||||
(
|
||||
exists(CallNode call |
|
||||
call = expr and
|
||||
points_to(call.getFunction(), context, theLenFunction(), _, _) and
|
||||
points_to(call.getFunction(), context, Object::builtin("len"), _, _) and
|
||||
use = call.getArg(0) and
|
||||
val.(SequenceObject).getLength() = result
|
||||
)
|
||||
@@ -2231,9 +2374,9 @@ module PointsTo {
|
||||
}
|
||||
|
||||
/** Holds if ESSA edge refinement, `def`, refers to `(value, cls, origin)`. */
|
||||
predicate ssa_filter_definition_points_to(PyEdgeRefinement def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
|
||||
predicate ssa_filter_definition_points_to(PyEdgeRefinement def, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin) {
|
||||
exists(ControlFlowNode test, ControlFlowNode use |
|
||||
refinement_test(test, use, test_evaluates_boolean(test, use, context, value, cls, origin), def)
|
||||
refinement_test(test, use, test_evaluates_boolean(test, use, context, value, cls, origin.toCfgNode()), def)
|
||||
)
|
||||
or
|
||||
/* If we can't understand the test, assume that value passes through.
|
||||
@@ -2248,23 +2391,23 @@ module PointsTo {
|
||||
|
||||
/** Holds if ESSA definition, `uniphi`, refers to `(value, cls, origin)`. */
|
||||
pragma [noinline]
|
||||
predicate uni_edged_phi_points_to(SingleSuccessorGuard uniphi, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
|
||||
predicate uni_edged_phi_points_to(SingleSuccessorGuard uniphi, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin) {
|
||||
exists(ControlFlowNode test, ControlFlowNode use |
|
||||
/* Because calls such as `len` may create a new variable, we need to go via the source variable
|
||||
* That is perfectly safe as we are only dealing with calls that do not mutate their arguments.
|
||||
*/
|
||||
use = uniphi.getInput().getSourceVariable().(Variable).getAUse() and
|
||||
test = uniphi.getDefiningNode() and
|
||||
uniphi.getSense() = test_evaluates_boolean(test, use, context, value, cls, origin)
|
||||
uniphi.getSense() = test_evaluates_boolean(test, use, context, value, cls, origin.toCfgNode())
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the named attibute of ESSA edge refinement, `def`, refers to `(value, cls, origin)`. */
|
||||
pragma[noinline]
|
||||
predicate ssa_filter_definition_named_attribute_points_to(PyEdgeRefinement def, PointsToContext context, string name, Object value, ClassObject cls, ObjectOrCfg origin) {
|
||||
predicate ssa_filter_definition_named_attribute_points_to(PyEdgeRefinement def, PointsToContext context, string name, Object value, ClassObject cls, CfgOrigin origin) {
|
||||
exists(ControlFlowNode test, AttrNode use, boolean sense |
|
||||
edge_refinement_attr_use_sense(def, test, use, name, sense) and
|
||||
sense = test_evaluates_boolean(test, use, context, value, cls, origin)
|
||||
sense = test_evaluates_boolean(test, use, context, value, cls, origin.toCfgNode())
|
||||
)
|
||||
or
|
||||
exists(EssaVariable input |
|
||||
@@ -2493,7 +2636,7 @@ module PointsTo {
|
||||
}
|
||||
|
||||
/** INTERNAL -- Use `ClassObject.declaredAttribute(name). instead. */
|
||||
cached predicate class_declared_attribute(ClassObject owner, string name, Object value, ClassObject vcls, ObjectOrCfg origin) {
|
||||
cached predicate class_declared_attribute(ClassObject owner, string name, Object value, ClassObject vcls, CfgOrigin origin) {
|
||||
/* Note that src_var must be a local variable, we aren't interested in the value that any global variable may hold */
|
||||
value != undefinedVariable() and
|
||||
exists(EssaVariable var, LocalVariable src_var |
|
||||
@@ -2504,7 +2647,7 @@ module PointsTo {
|
||||
)
|
||||
or
|
||||
value = builtin_class_attribute(owner, name) and class_declares_attribute(owner, name) and
|
||||
origin = value and vcls = builtin_object_type(value)
|
||||
origin = CfgOrigin::unknown() and vcls = builtin_object_type(value)
|
||||
}
|
||||
|
||||
private predicate interesting_class_attribute(ClassList mro, string name) {
|
||||
@@ -2555,7 +2698,7 @@ module PointsTo {
|
||||
|
||||
/** INTERNAL -- Use `ClassObject.attributeRefersTo(name, value, vlcs, origin). instead.
|
||||
*/
|
||||
cached predicate class_attribute_lookup(ClassObject cls, string name, Object value, ClassObject vcls, ObjectOrCfg origin) {
|
||||
cached predicate class_attribute_lookup(ClassObject cls, string name, Object value, ClassObject vcls, CfgOrigin origin) {
|
||||
exists(ClassObject defn |
|
||||
defn = get_mro(cls).findDeclaringClass(name) and
|
||||
class_declared_attribute(defn, name, value, vcls, origin)
|
||||
@@ -2777,6 +2920,15 @@ module PointsTo {
|
||||
|
||||
}
|
||||
|
||||
/** Get the ESSA pseudo-variable used to retain module state
|
||||
* during module initialization. Module attributes are handled
|
||||
* as attributes of this variable, allowing the SSA form to track
|
||||
* mutations of the module during its creation.
|
||||
*/
|
||||
private predicate isModuleStateVariable(EssaVariable var) {
|
||||
var.getName() = "$" and var.getScope() instanceof Module
|
||||
}
|
||||
|
||||
/** INTERNAL -- Public for testing only */
|
||||
module Test {
|
||||
|
||||
@@ -2844,19 +2996,21 @@ class SuperBoundMethod extends Object {
|
||||
|
||||
SuperCall superObject;
|
||||
string name;
|
||||
ClassObject startType;
|
||||
|
||||
cached
|
||||
SuperBoundMethod() {
|
||||
exists(ControlFlowNode object |
|
||||
this.(AttrNode).getObject(name) = object |
|
||||
PointsTo::points_to(object, _, superObject, _, _)
|
||||
PointsTo::points_to(object, _, superObject, _, _) and
|
||||
startType = superObject.startType()
|
||||
)
|
||||
}
|
||||
|
||||
FunctionObject getFunction(PointsToContext ctx) {
|
||||
exists(ClassList mro |
|
||||
mro = PointsTo::Types::get_mro(superObject.selfType(ctx)) |
|
||||
result = mro.startingAt(superObject.startType()).getTail().lookup(name)
|
||||
result = mro.startingAt(startType).getTail().lookup(name)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ predicate used_as_regex(Expr s, string mode) {
|
||||
/* Call to re.xxx(regex, ... [mode]) */
|
||||
exists(CallNode call, string name |
|
||||
call.getArg(0).refersTo(_, _, s.getAFlowNode()) and
|
||||
call.getFunction().refersTo(re.getAttribute(name)) |
|
||||
call.getFunction().refersTo(re.attr(name)) |
|
||||
mode = "None"
|
||||
or
|
||||
exists(Object obj |
|
||||
@@ -40,7 +40,7 @@ string mode_from_mode_object(Object obj) {
|
||||
result = "MULTILINE" or result = "DOTALL" or result = "UNICODE" or
|
||||
result = "VERBOSE"
|
||||
) and
|
||||
ModuleObject::named("sre_constants").getAttribute("SRE_FLAG_" + result) = obj
|
||||
obj = ModuleObject::named("sre_constants").attr("SRE_FLAG_" + result)
|
||||
or
|
||||
exists(BinaryExpr be, Object sub | obj.getOrigin() = be |
|
||||
be.getOp() instanceof BitOr and
|
||||
|
||||
@@ -95,7 +95,7 @@ module Cryptography {
|
||||
|
||||
class CipherClass extends ClassObject {
|
||||
CipherClass() {
|
||||
ciphers().getAttribute("Cipher") = this
|
||||
ciphers().attr("Cipher") = this
|
||||
}
|
||||
|
||||
}
|
||||
@@ -103,7 +103,7 @@ module Cryptography {
|
||||
class AlgorithmClass extends ClassObject {
|
||||
|
||||
AlgorithmClass() {
|
||||
ciphers().submodule("algorithms").getAttribute(_) = this
|
||||
ciphers().submodule("algorithms").attr(_) = this
|
||||
}
|
||||
|
||||
string getAlgorithmName() {
|
||||
|
||||
@@ -12,7 +12,7 @@ private ModuleObject theTracebackModule() {
|
||||
}
|
||||
|
||||
private FunctionObject traceback_function(string name) {
|
||||
result = theTracebackModule().getAttribute(name)
|
||||
result = theTracebackModule().attr(name)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -167,6 +167,7 @@ abstract class CollectionKind extends TaintKind {
|
||||
/* Prevent any collection kinds more than 2 deep */
|
||||
not this.charAt(2) = "[" and not this.charAt(2) = "{"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A taint kind representing a flat collections of kinds.
|
||||
@@ -193,7 +194,7 @@ class SequenceKind extends CollectionKind {
|
||||
tonode.(BinaryExprNode).getAnOperand() = fromnode
|
||||
)
|
||||
or
|
||||
result = this and copy_call(fromnode, tonode)
|
||||
result = this and TaintFlowImplementation::copyCall(fromnode, tonode)
|
||||
or
|
||||
exists(BinaryExprNode mod |
|
||||
mod = tonode and
|
||||
@@ -236,20 +237,6 @@ private predicate slice(ControlFlowNode fromnode, SubscriptNode tonode) {
|
||||
)
|
||||
}
|
||||
|
||||
/* A call that returns a copy (or similar) of the argument */
|
||||
private predicate copy_call(ControlFlowNode fromnode, CallNode tonode) {
|
||||
tonode.getFunction().(AttrNode).getObject("copy") = fromnode
|
||||
or
|
||||
exists(ModuleObject copy, string name |
|
||||
name = "copy" or name = "deepcopy" |
|
||||
copy.getAttribute(name).(FunctionObject).getACall() = tonode and
|
||||
tonode.getArg(0) = fromnode
|
||||
)
|
||||
or
|
||||
tonode.getFunction().refersTo(builtin_object("reversed")) and
|
||||
tonode.getArg(0) = fromnode
|
||||
}
|
||||
|
||||
/** A taint kind representing a mapping of objects to kinds.
|
||||
* Typically a dict, but can include other mappings.
|
||||
*/
|
||||
@@ -272,7 +259,7 @@ class DictKind extends CollectionKind {
|
||||
result = valueKind and
|
||||
tonode.(CallNode).getFunction().(AttrNode).getObject("get") = fromnode
|
||||
or
|
||||
result = this and copy_call(fromnode, tonode)
|
||||
result = this and TaintFlowImplementation::copyCall(fromnode, tonode)
|
||||
or
|
||||
result = this and
|
||||
tonode.(CallNode).getFunction().refersTo(theDictType()) and
|
||||
@@ -937,7 +924,7 @@ library module TaintFlowImplementation {
|
||||
pragma [noinline]
|
||||
predicate getattr_step(TaintedNode fromnode, TrackedValue totaint, CallContext tocontext, CallNode tonode) {
|
||||
exists(ControlFlowNode arg, string name |
|
||||
tonode.getFunction().refersTo(builtin_object("getattr")) and
|
||||
tonode.getFunction().refersTo(Object::builtin("getattr")) and
|
||||
arg = tonode.getArg(0) and
|
||||
name = tonode.getArg(1).getNode().(StrConst).getText() and
|
||||
arg = fromnode.getNode() and
|
||||
@@ -1263,6 +1250,20 @@ library module TaintFlowImplementation {
|
||||
context = fromnode.getContext()
|
||||
}
|
||||
|
||||
/* A call that returns a copy (or similar) of the argument */
|
||||
predicate copyCall(ControlFlowNode fromnode, CallNode tonode) {
|
||||
tonode.getFunction().(AttrNode).getObject("copy") = fromnode
|
||||
or
|
||||
exists(ModuleObject copy, string name |
|
||||
name = "copy" or name = "deepcopy" |
|
||||
copy.attr(name).(FunctionObject).getACall() = tonode and
|
||||
tonode.getArg(0) = fromnode
|
||||
)
|
||||
or
|
||||
tonode.getFunction().refersTo(Object::builtin("reversed")) and
|
||||
tonode.getArg(0) = fromnode
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Helper predicate for tainted_with */
|
||||
|
||||
@@ -11,10 +11,6 @@ import semmle.python.security.TaintTracking
|
||||
import semmle.python.security.strings.Untrusted
|
||||
|
||||
|
||||
private ModuleObject subprocessModule() {
|
||||
result.getName() = "subprocess"
|
||||
}
|
||||
|
||||
private ModuleObject osOrPopenModule() {
|
||||
result.getName() = "os" or
|
||||
result.getName() = "popen2"
|
||||
@@ -22,7 +18,7 @@ private ModuleObject osOrPopenModule() {
|
||||
|
||||
private Object makeOsCall() {
|
||||
exists(string name |
|
||||
result = subprocessModule().getAttribute(name) |
|
||||
result = ModuleObject::named("subprocess").attr(name) |
|
||||
name = "Popen" or
|
||||
name = "call" or
|
||||
name = "check_call" or
|
||||
@@ -79,7 +75,7 @@ class ShellCommand extends TaintSink {
|
||||
or
|
||||
exists(CallNode call, string name |
|
||||
call.getAnArg() = this and
|
||||
call.getFunction().refersTo(osOrPopenModule().getAttribute(name)) |
|
||||
call.getFunction().refersTo(osOrPopenModule().attr(name)) |
|
||||
name = "system" or
|
||||
name = "popen" or
|
||||
name.matches("popen_")
|
||||
|
||||
@@ -12,9 +12,9 @@ import semmle.python.security.strings.Untrusted
|
||||
|
||||
|
||||
private FunctionObject exec_or_eval() {
|
||||
result = builtin_object("exec")
|
||||
result = Object::builtin("exec")
|
||||
or
|
||||
result = builtin_object("eval")
|
||||
result = Object::builtin("eval")
|
||||
}
|
||||
|
||||
/** A taint sink that represents an argument to exec or eval that is vulnerable to malicious input.
|
||||
|
||||
@@ -12,7 +12,7 @@ import semmle.python.security.strings.Untrusted
|
||||
|
||||
|
||||
private FunctionObject marshalLoads() {
|
||||
result = any(ModuleObject marshal | marshal.getName() = "marshal").getAttribute("loads")
|
||||
result = ModuleObject::named("marshal").attr("loads")
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -20,12 +20,12 @@ private class PathSanitizer extends Sanitizer {
|
||||
}
|
||||
|
||||
private FunctionObject abspath() {
|
||||
exists(ModuleObject os, ModuleObject os_path |
|
||||
os.getName() = "os" and
|
||||
os.getAttribute("path") = os_path |
|
||||
os_path.getAttribute("abspath") = result
|
||||
exists(ModuleObject os_path |
|
||||
ModuleObject::named("os").attr("path") = os_path
|
||||
|
|
||||
os_path.attr("abspath") = result
|
||||
or
|
||||
os_path.getAttribute("normpath") = result
|
||||
os_path.attr("normpath") = result
|
||||
)
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ class OpenNode extends TaintSink {
|
||||
|
||||
OpenNode() {
|
||||
exists(CallNode call |
|
||||
call.getFunction().refersTo(builtin_object("open")) and
|
||||
call.getFunction().refersTo(Object::builtin("open")) and
|
||||
call.getAnArg() = this
|
||||
)
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ private ModuleObject pickleModule() {
|
||||
}
|
||||
|
||||
private FunctionObject pickleLoads() {
|
||||
result = pickleModule().getAttribute("loads")
|
||||
result = pickleModule().attr("loads")
|
||||
}
|
||||
|
||||
/** `pickle.loads(untrusted)` vulnerability. */
|
||||
|
||||
@@ -33,7 +33,7 @@ private class ExpatParser extends TaintKind {
|
||||
}
|
||||
|
||||
private FunctionObject expatCreateParseFunction() {
|
||||
result = ModuleObject::named("xml.parsers.expat").getAttribute("ParserCreate")
|
||||
result = ModuleObject::named("xml.parsers.expat").attr("ParserCreate")
|
||||
}
|
||||
|
||||
private class ExpatCreateParser extends TaintSource {
|
||||
@@ -52,13 +52,13 @@ private class ExpatCreateParser extends TaintSource {
|
||||
}
|
||||
|
||||
private FunctionObject xmlFromString() {
|
||||
result = xmlElementTreeModule().getAttribute("fromstring")
|
||||
result = xmlElementTreeModule().attr("fromstring")
|
||||
or
|
||||
result = xmlMiniDomModule().getAttribute("parseString")
|
||||
result = xmlMiniDomModule().attr("parseString")
|
||||
or
|
||||
result = xmlPullDomModule().getAttribute("parseString")
|
||||
result = xmlPullDomModule().attr("parseString")
|
||||
or
|
||||
result = xmlSaxModule().getAttribute("parseString")
|
||||
result = xmlSaxModule().attr("parseString")
|
||||
}
|
||||
|
||||
/** A (potentially) malicious XML string. */
|
||||
|
||||
@@ -12,13 +12,8 @@ import semmle.python.security.TaintTracking
|
||||
import semmle.python.security.strings.Untrusted
|
||||
|
||||
|
||||
private ModuleObject yamlModule() {
|
||||
result.getName() = "yaml"
|
||||
}
|
||||
|
||||
|
||||
private FunctionObject yamlLoad() {
|
||||
result = yamlModule().getAttribute("load")
|
||||
result = ModuleObject::named("yaml").attr("load")
|
||||
}
|
||||
|
||||
/** `yaml.load(untrusted)` vulnerability. */
|
||||
|
||||
@@ -110,9 +110,19 @@ private predicate slice(ControlFlowNode fromnode, SubscriptNode tonode) {
|
||||
/* tonode = os.path.join(..., fromnode, ...) */
|
||||
private predicate os_path_join(ControlFlowNode fromnode, CallNode tonode) {
|
||||
exists(FunctionObject path_join |
|
||||
exists(ModuleObject os | os.getName() = "os" |
|
||||
os.getAttribute("path").(ModuleObject).getAttribute("join") = path_join
|
||||
) |
|
||||
path_join = ModuleObject::named("os").attr("path").(ModuleObject).attr("join")
|
||||
and
|
||||
tonode = path_join.getACall() and tonode.getAnArg() = fromnode
|
||||
)
|
||||
}
|
||||
|
||||
/** A kind of "taint", representing a dictionary mapping str->"taint" */
|
||||
class StringDictKind extends DictKind {
|
||||
|
||||
StringDictKind() {
|
||||
this.getValue() instanceof StringKind
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@ predicate copy_call(ControlFlowNode fromnode, CallNode tonode) {
|
||||
or
|
||||
exists(ModuleObject copy, string name |
|
||||
name = "copy" or name = "deepcopy" |
|
||||
copy.getAttribute(name).(FunctionObject).getACall() = tonode and
|
||||
copy.attr(name).(FunctionObject).getACall() = tonode and
|
||||
tonode.getArg(0) = fromnode
|
||||
)
|
||||
or
|
||||
tonode.getFunction().refersTo(builtin_object("reversed")) and
|
||||
tonode.getFunction().refersTo(Object::builtin("reversed")) and
|
||||
tonode.getArg(0) = fromnode
|
||||
}
|
||||
|
||||
@@ -91,8 +91,28 @@ private predicate json_subscript_taint(SubscriptNode sub, ControlFlowNode obj, E
|
||||
|
||||
private predicate json_load(ControlFlowNode fromnode, CallNode tonode) {
|
||||
exists(FunctionObject json_loads |
|
||||
any(ModuleObject json | json.getName() = "json").getAttribute("loads") = json_loads and
|
||||
any(ModuleObject json | json.getName() = "json").attr("loads") = json_loads and
|
||||
json_loads.getACall() = tonode and tonode.getArg(0) = fromnode
|
||||
)
|
||||
}
|
||||
|
||||
/** A kind of "taint", representing an open file-like object from an external source. */
|
||||
class ExternalFileObject extends TaintKind {
|
||||
|
||||
ExternalFileObject() {
|
||||
this = "file[" + any(ExternalStringKind key) + "]"
|
||||
}
|
||||
|
||||
|
||||
/** Gets the taint kind for the contents of this file */
|
||||
TaintKind getValue() {
|
||||
this = "file[" + result + "]"
|
||||
}
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
name = "read" and result = this.getValue()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -10,19 +10,6 @@ predicate is_c_metaclass(Object o) {
|
||||
}
|
||||
|
||||
|
||||
library class ObjectOrCfg extends @py_object {
|
||||
|
||||
string toString() {
|
||||
/* Not to be displayed */
|
||||
none()
|
||||
}
|
||||
|
||||
ControlFlowNode getOrigin() {
|
||||
result = this
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A class whose instances represents Python classes.
|
||||
* Instances of this class represent either builtin classes
|
||||
* such as `list` or `str`, or program-defined Python classes
|
||||
@@ -147,13 +134,19 @@ class ClassObject extends Object {
|
||||
|
||||
/** Whether the named attribute refers to the object and origin */
|
||||
predicate attributeRefersTo(string name, Object obj, ControlFlowNode origin) {
|
||||
PointsTo::Types::class_attribute_lookup(this, name, obj, _, origin)
|
||||
exists(CfgOrigin orig |
|
||||
origin = orig.toCfgNode() and
|
||||
PointsTo::Types::class_attribute_lookup(this, name, obj, _, orig)
|
||||
)
|
||||
}
|
||||
|
||||
/** Whether the named attribute refers to the object, class and origin */
|
||||
predicate attributeRefersTo(string name, Object obj, ClassObject cls, ControlFlowNode origin) {
|
||||
not obj = unknownValue() and
|
||||
PointsTo::Types::class_attribute_lookup(this, name, obj, cls, origin)
|
||||
exists(CfgOrigin orig |
|
||||
origin = orig.toCfgNode() and
|
||||
PointsTo::Types::class_attribute_lookup(this, name, obj, cls, orig)
|
||||
)
|
||||
}
|
||||
|
||||
/** Whether this class has a attribute named `name`, either declared or inherited.*/
|
||||
@@ -502,9 +495,9 @@ ClassObject theUnicodeType() {
|
||||
|
||||
/** The builtin class '(x)range' */
|
||||
ClassObject theRangeType() {
|
||||
result = builtin_object("xrange")
|
||||
result = Object::builtin("xrange")
|
||||
or
|
||||
major_version() = 3 and result = builtin_object("range")
|
||||
major_version() = 3 and result = Object::builtin("range")
|
||||
}
|
||||
|
||||
/** The builtin class for bytes. str in Python2, bytes in Python3 */
|
||||
@@ -597,20 +590,20 @@ ClassObject theBuiltinPropertyType() {
|
||||
|
||||
/** The builtin class 'IOError' */
|
||||
ClassObject theIOErrorType() {
|
||||
result = builtin_object("IOError")
|
||||
result = Object::builtin("IOError")
|
||||
}
|
||||
|
||||
/** The builtin class 'super' */
|
||||
ClassObject theSuperType() {
|
||||
result = builtin_object("super")
|
||||
result = Object::builtin("super")
|
||||
}
|
||||
|
||||
/** The builtin class 'StopIteration' */
|
||||
ClassObject theStopIterationType() {
|
||||
result = builtin_object("StopIteration")
|
||||
result = Object::builtin("StopIteration")
|
||||
}
|
||||
|
||||
/** The builtin class 'NotImplementedError' */
|
||||
ClassObject theNotImplementedErrorType() {
|
||||
result = builtin_object("NotImplementedError")
|
||||
result = Object::builtin("NotImplementedError")
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ class RaisingNode extends ControlFlowNode {
|
||||
}
|
||||
|
||||
private predicate quits() {
|
||||
this.(CallNode).getFunction().refersTo(quitterObject(_))
|
||||
this.(CallNode).getFunction().refersTo(Object::quitter(_))
|
||||
}
|
||||
|
||||
/** Gets the type of an exception that may be raised
|
||||
@@ -49,7 +49,7 @@ class RaisingNode extends ControlFlowNode {
|
||||
|
||||
pragma[noinline]
|
||||
private ClassObject systemExitRaise() {
|
||||
this.quits() and result = builtin_object("SystemExit")
|
||||
this.quits() and result = Object::builtin("SystemExit")
|
||||
}
|
||||
|
||||
pragma [noinline, nomagic]
|
||||
@@ -62,7 +62,7 @@ class RaisingNode extends ControlFlowNode {
|
||||
(ex.refersTo(result) or ex.refersTo(_, result, _))
|
||||
)
|
||||
or
|
||||
this.getNode() instanceof ImportExpr and result = builtin_object("ImportError")
|
||||
this.getNode() instanceof ImportExpr and result = Object::builtin("ImportError")
|
||||
or
|
||||
this.getNode() instanceof Print and result = theIOErrorType()
|
||||
or
|
||||
|
||||
@@ -160,10 +160,7 @@ class PyFunctionObject extends FunctionObject {
|
||||
|
||||
/** Gets a control flow node corresponding to the value of a return statement */
|
||||
ControlFlowNode getAReturnedNode() {
|
||||
exists(Return ret |
|
||||
ret.getScope() = this.getFunction() and
|
||||
result.getNode() = ret.getValue()
|
||||
)
|
||||
result = this.getFunction().getAReturnValueFlowNode()
|
||||
}
|
||||
|
||||
override string descriptiveString() {
|
||||
@@ -348,21 +345,21 @@ class BuiltinFunctionObject extends BuiltinCallable {
|
||||
override ClassObject getAReturnType() {
|
||||
/* Enumerate the types of a few builtin functions, that the CPython analysis misses.
|
||||
*/
|
||||
this = builtin_object("hex") and result = theStrType()
|
||||
this = Object::builtin("hex") and result = theStrType()
|
||||
or
|
||||
this = builtin_object("oct") and result = theStrType()
|
||||
this = Object::builtin("oct") and result = theStrType()
|
||||
or
|
||||
this = builtin_object("intern") and result = theStrType()
|
||||
this = Object::builtin("intern") and result = theStrType()
|
||||
or
|
||||
/* Fix a few minor inaccuracies in the CPython analysis */
|
||||
ext_rettype(this, result) and not (
|
||||
this = builtin_object("__import__") and result = theNoneType()
|
||||
this = Object::builtin("__import__") and result = theNoneType()
|
||||
or
|
||||
this = builtin_object("compile") and result = theNoneType()
|
||||
this = Object::builtin("compile") and result = theNoneType()
|
||||
or
|
||||
this = builtin_object("sum")
|
||||
this = Object::builtin("sum")
|
||||
or
|
||||
this = builtin_object("filter")
|
||||
this = Object::builtin("filter")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -376,4 +373,53 @@ class BuiltinFunctionObject extends BuiltinCallable {
|
||||
|
||||
}
|
||||
|
||||
/** DEPRECATED -- Use `Object::builtin("apply")` instead. */
|
||||
Object theApplyFunction() {
|
||||
result = Object::builtin("apply")
|
||||
}
|
||||
|
||||
/** DEPRECATED -- Use `Object::builtin("hasattr")` instead. */
|
||||
Object theHasattrFunction() {
|
||||
result = Object::builtin("hasattr")
|
||||
}
|
||||
|
||||
/** DEPRECATED -- Use `Object::builtin("len")` instead. */
|
||||
Object theLenFunction() {
|
||||
result = Object::builtin("len")
|
||||
}
|
||||
|
||||
/** DEPRECATED -- Use `Object::builtin("format")` instead. */
|
||||
Object theFormatFunction() {
|
||||
result = Object::builtin("format")
|
||||
}
|
||||
|
||||
/** DEPRECATED -- Use `Object::builtin("open")` instead. */
|
||||
Object theOpenFunction() {
|
||||
result = Object::builtin("open")
|
||||
}
|
||||
|
||||
/** DEPRECATED -- Use `Object::builtin("print")` instead. */
|
||||
Object thePrintFunction() {
|
||||
result = Object::builtin("print")
|
||||
}
|
||||
|
||||
/** DEPRECATED -- Use `Object::builtin("input")` instead. */
|
||||
Object theInputFunction() {
|
||||
result = Object::builtin("input")
|
||||
}
|
||||
|
||||
/** DEPRECATED -- Use `Object::builtin("locals")` instead. */
|
||||
Object theLocalsFunction() {
|
||||
result = Object::builtin("locals")
|
||||
}
|
||||
|
||||
/** DEPRECATED -- Use `Object::builtin("globals")()` instead. */
|
||||
Object theGlobalsFunction() {
|
||||
result = Object::builtin("globals")
|
||||
}
|
||||
|
||||
/** DEPRECATED -- Use `Object::builtin("sysExit()` instead. */
|
||||
Object theExitFunctionObject() {
|
||||
result = ModuleObject::named("sys").attr("exit")
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,11 @@ abstract class ModuleObject extends Object {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Gets the source scope corresponding to this module, if this is a Python module */
|
||||
Module getSourceModule() {
|
||||
none()
|
||||
}
|
||||
|
||||
Container getPath() {
|
||||
none()
|
||||
}
|
||||
@@ -28,10 +33,18 @@ abstract class ModuleObject extends Object {
|
||||
}
|
||||
|
||||
/** Gets the named attribute of this module. Using attributeRefersTo() instead
|
||||
* may provide better results for presentation. */
|
||||
* may provide better results for presentation.
|
||||
* */
|
||||
pragma [noinline]
|
||||
abstract Object getAttribute(string name);
|
||||
|
||||
/** Gets the named attribute of this module.
|
||||
* Synonym for `getAttribute(name)` */
|
||||
pragma [inline]
|
||||
final Object attr(string name) {
|
||||
result = this.getAttribute(name)
|
||||
}
|
||||
|
||||
/** Whether the named attribute of this module "refers-to" value, with a known origin.
|
||||
*/
|
||||
abstract predicate attributeRefersTo(string name, Object value, ControlFlowNode origin);
|
||||
@@ -136,6 +149,10 @@ class PythonModuleObject extends ModuleObject {
|
||||
result = this.getOrigin()
|
||||
}
|
||||
|
||||
override Module getSourceModule() {
|
||||
result = this.getOrigin()
|
||||
}
|
||||
|
||||
override Container getPath() {
|
||||
result = this.getModule().getFile()
|
||||
}
|
||||
@@ -165,11 +182,17 @@ class PythonModuleObject extends ModuleObject {
|
||||
}
|
||||
|
||||
override predicate attributeRefersTo(string name, Object value, ControlFlowNode origin) {
|
||||
PointsTo::py_module_attributes(this.getModule(), name, value, _, origin)
|
||||
exists(CfgOrigin orig |
|
||||
origin = orig.toCfgNode() and
|
||||
PointsTo::py_module_attributes(this.getModule(), name, value, _, orig)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate attributeRefersTo(string name, Object value, ClassObject cls, ControlFlowNode origin) {
|
||||
PointsTo::py_module_attributes(this.getModule(), name, value, cls, origin)
|
||||
exists(CfgOrigin orig |
|
||||
origin = orig.toCfgNode() and
|
||||
PointsTo::py_module_attributes(this.getModule(), name, value, cls, orig)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -206,6 +229,10 @@ class PackageObject extends ModuleObject {
|
||||
result = this.getOrigin()
|
||||
}
|
||||
|
||||
override Module getSourceModule() {
|
||||
result = this.getModule().getInitModule()
|
||||
}
|
||||
|
||||
override Container getPath() {
|
||||
exists(ModuleObject m |
|
||||
m.getPackage() = this |
|
||||
@@ -247,11 +274,17 @@ class PackageObject extends ModuleObject {
|
||||
}
|
||||
|
||||
override predicate attributeRefersTo(string name, Object value, ControlFlowNode origin) {
|
||||
PointsTo::package_attribute_points_to(this, name, value, _, origin)
|
||||
exists(CfgOrigin orig |
|
||||
origin = orig.toCfgNode() and
|
||||
PointsTo::package_attribute_points_to(this, name, value, _, orig)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate attributeRefersTo(string name, Object value, ClassObject cls, ControlFlowNode origin) {
|
||||
PointsTo::package_attribute_points_to(this, name, value, cls, origin)
|
||||
exists(CfgOrigin orig |
|
||||
origin = orig.toCfgNode() and
|
||||
PointsTo::package_attribute_points_to(this, name, value, cls, orig)
|
||||
)
|
||||
}
|
||||
|
||||
Location getLocation() {
|
||||
|
||||
@@ -192,7 +192,7 @@ private Object findByName1(string longName) {
|
||||
exists(string owner, string attrname |
|
||||
longName = owner + "." + attrname
|
||||
|
|
||||
result = findByName0(owner).(ModuleObject).getAttribute(attrname)
|
||||
result = findByName0(owner).(ModuleObject).attr(attrname)
|
||||
or
|
||||
result = findByName0(owner).(ClassObject).lookupAttribute(attrname)
|
||||
)
|
||||
@@ -204,7 +204,7 @@ private Object findByName2(string longName) {
|
||||
exists(string owner, string attrname |
|
||||
longName = owner + "." + attrname
|
||||
|
|
||||
result = findByName1(owner).(ModuleObject).getAttribute(attrname)
|
||||
result = findByName1(owner).(ModuleObject).attr(attrname)
|
||||
or
|
||||
result = findByName1(owner).(ClassObject).lookupAttribute(attrname)
|
||||
)
|
||||
@@ -216,7 +216,7 @@ private Object findByName3(string longName) {
|
||||
exists(string owner, string attrname |
|
||||
longName = owner + "." + attrname
|
||||
|
|
||||
result = findByName2(owner).(ModuleObject).getAttribute(attrname)
|
||||
result = findByName2(owner).(ModuleObject).attr(attrname)
|
||||
or
|
||||
result = findByName2(owner).(ClassObject).lookupAttribute(attrname)
|
||||
)
|
||||
@@ -354,6 +354,14 @@ class TupleObject extends SequenceObject {
|
||||
|
||||
}
|
||||
|
||||
module TupleObject {
|
||||
|
||||
TupleObject empty() {
|
||||
py_cobjecttypes(result, theTupleType()) and not py_citems(result, _, _)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class NonEmptyTupleObject extends TupleObject {
|
||||
|
||||
NonEmptyTupleObject() {
|
||||
@@ -389,108 +397,82 @@ BuiltinModuleObject theSysModuleObject() {
|
||||
py_special_objects(result, "sys")
|
||||
}
|
||||
|
||||
/** DEPRECATED -- Use `Object::builtin(name)` instead. */
|
||||
deprecated
|
||||
Object builtin_object(string name) {
|
||||
py_cmembers_versioned(theBuiltinModuleObject(), name, result, major_version().toString())
|
||||
result = Object::builtin(name)
|
||||
}
|
||||
|
||||
/** The built-in object None */
|
||||
Object theNoneObject() {
|
||||
Object theNoneObject() {
|
||||
py_special_objects(result, "None")
|
||||
}
|
||||
|
||||
/** The built-in object True */
|
||||
Object theTrueObject() {
|
||||
Object theTrueObject() {
|
||||
py_special_objects(result, "True")
|
||||
}
|
||||
|
||||
/** The built-in object False */
|
||||
Object theFalseObject() {
|
||||
Object theFalseObject() {
|
||||
py_special_objects(result, "False")
|
||||
}
|
||||
|
||||
/** The builtin function apply (Python 2 only) */
|
||||
Object theApplyFunction() {
|
||||
result = builtin_object("apply")
|
||||
}
|
||||
|
||||
/** The builtin function hasattr */
|
||||
Object theHasattrFunction() {
|
||||
result = builtin_object("hasattr")
|
||||
}
|
||||
|
||||
/** The builtin function len */
|
||||
Object theLenFunction() {
|
||||
result = builtin_object("len")
|
||||
}
|
||||
|
||||
/** The builtin function format */
|
||||
Object theFormatFunction() {
|
||||
result = builtin_object("format")
|
||||
}
|
||||
|
||||
/** The builtin function open */
|
||||
Object theOpenFunction() {
|
||||
result = builtin_object("open")
|
||||
}
|
||||
|
||||
/** The builtin function print (Python 2.7 upwards) */
|
||||
Object thePrintFunction() {
|
||||
result = builtin_object("print")
|
||||
}
|
||||
|
||||
/** The builtin function input (Python 2 only) */
|
||||
Object theInputFunction() {
|
||||
result = builtin_object("input")
|
||||
}
|
||||
|
||||
/** The builtin function locals */
|
||||
Object theLocalsFunction() {
|
||||
py_special_objects(result, "locals")
|
||||
}
|
||||
|
||||
/** The builtin function globals */
|
||||
Object theGlobalsFunction() {
|
||||
py_special_objects(result, "globals")
|
||||
}
|
||||
|
||||
/** The builtin function sys.exit */
|
||||
Object theExitFunctionObject() {
|
||||
py_cmembers_versioned(theSysModuleObject(), "exit", result, major_version().toString())
|
||||
}
|
||||
|
||||
/** The NameError class */
|
||||
Object theNameErrorType() {
|
||||
result = builtin_object("NameError")
|
||||
result = Object::builtin("NameError")
|
||||
}
|
||||
|
||||
/** The StandardError class */
|
||||
Object theStandardErrorType() {
|
||||
result = builtin_object("StandardError")
|
||||
result = Object::builtin("StandardError")
|
||||
}
|
||||
|
||||
/** The IndexError class */
|
||||
Object theIndexErrorType() {
|
||||
result = builtin_object("IndexError")
|
||||
result = Object::builtin("IndexError")
|
||||
}
|
||||
|
||||
/** The LookupError class */
|
||||
Object theLookupErrorType() {
|
||||
result = builtin_object("LookupError")
|
||||
result = Object::builtin("LookupError")
|
||||
}
|
||||
|
||||
/** The named quitter object (quit or exit) in the builtin namespace */
|
||||
/** DEPRECATED -- Use `Object::quitter(name)` instead. */
|
||||
deprecated
|
||||
Object quitterObject(string name) {
|
||||
(name = "quit" or name = "exit") and
|
||||
result = builtin_object(name)
|
||||
result = Object::quitter(name)
|
||||
}
|
||||
|
||||
/** The builtin object `NotImplemented`. Not be confused with `NotImplementedError`. */
|
||||
/** DEPRECATED -- Use `Object::notImplemented()` instead. */
|
||||
deprecated
|
||||
Object theNotImplementedObject() {
|
||||
result = builtin_object("NotImplemented")
|
||||
result = Object::builtin("NotImplemented")
|
||||
}
|
||||
|
||||
/** DEPRECATED -- Use `TupleObject::empty()` instead. */
|
||||
deprecated
|
||||
Object theEmptyTupleObject() {
|
||||
py_cobjecttypes(result, theTupleType()) and not py_citems(result, _, _)
|
||||
result = TupleObject::empty()
|
||||
}
|
||||
|
||||
module Object {
|
||||
|
||||
Object builtin(string name) {
|
||||
py_cmembers_versioned(theBuiltinModuleObject(), name, result, major_version().toString())
|
||||
}
|
||||
|
||||
/** The named quitter object (quit or exit) in the builtin namespace */
|
||||
Object quitter(string name) {
|
||||
(name = "quit" or name = "exit") and
|
||||
result = builtin(name)
|
||||
}
|
||||
|
||||
/** The builtin object `NotImplemented`. Not be confused with `NotImplementedError`. */
|
||||
Object notImplemented() {
|
||||
result = builtin("NotImplemented")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ predicate tracked_object(ControlFlowNode obj, string attr) {
|
||||
}
|
||||
|
||||
predicate open_file(Object obj) {
|
||||
obj.(CallNode).getFunction().refersTo(theOpenFunction())
|
||||
obj.(CallNode).getFunction().refersTo(Object::builtin("open"))
|
||||
}
|
||||
|
||||
predicate string_attribute_any(ControlFlowNode n, string attr) {
|
||||
@@ -26,9 +26,9 @@ predicate string_attribute_any(ControlFlowNode n, string attr) {
|
||||
exists(Object input |
|
||||
n.(CallNode).getFunction().refersTo(input) |
|
||||
if major_version() = 2 then
|
||||
input = builtin_object("raw_input")
|
||||
input = Object::builtin("raw_input")
|
||||
else
|
||||
input = theInputFunction()
|
||||
input = Object::builtin("input")
|
||||
)
|
||||
or
|
||||
attr = "file-input" and
|
||||
|
||||
@@ -23,3 +23,69 @@ string httpVerb() {
|
||||
string httpVerbLower() {
|
||||
result = httpVerb().toLowerCase()
|
||||
}
|
||||
|
||||
/** Taint kind representing the WSGI environment.
|
||||
* As specified in PEP 3333. https://www.python.org/dev/peps/pep-3333/#environ-variables
|
||||
*/
|
||||
class WsgiEnvironment extends TaintKind {
|
||||
|
||||
WsgiEnvironment() { this = "wsgi.environment" }
|
||||
|
||||
override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
|
||||
result = this and TaintFlowImplementation::copyCall(fromnode, tonode)
|
||||
or
|
||||
result = this and
|
||||
tonode.(CallNode).getFunction().refersTo(theDictType()) and
|
||||
tonode.(CallNode).getArg(0) = fromnode
|
||||
or
|
||||
exists(StringObject key, string text |
|
||||
tonode.(CallNode).getFunction().(AttrNode).getObject("get") = fromnode and
|
||||
tonode.(CallNode).getArg(0).refersTo(key)
|
||||
or
|
||||
tonode.(SubscriptNode).getValue() = fromnode and tonode.isLoad() and
|
||||
tonode.(SubscriptNode).getIndex().refersTo(key)
|
||||
|
|
||||
text = key.getText() and result instanceof ExternalStringKind and
|
||||
(
|
||||
text = "QUERY_STRING" or
|
||||
text = "PATH_INFO" or
|
||||
text.prefix(5) = "HTTP_"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A standard morsel object from a HTTP request, a value in a cookie,
|
||||
* typically an instance of `http.cookies.Morsel` */
|
||||
class UntrustedMorsel extends TaintKind {
|
||||
|
||||
UntrustedMorsel() {
|
||||
this = "http.Morsel"
|
||||
}
|
||||
|
||||
|
||||
override TaintKind getTaintOfAttribute(string name) {
|
||||
result instanceof ExternalStringKind and
|
||||
(
|
||||
name = "value"
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A standard cookie object from a HTTP request, typically an instance of `http.cookies.SimpleCookie` */
|
||||
class UntrustedCookie extends TaintKind {
|
||||
|
||||
UntrustedCookie() {
|
||||
this = "http.Cookie"
|
||||
}
|
||||
|
||||
override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
|
||||
tonode.(SubscriptNode).getValue() = fromnode and
|
||||
result instanceof UntrustedMorsel
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -6,3 +6,4 @@ import semmle.python.web.django.Redirect
|
||||
import semmle.python.web.flask.Redirect
|
||||
import semmle.python.web.tornado.Redirect
|
||||
import semmle.python.web.pyramid.Redirect
|
||||
import semmle.python.web.bottle.Redirect
|
||||
|
||||
@@ -3,3 +3,7 @@ import semmle.python.web.flask.Request
|
||||
import semmle.python.web.tornado.Request
|
||||
import semmle.python.web.pyramid.Request
|
||||
import semmle.python.web.twisted.Request
|
||||
import semmle.python.web.bottle.Request
|
||||
import semmle.python.web.turbogears.Request
|
||||
import semmle.python.web.falcon.Request
|
||||
import semmle.python.web.cherrypy.Request
|
||||
|
||||
@@ -3,3 +3,7 @@ import semmle.python.web.flask.Response
|
||||
import semmle.python.web.pyramid.Response
|
||||
import semmle.python.web.tornado.Response
|
||||
import semmle.python.web.twisted.Response
|
||||
import semmle.python.web.bottle.Response
|
||||
import semmle.python.web.turbogears.Response
|
||||
import semmle.python.web.falcon.Response
|
||||
import semmle.python.web.cherrypy.Response
|
||||
|
||||
79
python/ql/src/semmle/python/web/bottle/General.qll
Normal file
79
python/ql/src/semmle/python/web/bottle/General.qll
Normal file
@@ -0,0 +1,79 @@
|
||||
import python
|
||||
import semmle.python.web.Http
|
||||
import semmle.python.types.Extensions
|
||||
|
||||
/** The bottle module */
|
||||
ModuleObject theBottleModule() {
|
||||
result = ModuleObject::named("bottle")
|
||||
}
|
||||
|
||||
/** The bottle.Bottle class */
|
||||
ClassObject theBottleClass() {
|
||||
result = ModuleObject::named("bottle").attr("Bottle")
|
||||
}
|
||||
|
||||
/** Holds if `route` is routed to `func`
|
||||
* by decorating `func` with `app.route(route)` or `route(route)`
|
||||
*/
|
||||
predicate bottle_route(CallNode route_call, ControlFlowNode route, Function func) {
|
||||
exists(CallNode decorator_call, string name |
|
||||
route_call.getFunction().(AttrNode).getObject(name).refersTo(_, theBottleClass(), _) or
|
||||
route_call.getFunction().refersTo(theBottleModule().attr(name))
|
||||
|
|
||||
(name = "route" or name = httpVerbLower()) and
|
||||
decorator_call.getFunction() = route_call and
|
||||
route_call.getArg(0) = route and
|
||||
decorator_call.getArg(0).getNode().(FunctionExpr).getInnerScope() = func
|
||||
)
|
||||
}
|
||||
|
||||
class BottleRoute extends ControlFlowNode {
|
||||
|
||||
BottleRoute() {
|
||||
bottle_route(this, _, _)
|
||||
}
|
||||
|
||||
string getUrl() {
|
||||
exists(StrConst url |
|
||||
bottle_route(this, url.getAFlowNode(), _) and
|
||||
result = url.getText()
|
||||
)
|
||||
}
|
||||
|
||||
Function getFunction() {
|
||||
bottle_route(this, _, result)
|
||||
}
|
||||
|
||||
Parameter getNamedArgument() {
|
||||
exists(string name, Function func |
|
||||
func = this.getFunction() and
|
||||
func.getArgByName(name) = result and
|
||||
this.getUrl().matches("%<" + name + ">%")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* bottle module route constants */
|
||||
|
||||
class BottleRoutePointToExtension extends CustomPointsToFact {
|
||||
|
||||
string name;
|
||||
|
||||
BottleRoutePointToExtension() {
|
||||
exists(DefinitionNode defn |
|
||||
defn.getScope().(Module).getName() = "bottle" and
|
||||
this = defn.getValue() and
|
||||
name = defn.(NameNode).getId()
|
||||
|
|
||||
name = "route" or
|
||||
name = httpVerbLower()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate pointsTo(Context context, Object value, ClassObject cls, ControlFlowNode origin) {
|
||||
context.isImport() and
|
||||
ModuleObject::named("bottle").attr("Bottle").(ClassObject).attributeRefersTo(name, value, cls, origin)
|
||||
}
|
||||
}
|
||||
|
||||
35
python/ql/src/semmle/python/web/bottle/Redirect.qll
Normal file
35
python/ql/src/semmle/python/web/bottle/Redirect.qll
Normal file
@@ -0,0 +1,35 @@
|
||||
/** Provides class representing the `bottle.redirect` function.
|
||||
* This module is intended to be imported into a taint-tracking query
|
||||
* to extend `TaintSink`.
|
||||
*/
|
||||
import python
|
||||
|
||||
import semmle.python.security.TaintTracking
|
||||
import semmle.python.security.strings.Basic
|
||||
import semmle.python.web.bottle.General
|
||||
|
||||
FunctionObject bottle_redirect() {
|
||||
result = theBottleModule().attr("redirect")
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an argument to the `bottle.redirect` function.
|
||||
*/
|
||||
class BottleRedirect extends TaintSink {
|
||||
|
||||
override string toString() {
|
||||
result = "bottle.redirect"
|
||||
}
|
||||
|
||||
BottleRedirect() {
|
||||
exists(CallNode call |
|
||||
bottle_redirect().getACall() = call and
|
||||
this = call.getAnArg()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) {
|
||||
kind instanceof StringKind
|
||||
}
|
||||
|
||||
}
|
||||
115
python/ql/src/semmle/python/web/bottle/Request.qll
Normal file
115
python/ql/src/semmle/python/web/bottle/Request.qll
Normal file
@@ -0,0 +1,115 @@
|
||||
import python
|
||||
|
||||
|
||||
import semmle.python.security.TaintTracking
|
||||
import semmle.python.security.strings.Untrusted
|
||||
import semmle.python.web.Http
|
||||
import semmle.python.web.bottle.General
|
||||
|
||||
private Object theBottleRequestObject() {
|
||||
result = theBottleModule().attr("request")
|
||||
}
|
||||
|
||||
class BottleRequestKind extends TaintKind {
|
||||
|
||||
BottleRequestKind() {
|
||||
this = "bottle.request"
|
||||
}
|
||||
|
||||
override TaintKind getTaintOfAttribute(string name) {
|
||||
result instanceof BottleFormsDict and
|
||||
(name = "cookies" or name = "query" or name = "form")
|
||||
or
|
||||
result instanceof UntrustedStringKind and
|
||||
(name = "query_string" or name = "url_args")
|
||||
or
|
||||
result.(DictKind).getValue() instanceof FileUpload and
|
||||
name = "files"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class RequestSource extends TaintSource {
|
||||
|
||||
RequestSource() {
|
||||
this.(ControlFlowNode).refersTo(theBottleRequestObject())
|
||||
}
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) {
|
||||
kind instanceof BottleRequestKind
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class BottleFormsDict extends TaintKind {
|
||||
|
||||
BottleFormsDict() {
|
||||
this = "bottle.FormsDict"
|
||||
}
|
||||
|
||||
override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
|
||||
/* Cannot use `getTaintOfAttribute(name)` as it wouldn't bind `name` */
|
||||
exists(string name |
|
||||
fromnode = tonode.(AttrNode).getObject(name) and
|
||||
result instanceof UntrustedStringKind
|
||||
|
|
||||
name != "get" and name != "getunicode" and name != "getall"
|
||||
)
|
||||
}
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
(name = "get" or name = "getunicode") and
|
||||
result instanceof UntrustedStringKind
|
||||
or
|
||||
name = "getall" and result.(SequenceKind).getItem() instanceof UntrustedStringKind
|
||||
}
|
||||
}
|
||||
|
||||
class FileUpload extends TaintKind {
|
||||
|
||||
FileUpload() {
|
||||
this = "bottle.FileUpload"
|
||||
}
|
||||
|
||||
override TaintKind getTaintOfAttribute(string name) {
|
||||
name = "filename" and result instanceof UntrustedStringKind
|
||||
or
|
||||
name = "raw_filename" and result instanceof UntrustedStringKind
|
||||
or
|
||||
name = "file" and result instanceof UntrustedFile
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class UntrustedFile extends TaintKind {
|
||||
|
||||
UntrustedFile() { this = "Untrusted file" }
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
// TO DO.. File uploads -- Should check about file uploads for other frameworks as well.
|
||||
// Move UntrustedFile to shared location
|
||||
//
|
||||
|
||||
|
||||
/** Parameter to a bottle request handler function */
|
||||
class BottleRequestParameter extends TaintSource {
|
||||
|
||||
BottleRequestParameter() {
|
||||
exists(BottleRoute route |
|
||||
route.getNamedArgument() = this.(ControlFlowNode).getNode()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) {
|
||||
kind instanceof UntrustedStringKind
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "bottle handler function argument"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
58
python/ql/src/semmle/python/web/bottle/Response.qll
Normal file
58
python/ql/src/semmle/python/web/bottle/Response.qll
Normal file
@@ -0,0 +1,58 @@
|
||||
import python
|
||||
|
||||
import semmle.python.security.TaintTracking
|
||||
import semmle.python.security.strings.Untrusted
|
||||
import semmle.python.web.Http
|
||||
import semmle.python.web.bottle.General
|
||||
|
||||
|
||||
/** A bottle.Response object
|
||||
* This isn't really a "taint", but we use the value tracking machinery to
|
||||
* track the flow of response objects.
|
||||
*/
|
||||
class BottleResponse extends TaintKind {
|
||||
|
||||
BottleResponse() {
|
||||
this = "bottle.response"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Object theBottleResponseObject() {
|
||||
result = theBottleModule().attr("response")
|
||||
}
|
||||
|
||||
class BottleResponseBodyAssignment extends TaintSink {
|
||||
|
||||
BottleResponseBodyAssignment() {
|
||||
exists(DefinitionNode lhs |
|
||||
lhs.getValue() = this and
|
||||
lhs.(AttrNode).getObject("body").refersTo(theBottleResponseObject())
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) {
|
||||
kind instanceof StringKind
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class BottleHandlerFunctionResult extends TaintSink {
|
||||
|
||||
BottleHandlerFunctionResult() {
|
||||
exists(BottleRoute route, Return ret |
|
||||
ret.getScope() = route.getFunction() and
|
||||
ret.getValue().getAFlowNode() = this
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) {
|
||||
kind instanceof StringKind
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "bottle handler function result"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
56
python/ql/src/semmle/python/web/cherrypy/General.qll
Normal file
56
python/ql/src/semmle/python/web/cherrypy/General.qll
Normal file
@@ -0,0 +1,56 @@
|
||||
import python
|
||||
import semmle.python.web.Http
|
||||
|
||||
module CherryPy {
|
||||
|
||||
FunctionObject expose() {
|
||||
result = ModuleObject::named("cherrypy").attr("expose")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class CherryPyExposedFunction extends Function {
|
||||
|
||||
CherryPyExposedFunction() {
|
||||
this.getADecorator().refersTo(CherryPy::expose())
|
||||
or
|
||||
this.getADecorator().(Call).getFunc().refersTo(CherryPy::expose())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class CherryPyRoute extends CallNode {
|
||||
|
||||
CherryPyRoute() {
|
||||
/* cherrypy.quickstart(root, script_name, config) */
|
||||
ModuleObject::named("cherrypy").attr("quickstart").(FunctionObject).getACall() = this
|
||||
or
|
||||
/* cherrypy.tree.mount(root, script_name, config) */
|
||||
this.getFunction().(AttrNode).getObject("mount").refersTo(ModuleObject::named("cherrypy").attr("tree"))
|
||||
}
|
||||
|
||||
ClassObject getAppClass() {
|
||||
this.getArg(0).refersTo(_, result, _)
|
||||
or
|
||||
this.getArgByName("root").refersTo(_, result, _)
|
||||
}
|
||||
|
||||
string getPath() {
|
||||
exists(StringObject path |
|
||||
result = path.getText()
|
||||
|
|
||||
this.getArg(1).refersTo(path)
|
||||
or
|
||||
this.getArgByName("script_name").refersTo(path)
|
||||
)
|
||||
}
|
||||
|
||||
Object getConfig() {
|
||||
this.getArg(2).refersTo(_, result, _)
|
||||
or
|
||||
this.getArgByName("config").refersTo(_, result, _)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
69
python/ql/src/semmle/python/web/cherrypy/Request.qll
Normal file
69
python/ql/src/semmle/python/web/cherrypy/Request.qll
Normal file
@@ -0,0 +1,69 @@
|
||||
import python
|
||||
|
||||
import semmle.python.security.TaintTracking
|
||||
import semmle.python.security.strings.Basic
|
||||
import semmle.python.web.Http
|
||||
import semmle.python.web.cherrypy.General
|
||||
|
||||
/** The cherrypy.request local-proxy object */
|
||||
class CherryPyRequest extends TaintKind {
|
||||
|
||||
CherryPyRequest() {
|
||||
this = "cherrypy.request"
|
||||
}
|
||||
|
||||
override TaintKind getTaintOfAttribute(string name) {
|
||||
name = "params" and result instanceof ExternalStringDictKind
|
||||
or
|
||||
name = "cookie" and result instanceof UntrustedCookie
|
||||
}
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
(
|
||||
name = "getHeader" or
|
||||
name = "getCookie" or
|
||||
name = "getUser" or
|
||||
name = "getPassword"
|
||||
) and
|
||||
result instanceof ExternalStringKind
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class CherryPyExposedFunctionParameter extends TaintSource {
|
||||
|
||||
CherryPyExposedFunctionParameter() {
|
||||
exists(Parameter p |
|
||||
p = any(CherryPyExposedFunction f).getAnArg() and
|
||||
not p.isSelf() and
|
||||
p.asName().getAFlowNode() = this
|
||||
)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "CherryPy handler function parameter"
|
||||
}
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) {
|
||||
kind instanceof ExternalStringKind
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class CherryPyRequestSource extends TaintSource {
|
||||
|
||||
CherryPyRequestSource() {
|
||||
this.(ControlFlowNode).refersTo(ModuleObject::named("cherrypy").attr("request"))
|
||||
}
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) {
|
||||
kind instanceof CherryPyRequest
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
28
python/ql/src/semmle/python/web/cherrypy/Response.qll
Normal file
28
python/ql/src/semmle/python/web/cherrypy/Response.qll
Normal file
@@ -0,0 +1,28 @@
|
||||
import python
|
||||
|
||||
import semmle.python.security.TaintTracking
|
||||
import semmle.python.security.strings.Untrusted
|
||||
import semmle.python.web.Http
|
||||
import semmle.python.web.cherrypy.General
|
||||
|
||||
|
||||
|
||||
class CherryPyExposedFunctionResult extends TaintSink {
|
||||
|
||||
CherryPyExposedFunctionResult() {
|
||||
exists(Return ret |
|
||||
ret.getScope() instanceof CherryPyExposedFunction and
|
||||
ret.getValue().getAFlowNode() = this
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) {
|
||||
kind instanceof StringKind
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "cherrypy handler function result"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ class DjangoDbCursor extends DbCursor {
|
||||
}
|
||||
|
||||
private Object theDjangoConnectionObject() {
|
||||
any(ModuleObject m | m.getName() = "django.db").getAttribute("connection") = result
|
||||
any(ModuleObject m | m.getName() = "django.db").attr("connection") = result
|
||||
}
|
||||
|
||||
/** A kind of taint source representing sources of django cursor objects.
|
||||
@@ -38,7 +38,7 @@ class DjangoDbCursorSource extends DbConnectionSource {
|
||||
|
||||
|
||||
ClassObject theDjangoRawSqlClass() {
|
||||
result = any(ModuleObject m | m.getName() = "django.db.models.expressions").getAttribute("RawSQL")
|
||||
result = any(ModuleObject m | m.getName() = "django.db.models.expressions").attr("RawSQL")
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,7 +8,7 @@ import semmle.python.web.Http
|
||||
class DjangoModel extends ClassObject {
|
||||
|
||||
DjangoModel() {
|
||||
any(ModuleObject m | m.getName() = "django.db.models").getAttribute("Model") = this.getAnImproperSuperType()
|
||||
any(ModuleObject m | m.getName() = "django.db.models").attr("Model") = this.getAnImproperSuperType()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ private class DjangoFunctionBasedViewRequestArgument extends DjangoRequestSource
|
||||
private class DjangoView extends ClassObject {
|
||||
|
||||
DjangoView() {
|
||||
any(ModuleObject m | m.getName() = "django.views.generic").getAttribute("View") = this.getAnImproperSuperType()
|
||||
any(ModuleObject m | m.getName() = "django.views.generic").attr("View") = this.getAnImproperSuperType()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ class DjangoClassBasedViewRequestArgument extends DjangoRequestSource {
|
||||
/* Function based views */
|
||||
predicate url_dispatch(CallNode call, ControlFlowNode regex, FunctionObject view) {
|
||||
exists(FunctionObject url |
|
||||
any(ModuleObject m | m.getName() = "django.conf.urls").getAttribute("url") = url and
|
||||
any(ModuleObject m | m.getName() = "django.conf.urls").attr("url") = url and
|
||||
url.getArgumentForCall(call, 0) = regex and
|
||||
url.getArgumentForCall(call, 1).refersTo(view)
|
||||
)
|
||||
|
||||
@@ -17,7 +17,7 @@ class DjangoResponse extends TaintKind {
|
||||
}
|
||||
|
||||
private ClassObject theDjangoHttpResponseClass() {
|
||||
result = any(ModuleObject m | m.getName() = "django.http.response").getAttribute("HttpResponse") and
|
||||
result = any(ModuleObject m | m.getName() = "django.http.response").attr("HttpResponse") and
|
||||
not result = theDjangoHttpRedirectClass()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import python
|
||||
|
||||
FunctionObject redirect() {
|
||||
result = any(ModuleObject m | m.getName() = "django.shortcuts").getAttribute("redirect")
|
||||
result = any(ModuleObject m | m.getName() = "django.shortcuts").attr("redirect")
|
||||
}
|
||||
|
||||
ClassObject theDjangoHttpRedirectClass() {
|
||||
result = any(ModuleObject m | m.getName() = "django.http.response").getAttribute("HttpResponseRedirectBase")
|
||||
result = any(ModuleObject m | m.getName() = "django.http.response").attr("HttpResponseRedirectBase")
|
||||
}
|
||||
|
||||
68
python/ql/src/semmle/python/web/falcon/General.qll
Normal file
68
python/ql/src/semmle/python/web/falcon/General.qll
Normal file
@@ -0,0 +1,68 @@
|
||||
import python
|
||||
import semmle.python.web.Http
|
||||
|
||||
|
||||
/** The falcon API class */
|
||||
ClassObject theFalconAPIClass() {
|
||||
result = ModuleObject::named("falcon").getAttribute("API")
|
||||
}
|
||||
|
||||
|
||||
/** Holds if `route` is routed to `resource`
|
||||
*/
|
||||
private predicate api_route(CallNode route_call, ControlFlowNode route, ClassObject resource) {
|
||||
route_call.getFunction().(AttrNode).getObject("add_route").refersTo(_, theFalconAPIClass(), _) and
|
||||
route_call.getArg(0) = route and
|
||||
route_call.getArg(1).refersTo(_, resource, _)
|
||||
}
|
||||
|
||||
private predicate route(FalconRoute route, Function target, string funcname) {
|
||||
route.getResourceClass().lookupAttribute("on_" + funcname).(FunctionObject).getFunction() = target
|
||||
}
|
||||
|
||||
class FalconRoute extends ControlFlowNode {
|
||||
|
||||
FalconRoute() {
|
||||
api_route(this, _, _)
|
||||
}
|
||||
|
||||
string getUrl() {
|
||||
exists(StrConst url |
|
||||
api_route(this, url.getAFlowNode(), _) and
|
||||
result = url.getText()
|
||||
)
|
||||
}
|
||||
|
||||
ClassObject getResourceClass() {
|
||||
api_route(this, _, result)
|
||||
}
|
||||
|
||||
FalconHandlerFunction getHandlerFunction(string method) {
|
||||
route(this, result, method)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class FalconHandlerFunction extends Function {
|
||||
|
||||
FalconHandlerFunction() {
|
||||
route(_, this, _)
|
||||
}
|
||||
|
||||
private string methodName() {
|
||||
route(_, this, result)
|
||||
}
|
||||
|
||||
string getMethod() {
|
||||
result = this.methodName().toUpperCase()
|
||||
}
|
||||
|
||||
Parameter getRequest() {
|
||||
result = this.getArg(1)
|
||||
}
|
||||
|
||||
Parameter getResponse() {
|
||||
result = this.getArg(2)
|
||||
}
|
||||
|
||||
}
|
||||
56
python/ql/src/semmle/python/web/falcon/Request.qll
Normal file
56
python/ql/src/semmle/python/web/falcon/Request.qll
Normal file
@@ -0,0 +1,56 @@
|
||||
import python
|
||||
|
||||
import semmle.python.security.TaintTracking
|
||||
import semmle.python.web.Http
|
||||
import semmle.python.web.falcon.General
|
||||
import semmle.python.security.strings.External
|
||||
|
||||
/** https://falcon.readthedocs.io/en/stable/api/request_and_response.html */
|
||||
class FalconRequest extends TaintKind {
|
||||
|
||||
FalconRequest() {
|
||||
this = "falcon.request"
|
||||
}
|
||||
|
||||
override TaintKind getTaintOfAttribute(string name) {
|
||||
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"
|
||||
)
|
||||
or
|
||||
result instanceof ExternalStringDictKind and
|
||||
(
|
||||
name = "cookies" or name = "params"
|
||||
)
|
||||
or
|
||||
name = "stream" and result instanceof ExternalFileObject
|
||||
}
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
name = "get_param" and result instanceof ExternalStringKind
|
||||
or
|
||||
name = "get_param_as_json" and result instanceof ExternalJsonKind
|
||||
or
|
||||
name = "get_param_as_list" and result instanceof ExternalStringSequenceKind
|
||||
}
|
||||
}
|
||||
|
||||
class FalconRequestParameter extends TaintSource {
|
||||
|
||||
FalconRequestParameter() {
|
||||
exists(FalconHandlerFunction f |
|
||||
f.getRequest() = this.(ControlFlowNode).getNode()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSourceOf(TaintKind k) {
|
||||
k instanceof FalconRequest
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
48
python/ql/src/semmle/python/web/falcon/Response.qll
Normal file
48
python/ql/src/semmle/python/web/falcon/Response.qll
Normal file
@@ -0,0 +1,48 @@
|
||||
import python
|
||||
|
||||
|
||||
import semmle.python.security.TaintTracking
|
||||
import semmle.python.web.Http
|
||||
import semmle.python.web.falcon.General
|
||||
import semmle.python.security.strings.External
|
||||
|
||||
|
||||
/** https://falcon.readthedocs.io/en/stable/api/request_and_response.html */
|
||||
class FalconResponse extends TaintKind {
|
||||
|
||||
FalconResponse() {
|
||||
this = "falcon.response"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class FalconResponseParameter extends TaintSource {
|
||||
|
||||
FalconResponseParameter() {
|
||||
exists(FalconHandlerFunction f |
|
||||
f.getResponse() = this.(ControlFlowNode).getNode()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSourceOf(TaintKind k) {
|
||||
k instanceof FalconResponse
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class FalconResponseBodySink extends TaintSink {
|
||||
|
||||
FalconResponseBodySink() {
|
||||
exists(AttrNode attr |
|
||||
any(FalconResponse f).taints(attr.getObject("body")) |
|
||||
attr.(DefinitionNode).getValue() = this
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) {
|
||||
kind instanceof StringKind
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -8,16 +8,16 @@ ModuleObject theFlaskModule() {
|
||||
|
||||
/** The flask app class */
|
||||
ClassObject theFlaskClass() {
|
||||
result = theFlaskModule().getAttribute("Flask")
|
||||
result = theFlaskModule().attr("Flask")
|
||||
}
|
||||
|
||||
/** The flask MethodView class */
|
||||
ClassObject theFlaskMethodViewClass() {
|
||||
result = any(ModuleObject m | m.getName() = "flask.views").getAttribute("MethodView")
|
||||
result = any(ModuleObject m | m.getName() = "flask.views").attr("MethodView")
|
||||
}
|
||||
|
||||
ClassObject theFlaskReponseClass() {
|
||||
result = theFlaskModule().getAttribute("Response")
|
||||
result = theFlaskModule().attr("Response")
|
||||
}
|
||||
|
||||
/** Holds if `route` is routed to `func`
|
||||
|
||||
@@ -9,7 +9,7 @@ import semmle.python.security.strings.Basic
|
||||
import semmle.python.web.flask.General
|
||||
|
||||
FunctionObject flask_redirect() {
|
||||
result = theFlaskModule().getAttribute("redirect")
|
||||
result = theFlaskModule().attr("redirect")
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,7 +5,7 @@ import semmle.python.web.Http
|
||||
import semmle.python.web.flask.General
|
||||
|
||||
private Object theFlaskRequestObject() {
|
||||
result = theFlaskModule().getAttribute("request")
|
||||
result = theFlaskModule().attr("request")
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -10,9 +10,9 @@ import semmle.python.security.strings.Basic
|
||||
private ClassObject redirectClass() {
|
||||
exists(ModuleObject ex |
|
||||
ex.getName() = "pyramid.httpexceptions" |
|
||||
ex.getAttribute("HTTPFound") = result
|
||||
ex.attr("HTTPFound") = result
|
||||
or
|
||||
ex.getAttribute("HTTPTemporaryRedirect") = result
|
||||
ex.attr("HTTPTemporaryRedirect") = result
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ class PyramidRequest extends BaseWebobRequest {
|
||||
}
|
||||
|
||||
override ClassObject getClass() {
|
||||
result = any(ModuleObject m | m.getName() = "pyramid.request").getAttribute("Request")
|
||||
result = any(ModuleObject m | m.getName() = "pyramid.request").attr("Request")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ ModuleObject thePyramidViewModule() {
|
||||
}
|
||||
|
||||
Object thePyramidViewConfig() {
|
||||
result = thePyramidViewModule().getAttribute("view_config")
|
||||
result = thePyramidViewModule().attr("view_config")
|
||||
}
|
||||
|
||||
predicate is_pyramid_view_function(Function func) {
|
||||
|
||||
@@ -3,7 +3,7 @@ import python
|
||||
import semmle.python.security.TaintTracking
|
||||
|
||||
private ClassObject theTornadoRequestHandlerClass() {
|
||||
result = any(ModuleObject m | m.getName() = "tornado.web").getAttribute("RequestHandler")
|
||||
result = any(ModuleObject m | m.getName() = "tornado.web").attr("RequestHandler")
|
||||
}
|
||||
|
||||
ClassObject aTornadoRequestHandlerClass() {
|
||||
|
||||
33
python/ql/src/semmle/python/web/turbogears/Request.qll
Normal file
33
python/ql/src/semmle/python/web/turbogears/Request.qll
Normal file
@@ -0,0 +1,33 @@
|
||||
import python
|
||||
import semmle.python.security.strings.Untrusted
|
||||
|
||||
import TurboGears
|
||||
|
||||
private class ValidatedMethodParameter extends Parameter {
|
||||
|
||||
ValidatedMethodParameter() {
|
||||
exists(string name, TurboGearsControllerMethod method |
|
||||
method.getArgByName(name) = this and
|
||||
method.getValidationDict().getItem(_).(KeyValuePair).getKey().(StrConst).getText() = name
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class UnvalidatedControllerMethodParameter extends TaintSource {
|
||||
|
||||
UnvalidatedControllerMethodParameter() {
|
||||
exists(Parameter p |
|
||||
any(TurboGearsControllerMethod m | not m.getName() = "onerror").getAnArg() = p and
|
||||
not p instanceof ValidatedMethodParameter and
|
||||
not p.isSelf() and
|
||||
p.(Name).getAFlowNode() = this
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) {
|
||||
kind instanceof UntrustedStringKind
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
38
python/ql/src/semmle/python/web/turbogears/Response.qll
Normal file
38
python/ql/src/semmle/python/web/turbogears/Response.qll
Normal file
@@ -0,0 +1,38 @@
|
||||
import python
|
||||
|
||||
import semmle.python.security.TaintTracking
|
||||
import semmle.python.security.strings.Basic
|
||||
|
||||
import TurboGears
|
||||
|
||||
|
||||
|
||||
class ControllerMethodReturnValue extends TaintSink {
|
||||
|
||||
ControllerMethodReturnValue() {
|
||||
exists(TurboGearsControllerMethod m |
|
||||
m.getAReturnValueFlowNode() = this and
|
||||
not m.isTemplated()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) {
|
||||
kind instanceof StringKind
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ControllerMethodTemplatedReturnValue extends TaintSink {
|
||||
|
||||
ControllerMethodTemplatedReturnValue() {
|
||||
exists(TurboGearsControllerMethod m |
|
||||
m.getAReturnValueFlowNode() = this and
|
||||
m.isTemplated()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) {
|
||||
kind instanceof StringDictKind
|
||||
}
|
||||
|
||||
}
|
||||
55
python/ql/src/semmle/python/web/turbogears/TurboGears.qll
Normal file
55
python/ql/src/semmle/python/web/turbogears/TurboGears.qll
Normal file
@@ -0,0 +1,55 @@
|
||||
import python
|
||||
|
||||
import semmle.python.security.TaintTracking
|
||||
|
||||
private ClassObject theTurboGearsControllerClass() {
|
||||
result = ModuleObject::named("tg").getAttribute("TGController")
|
||||
}
|
||||
|
||||
|
||||
ClassObject aTurboGearsControllerClass() {
|
||||
result.getASuperType() = theTurboGearsControllerClass()
|
||||
}
|
||||
|
||||
|
||||
class TurboGearsControllerMethod extends Function {
|
||||
|
||||
ControlFlowNode decorator;
|
||||
|
||||
TurboGearsControllerMethod() {
|
||||
aTurboGearsControllerClass().getPyClass() = this.getScope() and
|
||||
decorator = this.getADecorator().getAFlowNode() and
|
||||
/* Is decorated with @expose() or @expose(path) */
|
||||
(
|
||||
decorator.(CallNode).getFunction().(NameNode).getId() = "expose"
|
||||
or
|
||||
decorator.refersTo(_, ModuleObject::named("tg").getAttribute("expose"), _)
|
||||
)
|
||||
}
|
||||
|
||||
private ControlFlowNode templateName() {
|
||||
result = decorator.(CallNode).getArg(0)
|
||||
}
|
||||
|
||||
predicate isTemplated() {
|
||||
exists(templateName())
|
||||
}
|
||||
|
||||
string getTemplateName() {
|
||||
exists(StringObject str |
|
||||
templateName().refersTo(str) and
|
||||
result = str.getText()
|
||||
)
|
||||
}
|
||||
|
||||
Dict getValidationDict() {
|
||||
exists(Call call, Object dict |
|
||||
call = this.getADecorator() and
|
||||
call.getFunc().(Name).getId() = "validate" and
|
||||
call.getArg(0).refersTo(dict) and
|
||||
result = dict.getOrigin()
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,11 +3,11 @@ import python
|
||||
import semmle.python.security.TaintTracking
|
||||
|
||||
private ClassObject theTwistedHttpRequestClass() {
|
||||
result = any(ModuleObject m | m.getName() = "twisted.web.http").getAttribute("Request")
|
||||
result = any(ModuleObject m | m.getName() = "twisted.web.http").attr("Request")
|
||||
}
|
||||
|
||||
private ClassObject theTwistedHttpResourceClass() {
|
||||
result = any(ModuleObject m | m.getName() = "twisted.web.resource").getAttribute("Resource")
|
||||
result = any(ModuleObject m | m.getName() = "twisted.web.resource").attr("Resource")
|
||||
}
|
||||
|
||||
ClassObject aTwistedRequestHandlerClass() {
|
||||
|
||||
@@ -45,7 +45,7 @@ class WebobRequest extends BaseWebobRequest {
|
||||
}
|
||||
|
||||
override ClassObject getClass() {
|
||||
result = any(ModuleObject m | m.getName() = "webob.request").getAttribute("Request")
|
||||
result = any(ModuleObject m | m.getName() = "webob.request").attr("Request")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -431,6 +431,11 @@ py_extracted_version(int module : @py_Module ref,
|
||||
/* <Field> Slice.stop = 3, expr */
|
||||
/* <Field> Slice.step = 4, expr */
|
||||
|
||||
/* <Field> SpecialOperation.location = 0, location */
|
||||
/* <Field> SpecialOperation.parenthesised = 1, bool */
|
||||
/* <Field> SpecialOperation.name = 2, str */
|
||||
/* <Field> SpecialOperation.arguments = 3, expr_list */
|
||||
|
||||
/* <Field> Starred.location = 0, location */
|
||||
/* <Field> Starred.parenthesised = 1, bool */
|
||||
/* <Field> Starred.value = 2, expr */
|
||||
@@ -718,7 +723,8 @@ case @py_expr.kind of
|
||||
| 34 = @py_Await
|
||||
| 35 = @py_Fstring
|
||||
| 36 = @py_FormattedValue
|
||||
| 37 = @py_AssignExpr;
|
||||
| 37 = @py_AssignExpr
|
||||
| 38 = @py_SpecialOperation;
|
||||
|
||||
case @py_expr_context.kind of
|
||||
0 = @py_AugLoad
|
||||
@@ -789,7 +795,7 @@ case @py_unaryop.kind of
|
||||
|
||||
@py_expr_context_parent = @py_Attribute | @py_List | @py_Name | @py_PlaceHolder | @py_Starred | @py_Subscript | @py_TemplateDottedNotation | @py_Tuple;
|
||||
|
||||
@py_expr_list_parent = @py_Assign | @py_BoolExpr | @py_Call | @py_ClassExpr | @py_Compare | @py_Delete | @py_Fstring | @py_Function | @py_List | @py_Print | @py_Set | @py_Tuple | @py_arguments | @py_comprehension;
|
||||
@py_expr_list_parent = @py_Assign | @py_BoolExpr | @py_Call | @py_ClassExpr | @py_Compare | @py_Delete | @py_Fstring | @py_Function | @py_List | @py_Print | @py_Set | @py_SpecialOperation | @py_Tuple | @py_arguments | @py_comprehension;
|
||||
|
||||
@py_expr_or_stmt = @py_expr | @py_stmt;
|
||||
|
||||
@@ -805,7 +811,7 @@ case @py_unaryop.kind of
|
||||
|
||||
@py_str_list_parent = @py_Global | @py_Nonlocal;
|
||||
|
||||
@py_str_parent = @py_Attribute | @py_Class | @py_ClassExpr | @py_FormattedValue | @py_Function | @py_FunctionExpr | @py_ImportExpr | @py_ImportMember | @py_Module | @py_Str | @py_StringPart | @py_TemplateDottedNotation | @py_keyword | @py_str_list;
|
||||
@py_str_parent = @py_Attribute | @py_Class | @py_ClassExpr | @py_FormattedValue | @py_Function | @py_FunctionExpr | @py_ImportExpr | @py_ImportMember | @py_Module | @py_SpecialOperation | @py_Str | @py_StringPart | @py_TemplateDottedNotation | @py_keyword | @py_str_list;
|
||||
|
||||
@py_variable_parent = @py_Name | @py_PlaceHolder;
|
||||
|
||||
|
||||
@@ -324,6 +324,10 @@
|
||||
<v>200</v>
|
||||
</e>
|
||||
<e>
|
||||
<k>@py_SpecialOperation</k>
|
||||
<v>100</v>
|
||||
</e>
|
||||
<e>
|
||||
<k>@py_expr_context</k>
|
||||
<v>1140675</v>
|
||||
</e>
|
||||
|
||||
Reference in New Issue
Block a user