mirror of
https://github.com/github/codeql.git
synced 2026-04-30 11:15:13 +02:00
Merge branch 'master' into python-unsafe-use-of-mktemp
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() |
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
|
||||
Sanitizing untrusted URLs is an important technique for
|
||||
preventing attacks such as request forgeries and malicious
|
||||
redirections. Often, this is done by checking that the host of a URL
|
||||
is in a set of allowed hosts.
|
||||
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
||||
If a regular expression implements such a check, it is
|
||||
easy to accidentally make the check too permissive by not escaping the
|
||||
<code>.</code> meta-characters appropriately.
|
||||
|
||||
Even if the check is not used in a security-critical
|
||||
context, the incomplete check may still cause undesirable behaviors
|
||||
when it accidentally succeeds.
|
||||
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
|
||||
Escape all meta-characters appropriately when constructing
|
||||
regular expressions for security checks, pay special attention to the
|
||||
<code>.</code> meta-character.
|
||||
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
|
||||
<p>
|
||||
|
||||
The following example code checks that a URL redirection
|
||||
will reach the <code>example.com</code> domain, or one of its
|
||||
subdomains.
|
||||
|
||||
</p>
|
||||
|
||||
<sample src="examples/IncompleteHostnameRegExp.py"/>
|
||||
|
||||
<p>
|
||||
The <code>unsafe</code> check is easy to bypass because the unescaped
|
||||
<code>.</code> allows for any character before
|
||||
<code>example.com</code>, effectively allowing the redirect to go to
|
||||
an attacker-controlled domain such as <code>wwwXexample.com</code>.
|
||||
|
||||
</p>
|
||||
<p>
|
||||
The <code>safe</code> check closes this vulnerability by escaping the <code>.</code>
|
||||
so that URLs of the form <code>wwwXexample.com</code> are rejected.
|
||||
</p>
|
||||
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>OWASP: <a href="https://www.owasp.org/index.php/Server_Side_Request_Forgery">SSRF</a></li>
|
||||
<li>OWASP: <a href="https://www.owasp.org/index.php/Unvalidated_Redirects_and_Forwards_Cheat_Sheet">XSS Unvalidated Redirects and Forwards Cheat Sheet</a>.</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
44
python/ql/src/Security/CWE-020/IncompleteHostnameRegExp.ql
Normal file
44
python/ql/src/Security/CWE-020/IncompleteHostnameRegExp.ql
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* @name Incomplete regular expression for hostnames
|
||||
* @description Matching a URL or hostname against a regular expression that contains an unescaped dot as part of the hostname might match more hostnames than expected.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @precision high
|
||||
* @id py/incomplete-hostname-regexp
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-20
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.regex
|
||||
|
||||
private string commonTopLevelDomainRegex() {
|
||||
result = "com|org|edu|gov|uk|net|io"
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pattern` is a regular expression pattern for URLs with a host matched by `hostPart`,
|
||||
* and `pattern` contains a subtle mistake that allows it to match unexpected hosts.
|
||||
*/
|
||||
bindingset[pattern]
|
||||
predicate isIncompleteHostNameRegExpPattern(string pattern, string hostPart) {
|
||||
hostPart = pattern
|
||||
.regexpCapture("(?i).*" +
|
||||
// an unescaped single `.`
|
||||
"(?<!\\\\)[.]" +
|
||||
// immediately followed by a sequence of subdomains, perhaps with some regex characters mixed in, followed by a known TLD
|
||||
"([():|?a-z0-9-]+(\\\\)?[.](" + commonTopLevelDomainRegex() + "))" + ".*", 1)
|
||||
}
|
||||
|
||||
from Regex r, string pattern, string hostPart
|
||||
where
|
||||
(
|
||||
r.getText() = pattern
|
||||
) and
|
||||
isIncompleteHostNameRegExpPattern(pattern, hostPart) and
|
||||
// ignore patterns with capture groups after the TLD
|
||||
not pattern.regexpMatch("(?i).*[.](" + commonTopLevelDomainRegex() + ").*[(][?]:.*[)].*")
|
||||
select r,
|
||||
"This regular expression has an unescaped '.' before '" + hostPart +
|
||||
"', so it might match more hosts than expected."
|
||||
@@ -0,0 +1,85 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
|
||||
Sanitizing untrusted URLs is an important technique for
|
||||
preventing attacks such as request forgeries and malicious
|
||||
redirections. Usually, this is done by checking that the host of a URL
|
||||
is in a set of allowed hosts.
|
||||
|
||||
</p>
|
||||
|
||||
<p>
|
||||
However, treating the URL as a string and checking if one of the
|
||||
allowed hosts is a substring of the URL is very prone to errors.
|
||||
Malicious URLs can bypass such security checks by embedding one
|
||||
of the allowed hosts in an unexpected location.
|
||||
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
||||
Even if the substring check is not used in a
|
||||
security-critical context, the incomplete check may still cause
|
||||
undesirable behaviors when the check succeeds accidentally.
|
||||
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
|
||||
Parse a URL before performing a check on its host value,
|
||||
and ensure that the check handles arbitrary subdomain sequences
|
||||
correctly.
|
||||
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
|
||||
<p>
|
||||
|
||||
The following example code checks that a URL redirection
|
||||
will reach the <code>example.com</code> domain.
|
||||
|
||||
</p>
|
||||
|
||||
<sample src="examples/IncompleteUrlSubstringSanitization.py"/>
|
||||
|
||||
<p>
|
||||
|
||||
The first two examples show unsafe checks that are easily bypassed.
|
||||
In <code>unsafe1</code> the attacker can simply add
|
||||
<code>example.com</code> anywhere in the url. For example,
|
||||
<code>http://evil-example.net/example.com</code>.
|
||||
</p>
|
||||
<p>
|
||||
In <code>unsafe2</code> the attacker must use a hostname ending in
|
||||
<code>example.com</code>, but that is easy to do. For example,
|
||||
<code>http://benign-looking-prefix-example.com</code>.
|
||||
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
||||
The second two examples show safe checks.
|
||||
In <code>safe1</code>, a white-list is used. Although fairly inflexible,
|
||||
this is easy to get right and is most likely to be safe.
|
||||
</p>
|
||||
<p>
|
||||
In <code>safe2</code>, <code>urlparse</code> is used to parse the URL,
|
||||
then the hostname is checked to make sure it ends with <code>.example.com</code>.
|
||||
</p>
|
||||
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>OWASP: <a href="https://www.owasp.org/index.php/Server_Side_Request_Forgery">SSRF</a></li>
|
||||
<li>OWASP: <a href="https://www.owasp.org/index.php/Unvalidated_Redirects_and_Forwards_Cheat_Sheet">XSS Unvalidated Redirects and Forwards Cheat Sheet</a>.</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* @name Incomplete URL substring sanitization
|
||||
* @description Security checks on the substrings of an unparsed URL are often vulnerable to bypassing.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @precision high
|
||||
* @id py/incomplete-url-substring-sanitization
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-20
|
||||
*/
|
||||
|
||||
|
||||
import python
|
||||
import semmle.python.regex
|
||||
|
||||
private string commonTopLevelDomainRegex() {
|
||||
result = "com|org|edu|gov|uk|net|io"
|
||||
}
|
||||
|
||||
predicate looksLikeUrl(StrConst s) {
|
||||
exists(string text |
|
||||
text = s.getText()
|
||||
|
|
||||
text.regexpMatch("(?i)([a-z]*:?//)?\\.?([a-z0-9-]+\\.)+(" +
|
||||
commonTopLevelDomainRegex() +")(:[0-9]+)?/?")
|
||||
or
|
||||
// target is a HTTP URL to a domain on any TLD
|
||||
text.regexpMatch("(?i)https?://([a-z0-9-]+\\.)+([a-z]+)(:[0-9]+)?/?")
|
||||
)
|
||||
}
|
||||
|
||||
predicate incomplete_sanitization(Expr sanitizer, StrConst url) {
|
||||
looksLikeUrl(url) and
|
||||
(
|
||||
sanitizer.(Compare).compares(url, any(In i), _)
|
||||
or
|
||||
call_to_startswith(sanitizer, url)
|
||||
or
|
||||
unsafe_call_to_endswith(sanitizer, url)
|
||||
)
|
||||
}
|
||||
|
||||
predicate call_to_startswith(Call sanitizer, StrConst url) {
|
||||
sanitizer.getFunc().(Attribute).getName() = "startswith"
|
||||
and
|
||||
sanitizer.getArg(0) = url
|
||||
}
|
||||
|
||||
predicate unsafe_call_to_endswith(Call sanitizer, StrConst url) {
|
||||
sanitizer.getFunc().(Attribute).getName() = "endswith" and
|
||||
sanitizer.getArg(0) = url and
|
||||
not url.getText().regexpMatch("(?i)\\.([a-z0-9-]+)(\\.[a-z0-9-]+)+")
|
||||
}
|
||||
|
||||
from Expr sanitizer, StrConst url
|
||||
where incomplete_sanitization(sanitizer, url)
|
||||
select sanitizer, "'$@' may be at an arbitrary position in the sanitized URL.", url, url.getText()
|
||||
@@ -0,0 +1,19 @@
|
||||
from flask import Flask, request, redirect
|
||||
import re
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
UNSAFE_REGEX = re.compile("(www|beta).example.com/")
|
||||
SAFE_REGEX = re.compile(r"(www|beta)\.example\.com/")
|
||||
|
||||
@app.route('/some/path/bad')
|
||||
def unsafe(request):
|
||||
target = request.args.get('target', '')
|
||||
if UNSAFE_REGEX.match(target):
|
||||
return redirect(target)
|
||||
|
||||
@app.route('/some/path/good')
|
||||
def safe(request):
|
||||
target = request.args.get('target', '')
|
||||
if SAFE_REGEX.match(target):
|
||||
return redirect(target)
|
||||
@@ -0,0 +1,45 @@
|
||||
from flask import Flask, request, redirect
|
||||
from urllib.parse import urlparse
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# Not safe, as "evil-example.net/example.com" would be accepted
|
||||
|
||||
@app.route('/some/path/bad1')
|
||||
def unsafe1(request):
|
||||
target = request.args.get('target', '')
|
||||
if "example.com" in target:
|
||||
return redirect(target)
|
||||
|
||||
# Not safe, as "benign-looking-prefix-example.com" would be accepted
|
||||
|
||||
@app.route('/some/path/bad2')
|
||||
def unsafe2(request):
|
||||
target = request.args.get('target', '')
|
||||
if target.endswith("example.com"):
|
||||
return redirect(target)
|
||||
|
||||
|
||||
|
||||
#Simplest and safest approach is to use a white-list
|
||||
|
||||
@app.route('/some/path/good1')
|
||||
def safe1(request):
|
||||
whitelist = [
|
||||
"example.com/home",
|
||||
"example.com/login",
|
||||
]
|
||||
target = request.args.get('target', '')
|
||||
if target in whitelist:
|
||||
return redirect(target)
|
||||
|
||||
#More complex example allowing sub-domains.
|
||||
|
||||
@app.route('/some/path/good2')
|
||||
def safe2(request):
|
||||
target = request.args.get('target', '')
|
||||
host = urlparse(target).hostname
|
||||
#Note the '.' preceding example.com
|
||||
if host and host.endswith(".example.com"):
|
||||
return redirect(target)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ class SuppressionScope extends @py_comment {
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [LGTM locations](https://lgtm.com/help/ql/locations).
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
*/
|
||||
predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
|
||||
this.(SuppressionComment).covers(filepath, startline, startcolumn, endline, endcolumn)
|
||||
|
||||
@@ -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
|
||||
|
||||
4
python/ql/src/external/CodeDuplication.qll
vendored
4
python/ql/src/external/CodeDuplication.qll
vendored
@@ -12,7 +12,7 @@ string relativePath(File file) {
|
||||
* Holds if the `index`-th token of block `copy` is in file `file`, spanning
|
||||
* column `sc` of line `sl` to column `ec` of line `el`.
|
||||
*
|
||||
* For more information, see [LGTM locations](https://lgtm.com/help/ql/locations).
|
||||
* For more information, see [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
*/
|
||||
pragma[noinline, nomagic]
|
||||
private predicate tokenLocation(File file, int sl, int sc, int ec, int el, Copy copy, int index) {
|
||||
@@ -82,7 +82,7 @@ class Copy extends @duplication_or_similarity
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [LGTM locations](https://lgtm.com/help/ql/locations).
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
*/
|
||||
predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
|
||||
sourceFile().getName() = filepath and
|
||||
|
||||
2
python/ql/src/external/DefectFilter.qll
vendored
2
python/ql/src/external/DefectFilter.qll
vendored
@@ -8,7 +8,7 @@ import semmle.python.Files
|
||||
* column `startcol` of line `startline` to column `endcol` of line `endline`
|
||||
* in file `filepath`.
|
||||
*
|
||||
* For more information, see [LGTM locations](https://lgtm.com/help/ql/locations).
|
||||
* For more information, see [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
*/
|
||||
external predicate defectResults(int id, string queryPath, string filepath, int startline,
|
||||
int startcol, int endline, int endcol, string message);
|
||||
|
||||
@@ -321,7 +321,7 @@ abstract class Container extends @container {
|
||||
/**
|
||||
* Gets a URL representing the location of this container.
|
||||
*
|
||||
* For more information see https://lgtm.com/help/ql/locations#providing-urls.
|
||||
* For more information see [Providing URLs](https://help.semmle.com/QL/learn-ql/ql/locations.html#providing-urls).
|
||||
*/
|
||||
abstract string getURL();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 |
|
||||
@@ -610,7 +699,6 @@ module PointsTo {
|
||||
points_to(obj_node, context, x, icls, _) and
|
||||
(not x instanceof ModuleObject and not x instanceof ClassObject) and
|
||||
not icls.isBuiltin() and
|
||||
Types::class_has_attribute_bool(icls, name) = false and
|
||||
value = unknownValue() and cls = theUnknownType() and origin = f
|
||||
)
|
||||
)
|
||||
@@ -631,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()
|
||||
@@ -662,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)
|
||||
@@ -967,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)
|
||||
@@ -1011,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
|
||||
@@ -1373,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)
|
||||
@@ -1387,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
|
||||
@@ -1398,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)
|
||||
@@ -1407,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.
|
||||
@@ -1425,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)
|
||||
@@ -1570,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
|
||||
@@ -1627,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
|
||||
@@ -1639,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()
|
||||
@@ -1663,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
|
||||
@@ -1694,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()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1703,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() |
|
||||
@@ -1745,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)`
|
||||
* */
|
||||
@@ -1798,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
|
||||
@@ -1815,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)
|
||||
}
|
||||
|
||||
@@ -1833,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)
|
||||
@@ -1847,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() |
|
||||
@@ -1865,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()
|
||||
}
|
||||
@@ -1883,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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1899,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)
|
||||
}
|
||||
@@ -1920,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)
|
||||
@@ -1939,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)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1957,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
|
||||
@@ -1974,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)
|
||||
@@ -1984,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)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2117,10 +2259,15 @@ module PointsTo {
|
||||
private boolean truth_test_evaluates_boolean(ControlFlowNode expr, ControlFlowNode use, PointsToContext context, Object val, ClassObject cls, ControlFlowNode origin) {
|
||||
contains_interesting_expression_within_test(expr, use) and
|
||||
points_to(use, context, val, cls, origin) and
|
||||
expr = use and
|
||||
(
|
||||
expr = use and val.booleanValue() = result
|
||||
val.booleanValue() = result
|
||||
or
|
||||
expr = use and Types::instances_always_true(cls) and result = true
|
||||
Types::instances_always_true(cls) and result = true
|
||||
or
|
||||
val.maybe() and result = true
|
||||
or
|
||||
val.maybe() and result = false
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2211,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
|
||||
)
|
||||
@@ -2227,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.
|
||||
@@ -2244,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 |
|
||||
@@ -2489,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 |
|
||||
@@ -2500,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) {
|
||||
@@ -2551,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)
|
||||
@@ -2773,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 {
|
||||
|
||||
@@ -2840,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)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -242,11 +242,11 @@ private 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
|
||||
}
|
||||
|
||||
@@ -937,7 +937,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
|
||||
|
||||
@@ -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,8 @@ 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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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,7 +91,7 @@ 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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -348,21 +348,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 +376,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
|
||||
|
||||
@@ -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,4 @@ 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
|
||||
|
||||
@@ -3,3 +3,4 @@ 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
|
||||
|
||||
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 UntrustedStringKind
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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 UntrustedStringKind
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "bottle 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")
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
| 8 | ControlFlowNode for ExceptStmt | builtin-class AttributeError |
|
||||
| 10 | ControlFlowNode for ExceptStmt | builtin-class IndexError |
|
||||
| 12 | ControlFlowNode for ExceptStmt | builtin-class KeyError |
|
||||
| 14 | ControlFlowNode for ExceptStmt | builtin-class BaseException |
|
||||
| 23 | ControlFlowNode for ExceptStmt | builtin-class AttributeError |
|
||||
| 25 | ControlFlowNode for ExceptStmt | builtin-class IndexError |
|
||||
| 27 | ControlFlowNode for ExceptStmt | builtin-class KeyError |
|
||||
| 38 | ControlFlowNode for ExceptStmt | builtin-class AttributeError |
|
||||
| 40 | ControlFlowNode for ExceptStmt | builtin-class IndexError |
|
||||
| 42 | ControlFlowNode for ExceptStmt | builtin-class KeyError |
|
||||
| 57 | ControlFlowNode for ExceptStmt | builtin-class IOError |
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
import python
|
||||
|
||||
from ExceptFlowNode ex, Object obj
|
||||
where ex.handledException(obj, _, _)
|
||||
select ex.getLocation().getStartLine(), ex.toString(), obj.toString()
|
||||
@@ -0,0 +1,15 @@
|
||||
| 4 | ControlFlowNode for Subscript | builtin-class IndexError |
|
||||
| 4 | ControlFlowNode for Subscript | builtin-class KeyError |
|
||||
| 5 | ControlFlowNode for Attribute | builtin-class AttributeError |
|
||||
| 7 | ControlFlowNode for Raise | builtin-class Exception |
|
||||
| 19 | ControlFlowNode for Subscript | builtin-class IndexError |
|
||||
| 19 | ControlFlowNode for Subscript | builtin-class KeyError |
|
||||
| 20 | ControlFlowNode for Attribute | builtin-class AttributeError |
|
||||
| 22 | ControlFlowNode for Raise | builtin-class Exception |
|
||||
| 30 | ControlFlowNode for Pass | builtin-class Exception |
|
||||
| 34 | ControlFlowNode for Subscript | builtin-class IndexError |
|
||||
| 34 | ControlFlowNode for Subscript | builtin-class KeyError |
|
||||
| 35 | ControlFlowNode for Attribute | builtin-class AttributeError |
|
||||
| 37 | ControlFlowNode for Raise | builtin-class Exception |
|
||||
| 53 | ControlFlowNode for Attribute() | builtin-class IOError |
|
||||
| 54 | ControlFlowNode for Attribute() | builtin-class IOError |
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
import python
|
||||
|
||||
from RaisingNode r
|
||||
select r.getLocation().getStartLine(), r.toString(), r.getARaisedType().toString()
|
||||
@@ -0,0 +1,25 @@
|
||||
| 4 | ControlFlowNode for Subscript | 10 | ControlFlowNode for ExceptStmt |
|
||||
| 4 | ControlFlowNode for Subscript | 12 | ControlFlowNode for ExceptStmt |
|
||||
| 5 | ControlFlowNode for Attribute | 8 | ControlFlowNode for ExceptStmt |
|
||||
| 6 | ControlFlowNode for a() | 8 | ControlFlowNode for ExceptStmt |
|
||||
| 6 | ControlFlowNode for a() | 10 | ControlFlowNode for ExceptStmt |
|
||||
| 6 | ControlFlowNode for a() | 12 | ControlFlowNode for ExceptStmt |
|
||||
| 6 | ControlFlowNode for a() | 14 | ControlFlowNode for ExceptStmt |
|
||||
| 7 | ControlFlowNode for Raise | 14 | ControlFlowNode for ExceptStmt |
|
||||
| 19 | ControlFlowNode for Subscript | 25 | ControlFlowNode for ExceptStmt |
|
||||
| 19 | ControlFlowNode for Subscript | 27 | ControlFlowNode for ExceptStmt |
|
||||
| 20 | ControlFlowNode for Attribute | 23 | ControlFlowNode for ExceptStmt |
|
||||
| 21 | ControlFlowNode for a() | 23 | ControlFlowNode for ExceptStmt |
|
||||
| 21 | ControlFlowNode for a() | 25 | ControlFlowNode for ExceptStmt |
|
||||
| 21 | ControlFlowNode for a() | 27 | ControlFlowNode for ExceptStmt |
|
||||
| 21 | ControlFlowNode for a() | 30 | ControlFlowNode for Pass |
|
||||
| 22 | ControlFlowNode for Raise | 30 | ControlFlowNode for Pass |
|
||||
| 34 | ControlFlowNode for Subscript | 40 | ControlFlowNode for ExceptStmt |
|
||||
| 34 | ControlFlowNode for Subscript | 42 | ControlFlowNode for ExceptStmt |
|
||||
| 35 | ControlFlowNode for Attribute | 38 | ControlFlowNode for ExceptStmt |
|
||||
| 36 | ControlFlowNode for a() | 38 | ControlFlowNode for ExceptStmt |
|
||||
| 36 | ControlFlowNode for a() | 40 | ControlFlowNode for ExceptStmt |
|
||||
| 36 | ControlFlowNode for a() | 42 | ControlFlowNode for ExceptStmt |
|
||||
| 53 | ControlFlowNode for Attribute() | 57 | ControlFlowNode for ExceptStmt |
|
||||
| 54 | ControlFlowNode for Attribute() | 57 | ControlFlowNode for ExceptStmt |
|
||||
| 56 | ControlFlowNode for Attribute() | 57 | ControlFlowNode for ExceptStmt |
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
import python
|
||||
|
||||
from ControlFlowNode r, ControlFlowNode s
|
||||
where s = r.getAnExceptionalSuccessor() and
|
||||
not r.(RaisingNode).unlikelySuccessor(s)
|
||||
select r.getLocation().getStartLine(), r.toString(), s.getLocation().getStartLine(), s.toString()
|
||||
@@ -0,0 +1,6 @@
|
||||
| 6 | ControlFlowNode for a() |
|
||||
| 21 | ControlFlowNode for a() |
|
||||
| 36 | ControlFlowNode for a() |
|
||||
| 51 | ControlFlowNode for open() |
|
||||
| 56 | ControlFlowNode for Attribute() |
|
||||
| 58 | ControlFlowNode for Attribute() |
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
import python
|
||||
|
||||
from RaisingNode r
|
||||
where r.raisesUnknownType()
|
||||
select r.getLocation().getStartLine(), r.toString()
|
||||
@@ -0,0 +1,59 @@
|
||||
|
||||
def f(a, x):
|
||||
try:
|
||||
a[x]
|
||||
a.b
|
||||
a(x)
|
||||
raise Exception()
|
||||
except AttributeError:
|
||||
pass
|
||||
except IndexError:
|
||||
pass
|
||||
except KeyError:
|
||||
pass
|
||||
except:
|
||||
pass
|
||||
|
||||
def g(a, x):
|
||||
try:
|
||||
a[x]
|
||||
a.b
|
||||
a(x)
|
||||
raise Exception()
|
||||
except AttributeError:
|
||||
pass
|
||||
except IndexError:
|
||||
pass
|
||||
except KeyError:
|
||||
pass
|
||||
finally:
|
||||
pass
|
||||
|
||||
def h(a, x):
|
||||
try:
|
||||
a[x]
|
||||
a.b
|
||||
a(x)
|
||||
raise Exception()
|
||||
except AttributeError:
|
||||
pass
|
||||
except IndexError:
|
||||
pass
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
#I/O stuff.
|
||||
|
||||
def doesnt_raise():
|
||||
pass
|
||||
|
||||
def io():
|
||||
f12 = open("filename")
|
||||
try:
|
||||
f12.read("IOError could occur")
|
||||
f12.write("IOError could occur")
|
||||
doesnt_raise("Potential false positive here")
|
||||
f12.close()
|
||||
except IOError:
|
||||
f12.close()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user