Merge branch 'master' into python-mutable-default-with-flow

This commit is contained in:
Taus Brock-Nannestad
2019-03-01 11:10:56 +01:00
1694 changed files with 74369 additions and 54211 deletions

View File

@@ -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()
)

View File

@@ -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")
)
}

View File

@@ -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()
)
}

View File

@@ -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) {

View File

@@ -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

View File

@@ -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")
)
}

View File

@@ -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'."

View File

@@ -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."

View File

@@ -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) {

View File

@@ -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() |

View File

@@ -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"
)

View File

@@ -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())
)
}

View File

@@ -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"
)
}

View File

@@ -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) {

View File

@@ -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

View File

@@ -0,0 +1,7 @@
from tempfile import mktemp
def write_results(results):
filename = mktemp()
with open(filename, "w+") as f:
f.write(results)
print("Results written to", filename)

View File

@@ -0,0 +1,52 @@
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
<qhelp>
<overview>
<p>
Functions that create temporary file names (such as <code>tempfile.mktemp</code>
and <code>os.tempnam</code>) are fundamentally insecure, as they do not
ensure exclusive access to a file with the temporary name they return.
The file name returned by these functions is guaranteed to be unique on
creation but the file must be opened in a separate operation. There is no
guarantee that the creation and open operations will happen atomically. This
provides an opportunity for an attacker to interfere with the file before it is
opened.
</p>
<p>
Note that <code>mktemp</code> has been deprecated since Python 2.3.
</p>
</overview>
<recommendation>
<p>
Replace the use of <code>mktemp</code> with some of the more secure functions
in the <code>tempfile</code> module, such as <code>TemporaryFile</code>. If the
file is intended to be accessed from other processes, consider using the
<code>NamedTemporaryFile</code> function.
</p>
</recommendation>
<example>
<p>
The following piece of code opens a temporary file and writes a set of results
to it. Because the file name is created using <code>mktemp</code>, another
process may access this file before it is opened using <code>open</code>.
</p>
<sample src="InsecureTemporaryFile.py" />
<p>
By changing the code to use <code>NamedTemporaryFile</code> instead, the file is
opened immediately.
</p>
<sample src="SecureTemporaryFile.py" />
</example>
<references>
<li>
Python Standard Library: <a href="https://docs.python.org/3/library/tempfile.html#tempfile.mktemp">tempfile.mktemp</a>.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,31 @@
/**
* @name Insecure temporary file
* @description Creating a temporary file using this method may be insecure.
* @id py/insecure-temporary-file
* @problem.severity error
* @sub-severity high
* @precision high
* @tags external/cwe/cwe-377
* security
*/
import python
FunctionObject temporary_name_function(string mod, string function) {
(
mod = "tempfile" and function = "mktemp"
or
mod = "os" and
(
function = "tmpnam"
or
function = "tempnam"
)
) and
result = any(ModuleObject m | m.getName() = mod).getAttribute(function)
}
from Call c, string mod, string function
where
temporary_name_function(mod, function).getACall().getNode() = c
select c, "Call to deprecated function " + mod + "." + function + " may be insecure."

View File

@@ -0,0 +1,6 @@
from tempfile import NamedTemporaryFile
def write_results(results):
with NamedTemporaryFile(mode="w+", delete=False) as f:
f.write(results)
print("Results written to", f.name)

View File

@@ -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)
}

View File

@@ -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) {

View File

@@ -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)
}

View File

@@ -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. */

View File

@@ -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."

View File

@@ -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 |

View File

@@ -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

View File

@@ -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) |

View File

@@ -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)

View File

@@ -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

View File

@@ -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")
)
}

View File

@@ -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()
)
}

View File

@@ -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
)

View File

@@ -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

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -174,6 +174,14 @@ class Function extends Function_, Scope, AstNode {
Scope.super.contains(inner)
}
/** Gets a control flow node for a return value of this function */
ControlFlowNode getAReturnValueFlowNode() {
exists(Return ret |
ret.getScope() = this and
ret.getValue() = result.getNode()
)
}
}
/** A def statement. Note that FunctionDef extends Assign as a function definition binds the newly created function */

View File

@@ -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 */

View File

@@ -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

View File

@@ -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())
}

View File

@@ -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() {

View File

@@ -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"
)

View File

@@ -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
)

View File

@@ -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 {

View File

@@ -31,14 +31,84 @@ private import Filters as BaseFilters
import semmle.dataflow.SSA
private import MRO
/** Get a `ControlFlowNode` from an object or `here`.
* If the object is a ControlFlowNode then use that, otherwise fall back on `here`
*/
pragma[inline]
private ControlFlowNode origin_from_object_or_here(ObjectOrCfg object, ControlFlowNode here) {
result = object
or
not object instanceof ControlFlowNode and result = here
/* Use this version for speed */
library class CfgOrigin extends @py_object {
string toString() {
/* Not to be displayed */
none()
}
/** Get a `ControlFlowNode` from `this` or `here`.
* If `this` is a ControlFlowNode then use that, otherwise fall back on `here`
*/
pragma[inline]
ControlFlowNode asCfgNodeOrHere(ControlFlowNode here) {
result = this
or
not this instanceof ControlFlowNode and result = here
}
ControlFlowNode toCfgNode() {
result = this
}
pragma[inline]
CfgOrigin fix(ControlFlowNode here) {
if this = unknownValue() then
result = here
else
result = this
}
}
/* Use this version for stronger type-checking */
//private newtype TCfgOrigin =
// TUnknownOrigin()
// or
// TCfgOrigin(ControlFlowNode f)
//
//library class CfgOrigin extends TCfgOrigin {
//
// string toString() {
// /* Not to be displayed */
// none()
// }
//
// /** Get a `ControlFlowNode` from `this` or `here`.
// * If `this` is a ControlFlowNode then use that, otherwise fall back on `here`
// */
// pragma[inline]
// ControlFlowNode asCfgNodeOrHere(ControlFlowNode here) {
// this = TUnknownOrigin() and result = here
// or
// this = TCfgOrigin(result)
// }
//
// ControlFlowNode toCfgNode() {
// this = TCfgOrigin(result)
// }
//
// CfgOrigin fix(ControlFlowNode here) {
// this = TUnknownOrigin() and result = TCfgOrigin(here)
// or
// not this = TUnknownOrigin() and result = this
// }
//}
//
module CfgOrigin {
CfgOrigin fromCfgNode(ControlFlowNode f) {
result = f
}
CfgOrigin unknown() {
result = unknownValue()
}
}
module PointsTo {
@@ -114,38 +184,41 @@ module PointsTo {
/** INTERNAL -- Do not use.
*
* Holds if `package.name` points to `(value, cls, origin)`, where `package` is a package object. */
cached predicate package_attribute_points_to(PackageObject package, string name, Object value, ClassObject cls, ControlFlowNode origin) {
cached predicate package_attribute_points_to(PackageObject package, string name, Object value, ClassObject cls, CfgOrigin origin) {
py_module_attributes(package.getInitModule().getModule(), name, value, cls, origin)
or
exists(Module init |
init = package.getInitModule().getModule() |
not exists(PythonSsaSourceVariable v | v.getScope() = init | v.getName() = name or v.getName() = "*")
or
exists(EssaVariable v, PointsToContext imp |
v.getScope() = init and v.getName() = "*" and v.getAUse() = init.getANormalExit() |
SSA::ssa_variable_named_attribute_points_to(v, imp, name, undefinedVariable(), _, _) and
imp.isImport()
init = package.getInitModule().getModule() and
not exists(EssaVariable var | var.getAUse() = init.getANormalExit() and var.getSourceVariable().getName() = name) and
exists(EssaVariable var, Context context |
isModuleStateVariable(var) and var.getAUse() = init.getANormalExit() and
context.isImport() and
SSA::ssa_variable_named_attribute_points_to(var, context, name, undefinedVariable(), _, _) and
origin = value and
value = package.submodule(name) and
cls = theModuleType()
)
) and explicitly_imported(value) and
value = package.submodule(name) and cls = theModuleType() and origin = value
)
or
package.hasNoInitModule() and
value = package.submodule(name) and cls = theModuleType() and origin = CfgOrigin::fromCfgNode(value)
}
/** INTERNAL -- `Use m.attributeRefersTo(name, obj, origin)` instead.
*
* Holds if `m.name` points to `(value, cls, origin)`, where `m` is a (source) module. */
cached predicate py_module_attributes(Module m, string name, Object obj, ClassObject cls, ControlFlowNode origin) {
exists(EssaVariable var, ControlFlowNode exit, ObjectOrCfg orig, PointsToContext imp |
cached predicate py_module_attributes(Module m, string name, Object obj, ClassObject cls, CfgOrigin origin) {
exists(EssaVariable var, ControlFlowNode exit, PointsToContext imp |
exit = m.getANormalExit() and var.getAUse() = exit and
var.getSourceVariable().getName() = name and
ssa_variable_points_to(var, imp, obj, cls, orig) and
ssa_variable_points_to(var, imp, obj, cls, origin) and
imp.isImport() and
obj != undefinedVariable() |
origin = origin_from_object_or_here(orig, exit)
obj != undefinedVariable()
)
or
not exists(EssaVariable var | var.getAUse() = m.getANormalExit() and var.getSourceVariable().getName() = name) and
exists(EssaVariable var, PointsToContext imp |
var.getAUse() = m.getANormalExit() and var.getName() = "*" |
var.getAUse() = m.getANormalExit() and isModuleStateVariable(var) |
SSA::ssa_variable_named_attribute_points_to(var, imp, name, obj, cls, origin) and
imp.isImport() and obj != undefinedVariable()
)
@@ -249,7 +322,7 @@ module PointsTo {
/** Holds if `call` is of the form `getattr(arg, "name")`. */
cached predicate getattr(CallNode call, ControlFlowNode arg, string name) {
points_to(call.getFunction(), _, builtin_object("getattr"), _, _) and
points_to(call.getFunction(), _, Object::builtin("getattr"), _, _) and
call.getArg(1).getNode().(StrConst).getText() = name and
arg = call.getArg(0)
}
@@ -262,7 +335,7 @@ module PointsTo {
}
/** Holds if `var` refers to `(value, cls, origin)` given the context `context`. */
cached predicate ssa_variable_points_to(EssaVariable var, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
cached predicate ssa_variable_points_to(EssaVariable var, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin) {
SSA::ssa_definition_points_to(var.getDefinition(), context, value, cls, origin)
}
@@ -374,10 +447,13 @@ module PointsTo {
private module Layer {
/* Holds if BasicBlock `b` is reachable, given the context `context`. */
predicate reachableBlock(BasicBlock b, PointsToContext context) {
context.appliesToScope(b.getScope()) and
forall(ConditionBlock guard |
guard.controls(b, _) |
predicate reachableBlock(BasicBlock b, PointsToContext context) {
context.appliesToScope(b.getScope()) and not exists(ConditionBlock guard | guard.controls(b, _))
or
exists(ConditionBlock guard |
guard = b.getImmediatelyControllingBlock() and
reachableBlock(guard, context)
|
exists(Object value |
points_to(guard.getLastNode(), context, value, _, _)
|
@@ -413,12 +489,12 @@ module PointsTo {
}
/** Holds if `mod.name` points to `(value, cls, origin)`, where `mod` is a module object. */
predicate module_attribute_points_to(ModuleObject mod, string name, Object value, ClassObject cls, ObjectOrCfg origin) {
predicate module_attribute_points_to(ModuleObject mod, string name, Object value, ClassObject cls, CfgOrigin origin) {
py_module_attributes(mod.getModule(), name, value, cls, origin)
or
package_attribute_points_to(mod, name, value, cls, origin)
or
builtin_module_attribute(mod, name, value, cls) and origin = value
builtin_module_attribute(mod, name, value, cls) and origin = CfgOrigin::unknown()
}
}
@@ -457,7 +533,10 @@ module PointsTo {
or
attribute_load_points_to(f, context, value, cls, origin)
or
getattr_points_to(f, context, value, cls, origin)
exists(CfgOrigin orig |
getattr_points_to(f, context, value, cls, orig) and
origin = orig.asCfgNodeOrHere(f)
)
or
if_exp_points_to(f, context, value, cls, origin)
or
@@ -521,7 +600,7 @@ module PointsTo {
result.getSourceVariable() instanceof GlobalVariable
}
private predicate use_points_to_maybe_origin(NameNode f, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin_or_obj) {
private predicate use_points_to_maybe_origin(NameNode f, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin_or_obj) {
ssa_variable_points_to(fast_local_variable(f), context, value, cls, origin_or_obj)
or
name_lookup_points_to_maybe_origin(f, context, value, cls, origin_or_obj)
@@ -535,7 +614,7 @@ module PointsTo {
ssa_variable_points_to(name_local_variable(f), context, undefinedVariable(), _, _)
}
private predicate name_lookup_points_to_maybe_origin(NameNode f, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin_or_obj) {
private predicate name_lookup_points_to_maybe_origin(NameNode f, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin_or_obj) {
exists(EssaVariable var | var = name_local_variable(f) |
ssa_variable_points_to(var, context, value, cls, origin_or_obj)
)
@@ -544,22 +623,26 @@ module PointsTo {
global_lookup_points_to_maybe_origin(f, context, value, cls, origin_or_obj)
}
private predicate global_lookup_points_to_maybe_origin(NameNode f, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin_or_obj) {
private predicate global_lookup_points_to_maybe_origin(NameNode f, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin_or_obj) {
ssa_variable_points_to(global_variable(f), context, value, cls, origin_or_obj)
or
ssa_variable_points_to(global_variable(f), context, undefinedVariable(), _, _) and
potential_builtin_points_to(f, value, cls, origin_or_obj)
or
not exists(global_variable(f)) and context.appliesToScope(f.getScope()) and
potential_builtin_points_to(f, value, cls, origin_or_obj)
exists(ControlFlowNode origin |
origin_or_obj = CfgOrigin::fromCfgNode(origin)
|
ssa_variable_points_to(global_variable(f), context, undefinedVariable(), _, _) and
potential_builtin_points_to(f, value, cls, origin)
or
not exists(global_variable(f)) and context.appliesToScope(f.getScope()) and
potential_builtin_points_to(f, value, cls, origin)
)
}
/** Gets an object pointed to by a use (of a variable). */
private predicate use_points_to(NameNode f, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) {
exists(ObjectOrCfg origin_or_obj |
exists(CfgOrigin origin_or_obj |
value != undefinedVariable() and
use_points_to_maybe_origin(f, context, value, cls, origin_or_obj) |
origin = origin_from_object_or_here(origin_or_obj, f)
origin = origin_or_obj.asCfgNodeOrHere(f)
)
}
@@ -579,12 +662,15 @@ module PointsTo {
/** Holds if `obj.name` points to `(value, cls, orig)`. */
pragma [noinline]
private predicate class_or_module_attribute(Object obj, string name, Object value, ClassObject cls, ObjectOrCfg orig) {
private predicate class_or_module_attribute(Object obj, string name, Object value, ClassObject cls, CfgOrigin orig) {
/* Normal class attributes */
Types::class_attribute_lookup(obj, name, value, cls, orig) and cls != theStaticMethodType() and cls != theClassMethodType()
or
/* Static methods of the class */
exists(CallNode sm | Types::class_attribute_lookup(obj, name, sm, theStaticMethodType(), _) and sm.getArg(0) = value and cls = thePyFunctionType() and orig = value)
exists(CallNode sm |
Types::class_attribute_lookup(obj, name, sm, theStaticMethodType(), _) and sm.getArg(0) = value and
cls = thePyFunctionType() and orig = CfgOrigin::fromCfgNode(sm.getArg(0))
)
or
/* Module attributes */
Layer::module_attribute_points_to(obj, name, value, cls, orig)
@@ -595,7 +681,10 @@ module PointsTo {
private predicate instance_attribute_load_points_to(AttrNode f, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) {
f.isLoad() and
exists(string name |
named_attribute_points_to(f.getObject(name), context, name, value, cls, origin)
exists(CfgOrigin orig |
origin = orig.asCfgNodeOrHere(f) and
named_attribute_points_to(f.getObject(name), context, name, value, cls, orig)
)
or
/* Static methods on the class of the instance */
exists(CallNode sm, ClassObject icls |
@@ -630,10 +719,10 @@ module PointsTo {
private predicate attribute_load_points_to(AttrNode f, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) {
instance_attribute_load_points_to(f, context, value, cls, origin)
or
exists(Object cls_or_mod, string name, ObjectOrCfg orig |
exists(Object cls_or_mod, string name, CfgOrigin orig |
receiver_object(f, context, cls_or_mod, name) and
class_or_module_attribute(cls_or_mod, name, value, cls, orig) and
origin = origin_from_object_or_here(orig, f)
origin = orig.asCfgNodeOrHere(f)
)
or
points_to(f.getObject(), context, unknownValue(), theUnknownType(), origin) and value = unknownValue() and cls = theUnknownType()
@@ -661,24 +750,38 @@ module PointsTo {
/** Holds if `f` is a "from import" expression, `from mod import x` and points to `(value, cls, origin)`. */
pragma [nomagic]
private predicate from_import_points_to(ImportMemberNode f, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) {
exists(EssaVariable var, ObjectOrCfg orig |
live_import_from_dot_in_init(f, var) and
ssa_variable_points_to(var, context, value, cls, orig) and
origin = origin_from_object_or_here(orig, f)
exists(string name, ModuleObject mod, CfgOrigin orig |
points_to(f.getModule(name), context, mod, _, _) and
origin = orig.asCfgNodeOrHere(f)
|
mod.getSourceModule() = f.getEnclosingModule() and
not exists(EssaVariable var | var.getSourceVariable().getName() = name and var.getAUse() = f) and
exists(EssaVariable dollar |
isModuleStateVariable(dollar) and dollar.getAUse() = f and
SSA::ssa_variable_named_attribute_points_to(dollar, context, name, value, cls, orig)
)
or
not mod.getSourceModule() = f.getEnclosingModule() and
Layer::module_attribute_points_to(mod, name, value, cls, orig)
)
or
not live_import_from_dot_in_init(f, _) and
exists(string name, ModuleObject mod |
points_to(f.getModule(name), context, mod, _, _) |
exists(ObjectOrCfg orig |
Layer::module_attribute_points_to(mod, name, value, cls, orig) and
origin = origin_from_object_or_here(orig, f)
)
exists(EssaVariable var, CfgOrigin orig |
var = ssa_variable_for_module_attribute(f, context) and
ssa_variable_points_to(var, context, value, cls, orig) and
origin = orig.asCfgNodeOrHere(f)
)
}
private EssaVariable ssa_variable_for_module_attribute(ImportMemberNode f, PointsToContext context) {
exists(string name, ModuleObject mod, Module m |
mod.getSourceModule() = m and m = f.getEnclosingModule() and m = result.getScope() and
points_to(f.getModule(name), context, mod, _, _) and
result.getSourceVariable().getName() = name and result.getAUse() = f
)
}
/** Holds if `f` is of the form `getattr(x, "name")` and x.name points to `(value, cls, origin)`. */
private predicate getattr_points_to(CallNode f, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) {
private predicate getattr_points_to(CallNode f, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin) {
exists(ControlFlowNode arg, string name |
named_attribute_points_to(arg, context, name, value, cls, origin) and
getattr(f, arg, name)
@@ -966,7 +1069,7 @@ module PointsTo {
)
}
predicate named_attribute_points_to(ControlFlowNode f, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) {
predicate named_attribute_points_to(ControlFlowNode f, PointsToContext context, string name, Object value, ClassObject cls, CfgOrigin origin) {
exists(EssaVariable var |
var.getAUse() = f |
SSA::ssa_variable_named_attribute_points_to(var, context, name, value, cls, origin)
@@ -1010,9 +1113,9 @@ module PointsTo {
pragma [noinline]
predicate call_points_to_builtin_function(CallNode f, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) {
exists(BuiltinCallable b |
b != builtin_object("isinstance") and
b != builtin_object("issubclass") and
b != builtin_object("callable") and
b != Object::builtin("isinstance") and
b != Object::builtin("issubclass") and
b != Object::builtin("callable") and
f = get_a_call(b, context) and
cls = b.getAReturnType()
) and
@@ -1372,7 +1475,7 @@ module PointsTo {
/** Holds if the phi-function `phi` refers to `(value, cls, origin)` given the context `context`. */
pragma [noinline]
private predicate ssa_phi_points_to(PhiFunction phi, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
private predicate ssa_phi_points_to(PhiFunction phi, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin) {
exists(EssaVariable input, BasicBlock pred |
input = phi.getInput(pred) and
ssa_variable_points_to(input, context, value, cls, origin)
@@ -1386,10 +1489,13 @@ module PointsTo {
}
/** Holds if the ESSA definition `def` refers to `(value, cls, origin)` given the context `context`. */
predicate ssa_definition_points_to(EssaDefinition def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
predicate ssa_definition_points_to(EssaDefinition def, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin) {
ssa_phi_points_to(def, context, value, cls, origin)
or
ssa_node_definition_points_to(def, context, value, cls, origin)
exists(ControlFlowNode orig |
ssa_node_definition_points_to(def, context, value, cls, orig) and
origin = CfgOrigin::fromCfgNode(orig)
)
or
Filters::ssa_filter_definition_points_to(def, context, value, cls, origin)
or
@@ -1397,7 +1503,7 @@ module PointsTo {
}
pragma [nomagic]
private predicate ssa_node_definition_points_to_unpruned(EssaNodeDefinition def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
private predicate ssa_node_definition_points_to_unpruned(EssaNodeDefinition def, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) {
assignment_points_to(def, context, value, cls, origin)
or
parameter_points_to(def, context, value, cls, origin)
@@ -1406,12 +1512,12 @@ module PointsTo {
or
delete_points_to(def, context, value, cls, origin)
or
module_name_points_to(def, context, value, cls, origin)
or
scope_entry_points_to(def, context, value, cls, origin)
or
implicit_submodule_points_to(def, context, value, cls, origin)
or
module_name_points_to(def, context, value, cls, origin)
or
iteration_definition_points_to(def, context, value, cls, origin)
/*
* No points-to for non-local function entry definitions yet.
@@ -1424,13 +1530,13 @@ module PointsTo {
}
pragma [noinline]
private predicate ssa_node_definition_points_to(EssaNodeDefinition def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
private predicate ssa_node_definition_points_to(EssaNodeDefinition def, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) {
reachable_definitions(def) and
ssa_node_definition_points_to_unpruned(def, context, value, cls, origin)
}
pragma [noinline]
private predicate ssa_node_refinement_points_to(EssaNodeRefinement def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
private predicate ssa_node_refinement_points_to(EssaNodeRefinement def, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin) {
method_callsite_points_to(def, context, value, cls, origin)
or
import_star_points_to(def, context, value, cls, origin)
@@ -1569,8 +1675,9 @@ module PointsTo {
neither_class_nor_static_method(scope)
)
or
exists(EssaVariable obj, PointsToContext caller |
ssa_variable_points_to(obj, caller, value, cls, origin) and
exists(EssaVariable obj, PointsToContext caller, CfgOrigin orig |
origin = orig.asCfgNodeOrHere(def.getDefiningNode()) and
ssa_variable_points_to(obj, caller, value, cls, orig) and
callsite_self_argument_transfer(obj, caller, def, context)
)
or
@@ -1626,7 +1733,7 @@ module PointsTo {
* PointsTo isn't exactly how the interpreter works, but is the best approximation we can manage statically.
*/
pragma [noinline]
private predicate implicit_submodule_points_to(ImplicitSubModuleDefinition def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
private predicate implicit_submodule_points_to(ImplicitSubModuleDefinition def, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) {
exists(PackageObject package |
package.getInitModule().getModule() = def.getDefiningNode().getScope() |
value = package.submodule(def.getSourceVariable().getName()) and
@@ -1638,7 +1745,7 @@ module PointsTo {
/** Implicit "definition" of `__name__` at the start of a module. */
pragma [noinline]
private predicate module_name_points_to(ScopeEntryDefinition def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
private predicate module_name_points_to(ScopeEntryDefinition def, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) {
def.getVariable().getName() = "__name__" and
exists(Module m |
m = def.getScope()
@@ -1662,24 +1769,25 @@ module PointsTo {
/** Definition of iteration variable in loop */
pragma [noinline]
private predicate iteration_definition_points_to(IterationDefinition def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
private predicate iteration_definition_points_to(IterationDefinition def, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) {
points_to(def.getSequence(), context, unknownValue(), _, _) and
value = unknownValue() and cls = theUnknownType() and origin = def.getDefiningNode()
}
/** Points-to for implicit variable declarations at scope-entry. */
pragma [noinline]
private predicate scope_entry_points_to(ScopeEntryDefinition def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
private predicate scope_entry_points_to(ScopeEntryDefinition def, PointsToContext context, Object value, ClassObject cls, ControlFlowNode origin) {
/* Transfer from another scope */
exists(EssaVariable var, PointsToContext outer |
exists(EssaVariable var, PointsToContext outer, CfgOrigin orig |
Flow::scope_entry_value_transfer(var, outer, def, context) and
ssa_variable_points_to(var, outer, value, cls, origin)
ssa_variable_points_to(var, outer, value, cls, orig) and
origin = orig.asCfgNodeOrHere(def.getDefiningNode())
)
or
/* Undefined variable */
exists(Scope scope |
not def.getVariable().getName() = "__name__" and
not def.getVariable().getName() = "*" and
not def.getVariable().getName() = "$" and
def.getScope() = scope and context.appliesToScope(scope) |
def.getSourceVariable() instanceof GlobalVariable and scope instanceof Module
or
@@ -1693,7 +1801,7 @@ module PointsTo {
mod = def.getScope().getEnclosingModule() and
context.appliesToScope(def.getScope()) and
not exists(EssaVariable v | v.getSourceVariable() = var and v.getScope() = mod) and
builtin_name_points_to(var.getId(), value, cls) and origin = value
builtin_name_points_to(var.getId(), value, cls) and origin = def.getDefiningNode()
)
}
@@ -1702,39 +1810,54 @@ module PointsTo {
* Where var may be redefined in call to `foo` if `var` escapes (is global or non-local).
*/
pragma [noinline]
private predicate callsite_points_to(CallsiteRefinement def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
exists(EssaVariable var, PointsToContext callee |
Flow::callsite_exit_value_transfer(var, callee, def, context) and
ssa_variable_points_to(var, callee, value, cls, origin)
private predicate callsite_points_to(CallsiteRefinement def, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin) {
exists(SsaSourceVariable srcvar |
srcvar = def.getSourceVariable() |
if srcvar instanceof EscapingAssignmentGlobalVariable then (
/* If global variable can be reassigned, we need to track it through calls */
exists(EssaVariable var, PointsToContext callee |
Flow::callsite_exit_value_transfer(var, callee, def, context) and
ssa_variable_points_to(var, callee, value, cls, origin)
)
or
callsite_points_to_python(def, context, value, cls, origin)
or
callsite_points_to_builtin(def, context, value, cls, origin)
) else (
/* Otherwise we can assume its value (but not those of its attributes or members) has not changed. */
ssa_variable_points_to(def.getInput(), context, value, cls, origin)
)
)
or
callsite_points_to_python(def, context, value, cls, origin)
or
callsite_points_to_builtin(def, context, value, cls, origin)
}
pragma [noinline]
private predicate callsite_points_to_python(CallsiteRefinement def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
ssa_variable_points_to(def.getInput(), context, value, cls, origin) and
exists(CallNode call, PythonSsaSourceVariable var |
/** Holds if `call`, in `context` is a call to a function that does not modify the refined variable */
private predicate call_to_safe_function(CallsiteRefinement def, PointsToContext context) {
exists(CallNode call |
call = def.getCall() and
var = def.getSourceVariable() and
context.untrackableCall(call) and
exists(PyFunctionObject modifier, Function f |
f = modifier.getFunction() and
call = get_a_call(modifier, context) and
not modifies_escaping_variable(f, var)
not modifies_escaping_variable(f, def.getSourceVariable())
)
)
}
private predicate callsite_points_to_python(CallsiteRefinement def, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin) {
exists(EssaVariable input |
input = def.getInput() and
ssa_variable_points_to(input, context, value, cls, origin) and
call_to_safe_function(def, context)
)
}
private predicate modifies_escaping_variable(Function modifier, PythonSsaSourceVariable var) {
exists(var.redefinedAtCallSite()) and
modifier.getBody().contains(var.(Variable).getAStore())
}
pragma [noinline]
private predicate callsite_points_to_builtin(CallsiteRefinement def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
private predicate callsite_points_to_builtin(CallsiteRefinement def, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin) {
ssa_variable_points_to(def.getInput(), context, value, cls, origin) and
exists(CallNode call |
call = def.getCall() |
@@ -1744,51 +1867,55 @@ module PointsTo {
}
/** Pass through for `self` for the implicit re-definition of `self` in `self.foo()`. */
private predicate method_callsite_points_to(MethodCallsiteRefinement def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
private predicate method_callsite_points_to(MethodCallsiteRefinement def, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin) {
/* The value of self remains the same, only the attributes may change */
ssa_variable_points_to(def.getInput(), context, value, cls, origin)
}
/** Points-to for `from ... import *`. */
private predicate import_star_points_to(ImportStarRefinement def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
exists(ModuleObject mod, string name |
Flow::module_and_name_for_import_star(mod, name, def, context) |
/* Attribute from imported module */
module_exports(mod, name) and
Layer::module_attribute_points_to(mod, name, value, cls, origin)
)
or
exists(EssaVariable var |
/* Retain value held before import */
Flow::variable_not_redefined_by_import_star(var, context, def) and
ssa_variable_points_to(var, context, value, cls, origin)
private predicate import_star_points_to(ImportStarRefinement def, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin) {
exists(CfgOrigin orig |
origin = orig.fix(def.getDefiningNode())
|
exists(ModuleObject mod, string name |
Flow::module_and_name_for_import_star(mod, name, def, context) |
/* Attribute from imported module */
module_exports(mod, name) and
Layer::module_attribute_points_to(mod, name, value, cls, orig)
)
or
exists(EssaVariable var |
/* Retain value held before import */
Flow::variable_not_redefined_by_import_star(var, context, def) and
ssa_variable_points_to(var, context, value, cls, orig)
)
)
}
/** Attribute assignments have no effect as far as value tracking is concerned, except for `__class__`. */
pragma [noinline]
private predicate attribute_assignment_points_to(AttributeAssignment def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
private predicate attribute_assignment_points_to(AttributeAssignment def, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin) {
if def.getName() = "__class__" then
ssa_variable_points_to(def.getInput(), context, value, _, _) and points_to(def.getValue(), _, cls, _,_) and
origin = def.getDefiningNode()
origin = CfgOrigin::fromCfgNode(def.getDefiningNode())
else
ssa_variable_points_to(def.getInput(), context, value, cls, origin)
}
/** Ignore the effects of calls on their arguments. PointsTo is an approximation, but attempting to improve accuracy would be very expensive for very little gain. */
pragma [noinline]
private predicate argument_points_to(ArgumentRefinement def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
private predicate argument_points_to(ArgumentRefinement def, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin) {
ssa_variable_points_to(def.getInput(), context, value, cls, origin)
}
/** Attribute deletions have no effect as far as value tracking is concerned. */
pragma [noinline]
private predicate attribute_delete_points_to(EssaAttributeDeletion def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
private predicate attribute_delete_points_to(EssaAttributeDeletion def, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin) {
ssa_variable_points_to(def.getInput(), context, value, cls, origin)
}
/* Data flow for attributes. These mirror the "normal" points-to predicates.
* For each points-to predicate `xxx_points_to(XXX def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin)`
* For each points-to predicate `xxx_points_to(XXX def, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin)`
* There is an equivalent predicate that tracks the values in attributes:
* `xxx_named_attribute_points_to(XXX def, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin)`
* */
@@ -1797,15 +1924,18 @@ module PointsTo {
*
* Hold if the attribute `name` of the ssa variable `var` refers to `(value, cls, origin)`.
*/
predicate ssa_variable_named_attribute_points_to(EssaVariable var, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) {
predicate ssa_variable_named_attribute_points_to(EssaVariable var, PointsToContext context, string name, Object value, ClassObject cls, CfgOrigin origin) {
ssa_definition_named_attribute_points_to(var.getDefinition(), context, name, value, cls, origin)
}
/** Helper for `ssa_variable_named_attribute_points_to`. */
private predicate ssa_definition_named_attribute_points_to(EssaDefinition def, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) {
private predicate ssa_definition_named_attribute_points_to(EssaDefinition def, PointsToContext context, string name, Object value, ClassObject cls, CfgOrigin origin) {
ssa_phi_named_attribute_points_to(def, context, name, value, cls, origin)
or
ssa_node_definition_named_attribute_points_to(def, context, name, value, cls, origin)
exists(ControlFlowNode orig |
ssa_node_definition_named_attribute_points_to(def, context, name, value, cls, orig) and
origin = CfgOrigin::fromCfgNode(orig)
)
or
ssa_node_refinement_named_attribute_points_to(def, context, name, value, cls, origin)
or
@@ -1814,7 +1944,7 @@ module PointsTo {
/** Holds if the attribute `name` of the ssa phi-function definition `phi` refers to `(value, cls, origin)`. */
pragma[noinline]
private predicate ssa_phi_named_attribute_points_to(PhiFunction phi, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) {
private predicate ssa_phi_named_attribute_points_to(PhiFunction phi, PointsToContext context, string name, Object value, ClassObject cls, CfgOrigin origin) {
ssa_variable_named_attribute_points_to(phi.getAnInput(), context, name, value, cls, origin)
}
@@ -1832,7 +1962,7 @@ module PointsTo {
/** Helper for `ssa_definition_named_attribute_points_to`. */
pragma[noinline]
private predicate ssa_node_refinement_named_attribute_points_to(EssaNodeRefinement def, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) {
private predicate ssa_node_refinement_named_attribute_points_to(EssaNodeRefinement def, PointsToContext context, string name, Object value, ClassObject cls, CfgOrigin origin) {
attribute_assignment_named_attribute_points_to(def, context, name, value, cls, origin)
or
attribute_delete_named_attribute_points_to(def, context, name, value, cls, origin)
@@ -1846,13 +1976,14 @@ module PointsTo {
pragma[noinline]
private predicate scope_entry_named_attribute_points_to(ScopeEntryDefinition def, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) {
exists(EssaVariable var, PointsToContext outer |
exists(EssaVariable var, PointsToContext outer, CfgOrigin orig |
Flow::scope_entry_value_transfer(var, outer, def, context) and
ssa_variable_named_attribute_points_to(var, outer, name, value, cls, origin)
ssa_variable_named_attribute_points_to(var, outer, name, value, cls, orig) and
origin = orig.asCfgNodeOrHere(def.getDefiningNode())
)
or
origin = def.getDefiningNode() and
def.getSourceVariable().getName() = "*" and
isModuleStateVariable(def.getVariable()) and
context.isImport() and
exists(PackageObject package |
package.getInitModule().getModule() = def.getScope() |
@@ -1864,12 +1995,15 @@ module PointsTo {
pragma[noinline]
private predicate assignment_named_attribute_points_to(AssignmentDefinition def, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) {
named_attribute_points_to(def.getValue(), context, name, value, cls, origin)
exists(CfgOrigin orig |
named_attribute_points_to(def.getValue(), context, name, value, cls, orig) and
origin = orig.asCfgNodeOrHere(def.getDefiningNode())
)
}
pragma[noinline]
private predicate attribute_assignment_named_attribute_points_to(AttributeAssignment def, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) {
points_to(def.getValue(), context, value, cls, origin) and name = def.getName()
private predicate attribute_assignment_named_attribute_points_to(AttributeAssignment def, PointsToContext context, string name, Object value, ClassObject cls, CfgOrigin origin) {
points_to(def.getValue(), context, value, cls, origin.toCfgNode()) and name = def.getName()
or
ssa_variable_named_attribute_points_to(def.getInput(), context, name, value, cls, origin) and not name = def.getName()
}
@@ -1882,9 +2016,9 @@ module PointsTo {
exists(ControlFlowNode func, Object obj |
two_args_first_arg_string(def, func, name) and
points_to(func, _, obj, _, _) |
obj = builtin_object("setattr") and result = true
obj = Object::builtin("setattr") and result = true
or
obj != builtin_object("setattr") and result = false
obj != Object::builtin("setattr") and result = false
)
}
@@ -1898,10 +2032,10 @@ module PointsTo {
}
pragma[noinline]
private predicate argument_named_attribute_points_to(ArgumentRefinement def, PointsToContext context, string name, Object value, ClassObject cls, ObjectOrCfg origin) {
private predicate argument_named_attribute_points_to(ArgumentRefinement def, PointsToContext context, string name, Object value, ClassObject cls, CfgOrigin origin) {
not two_args_first_arg_string(def, _, name) and ssa_variable_named_attribute_points_to(def.getInput(), context, name, value, cls, origin)
or
sets_attribute(def, name) = true and points_to(def.getDefiningNode().(CallNode).getArg(2), context, value, cls, origin)
sets_attribute(def, name) = true and points_to(def.getDefiningNode().(CallNode).getArg(2), context, value, cls, origin.toCfgNode())
or
sets_attribute(def, name) = false and ssa_variable_named_attribute_points_to(def.getInput(), context, name, value, cls, origin)
}
@@ -1919,7 +2053,7 @@ module PointsTo {
}
pragma[noinline]
private predicate self_callsite_named_attribute_points_to(SelfCallsiteRefinement def, PointsToContext context, string name, Object value, ClassObject cls, ObjectOrCfg origin) {
private predicate self_callsite_named_attribute_points_to(SelfCallsiteRefinement def, PointsToContext context, string name, Object value, ClassObject cls, CfgOrigin origin) {
exists(EssaVariable var, PointsToContext callee |
ssa_variable_named_attribute_points_to(var, callee, name, value, cls, origin) and
callee_self_variable(var, callee, def, context)
@@ -1938,17 +2072,28 @@ module PointsTo {
)
}
/** Maps the caller object/context to callee parameter/context for self in calls to methods */
private predicate self_in_method_call(ControlFlowNode obj, PointsToContext caller, ParameterDefinition self, PointsToContext callee) {
self.isSelf() and
exists(FunctionObject meth, CallNode call |
meth.getFunction() = self.getScope() and
callee.fromCall(call, meth, caller) and
call.getFunction().(AttrNode).getObject() = obj
)
}
pragma [noinline]
private predicate self_parameter_named_attribute_points_to(ParameterDefinition def, PointsToContext context, string name, Object value, ClassObject vcls, ControlFlowNode origin) {
context.isRuntime() and executes_in_runtime_context(def.getScope()) and
ssa_variable_named_attribute_points_to(preceding_self_variable(def), context, name, value, vcls, origin)
or
exists(FunctionObject meth, CallNode call, PointsToContext caller_context, ControlFlowNode obj |
meth.getFunction() = def.getScope() and
method_call(meth, caller_context, call) and
call.getFunction().(AttrNode).getObject() = obj and
context.fromCall(call, meth, caller_context) and
named_attribute_points_to(obj, caller_context, name, value, vcls, origin)
exists(CfgOrigin orig |
origin = orig.toCfgNode()
|
context.isRuntime() and executes_in_runtime_context(def.getScope()) and
ssa_variable_named_attribute_points_to(preceding_self_variable(def), context, name, value, vcls, orig)
or
exists(PointsToContext caller_context, ControlFlowNode obj |
self_in_method_call(obj, caller_context, def, context) and
named_attribute_points_to(obj, caller_context, name, value, vcls, orig)
)
)
}
@@ -1956,14 +2101,14 @@ module PointsTo {
none()
}
private predicate attribute_delete_named_attribute_points_to(EssaAttributeDeletion def, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) {
private predicate attribute_delete_named_attribute_points_to(EssaAttributeDeletion def, PointsToContext context, string name, Object value, ClassObject cls, CfgOrigin origin) {
none()
}
/* Helper for import_star_named_attribute_points_to */
pragma [noinline]
private predicate star_variable_import_star_module(ImportStarRefinement def, ImportStarNode imp, PointsToContext context, ModuleObject mod) {
def.getSourceVariable().getName() = "*" and
isModuleStateVariable(def.getVariable()) and
exists(ControlFlowNode fmod |
fmod = imp.getModule() and
imp = def.getDefiningNode() and
@@ -1973,7 +2118,7 @@ module PointsTo {
/* Helper for import_star_named_attribute_points_to */
pragma [noinline, nomagic]
private predicate ssa_star_variable_input_points_to(ImportStarRefinement def, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) {
private predicate ssa_star_variable_input_points_to(ImportStarRefinement def, PointsToContext context, string name, Object value, ClassObject cls, CfgOrigin origin) {
exists(EssaVariable var |
ssa_star_import_star_input(def, var) and
ssa_variable_named_attribute_points_to(var, context, name, value, cls, origin)
@@ -1983,24 +2128,22 @@ module PointsTo {
/* Helper for ssa_star_variable_input_points_to */
pragma [noinline]
private predicate ssa_star_import_star_input(ImportStarRefinement def, EssaVariable var) {
def.getSourceVariable().getName() = "*" and var = def.getInput()
isModuleStateVariable(def.getVariable()) and var = def.getInput()
}
pragma [noinline]
private predicate import_star_named_attribute_points_to(ImportStarRefinement def, PointsToContext context, string name, Object value, ClassObject cls, ControlFlowNode origin) {
exists(ImportStarNode imp, ModuleObject mod |
private predicate import_star_named_attribute_points_to(ImportStarRefinement def, PointsToContext context, string name, Object value, ClassObject cls, CfgOrigin origin) {
exists(ImportStarNode imp, ModuleObject mod, CfgOrigin orig |
origin = orig.fix(imp) and
star_variable_import_star_module(def, imp, context, mod) |
/* Attribute from imported module */
module_exports_boolean(mod, name) = true and
exists(ObjectOrCfg obj |
Layer::module_attribute_points_to(mod, name, value, cls, obj) and
not exists(Variable v | v.getId() = name and v.getScope() = imp.getScope()) and
origin = origin_from_object_or_here(obj, imp)
)
Layer::module_attribute_points_to(mod, name, value, cls, orig) and
not exists(Variable v | v.getId() = name and v.getScope() = imp.getScope())
or
/* Retain value held before import */
module_exports_boolean(mod, name) = false and
ssa_star_variable_input_points_to(def, context, name, value, cls, origin)
ssa_star_variable_input_points_to(def, context, name, value, cls, orig)
)
}
@@ -2215,7 +2358,7 @@ module PointsTo {
(
exists(CallNode call |
call = expr and
points_to(call.getFunction(), context, theLenFunction(), _, _) and
points_to(call.getFunction(), context, Object::builtin("len"), _, _) and
use = call.getArg(0) and
val.(SequenceObject).getLength() = result
)
@@ -2231,9 +2374,9 @@ module PointsTo {
}
/** Holds if ESSA edge refinement, `def`, refers to `(value, cls, origin)`. */
predicate ssa_filter_definition_points_to(PyEdgeRefinement def, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
predicate ssa_filter_definition_points_to(PyEdgeRefinement def, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin) {
exists(ControlFlowNode test, ControlFlowNode use |
refinement_test(test, use, test_evaluates_boolean(test, use, context, value, cls, origin), def)
refinement_test(test, use, test_evaluates_boolean(test, use, context, value, cls, origin.toCfgNode()), def)
)
or
/* If we can't understand the test, assume that value passes through.
@@ -2248,23 +2391,23 @@ module PointsTo {
/** Holds if ESSA definition, `uniphi`, refers to `(value, cls, origin)`. */
pragma [noinline]
predicate uni_edged_phi_points_to(SingleSuccessorGuard uniphi, PointsToContext context, Object value, ClassObject cls, ObjectOrCfg origin) {
predicate uni_edged_phi_points_to(SingleSuccessorGuard uniphi, PointsToContext context, Object value, ClassObject cls, CfgOrigin origin) {
exists(ControlFlowNode test, ControlFlowNode use |
/* Because calls such as `len` may create a new variable, we need to go via the source variable
* That is perfectly safe as we are only dealing with calls that do not mutate their arguments.
*/
use = uniphi.getInput().getSourceVariable().(Variable).getAUse() and
test = uniphi.getDefiningNode() and
uniphi.getSense() = test_evaluates_boolean(test, use, context, value, cls, origin)
uniphi.getSense() = test_evaluates_boolean(test, use, context, value, cls, origin.toCfgNode())
)
}
/** Holds if the named attibute of ESSA edge refinement, `def`, refers to `(value, cls, origin)`. */
pragma[noinline]
predicate ssa_filter_definition_named_attribute_points_to(PyEdgeRefinement def, PointsToContext context, string name, Object value, ClassObject cls, ObjectOrCfg origin) {
predicate ssa_filter_definition_named_attribute_points_to(PyEdgeRefinement def, PointsToContext context, string name, Object value, ClassObject cls, CfgOrigin origin) {
exists(ControlFlowNode test, AttrNode use, boolean sense |
edge_refinement_attr_use_sense(def, test, use, name, sense) and
sense = test_evaluates_boolean(test, use, context, value, cls, origin)
sense = test_evaluates_boolean(test, use, context, value, cls, origin.toCfgNode())
)
or
exists(EssaVariable input |
@@ -2493,7 +2636,7 @@ module PointsTo {
}
/** INTERNAL -- Use `ClassObject.declaredAttribute(name). instead. */
cached predicate class_declared_attribute(ClassObject owner, string name, Object value, ClassObject vcls, ObjectOrCfg origin) {
cached predicate class_declared_attribute(ClassObject owner, string name, Object value, ClassObject vcls, CfgOrigin origin) {
/* Note that src_var must be a local variable, we aren't interested in the value that any global variable may hold */
value != undefinedVariable() and
exists(EssaVariable var, LocalVariable src_var |
@@ -2504,7 +2647,7 @@ module PointsTo {
)
or
value = builtin_class_attribute(owner, name) and class_declares_attribute(owner, name) and
origin = value and vcls = builtin_object_type(value)
origin = CfgOrigin::unknown() and vcls = builtin_object_type(value)
}
private predicate interesting_class_attribute(ClassList mro, string name) {
@@ -2555,7 +2698,7 @@ module PointsTo {
/** INTERNAL -- Use `ClassObject.attributeRefersTo(name, value, vlcs, origin). instead.
*/
cached predicate class_attribute_lookup(ClassObject cls, string name, Object value, ClassObject vcls, ObjectOrCfg origin) {
cached predicate class_attribute_lookup(ClassObject cls, string name, Object value, ClassObject vcls, CfgOrigin origin) {
exists(ClassObject defn |
defn = get_mro(cls).findDeclaringClass(name) and
class_declared_attribute(defn, name, value, vcls, origin)
@@ -2777,6 +2920,15 @@ module PointsTo {
}
/** Get the ESSA pseudo-variable used to retain module state
* during module initialization. Module attributes are handled
* as attributes of this variable, allowing the SSA form to track
* mutations of the module during its creation.
*/
private predicate isModuleStateVariable(EssaVariable var) {
var.getName() = "$" and var.getScope() instanceof Module
}
/** INTERNAL -- Public for testing only */
module Test {
@@ -2844,19 +2996,21 @@ class SuperBoundMethod extends Object {
SuperCall superObject;
string name;
ClassObject startType;
cached
SuperBoundMethod() {
exists(ControlFlowNode object |
this.(AttrNode).getObject(name) = object |
PointsTo::points_to(object, _, superObject, _, _)
PointsTo::points_to(object, _, superObject, _, _) and
startType = superObject.startType()
)
}
FunctionObject getFunction(PointsToContext ctx) {
exists(ClassList mro |
mro = PointsTo::Types::get_mro(superObject.selfType(ctx)) |
result = mro.startingAt(superObject.startType()).getTail().lookup(name)
result = mro.startingAt(startType).getTail().lookup(name)
)
}

View File

@@ -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

View File

@@ -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() {

View File

@@ -12,7 +12,7 @@ private ModuleObject theTracebackModule() {
}
private FunctionObject traceback_function(string name) {
result = theTracebackModule().getAttribute(name)
result = theTracebackModule().attr(name)
}
/**

View File

@@ -167,6 +167,7 @@ abstract class CollectionKind extends TaintKind {
/* Prevent any collection kinds more than 2 deep */
not this.charAt(2) = "[" and not this.charAt(2) = "{"
}
}
/** A taint kind representing a flat collections of kinds.
@@ -193,7 +194,7 @@ class SequenceKind extends CollectionKind {
tonode.(BinaryExprNode).getAnOperand() = fromnode
)
or
result = this and copy_call(fromnode, tonode)
result = this and TaintFlowImplementation::copyCall(fromnode, tonode)
or
exists(BinaryExprNode mod |
mod = tonode and
@@ -236,20 +237,6 @@ private predicate slice(ControlFlowNode fromnode, SubscriptNode tonode) {
)
}
/* A call that returns a copy (or similar) of the argument */
private predicate copy_call(ControlFlowNode fromnode, CallNode tonode) {
tonode.getFunction().(AttrNode).getObject("copy") = fromnode
or
exists(ModuleObject copy, string name |
name = "copy" or name = "deepcopy" |
copy.getAttribute(name).(FunctionObject).getACall() = tonode and
tonode.getArg(0) = fromnode
)
or
tonode.getFunction().refersTo(builtin_object("reversed")) and
tonode.getArg(0) = fromnode
}
/** A taint kind representing a mapping of objects to kinds.
* Typically a dict, but can include other mappings.
*/
@@ -272,7 +259,7 @@ class DictKind extends CollectionKind {
result = valueKind and
tonode.(CallNode).getFunction().(AttrNode).getObject("get") = fromnode
or
result = this and copy_call(fromnode, tonode)
result = this and TaintFlowImplementation::copyCall(fromnode, tonode)
or
result = this and
tonode.(CallNode).getFunction().refersTo(theDictType()) and
@@ -937,7 +924,7 @@ library module TaintFlowImplementation {
pragma [noinline]
predicate getattr_step(TaintedNode fromnode, TrackedValue totaint, CallContext tocontext, CallNode tonode) {
exists(ControlFlowNode arg, string name |
tonode.getFunction().refersTo(builtin_object("getattr")) and
tonode.getFunction().refersTo(Object::builtin("getattr")) and
arg = tonode.getArg(0) and
name = tonode.getArg(1).getNode().(StrConst).getText() and
arg = fromnode.getNode() and
@@ -1263,6 +1250,20 @@ library module TaintFlowImplementation {
context = fromnode.getContext()
}
/* A call that returns a copy (or similar) of the argument */
predicate copyCall(ControlFlowNode fromnode, CallNode tonode) {
tonode.getFunction().(AttrNode).getObject("copy") = fromnode
or
exists(ModuleObject copy, string name |
name = "copy" or name = "deepcopy" |
copy.attr(name).(FunctionObject).getACall() = tonode and
tonode.getArg(0) = fromnode
)
or
tonode.getFunction().refersTo(Object::builtin("reversed")) and
tonode.getArg(0) = fromnode
}
}
/* Helper predicate for tainted_with */

View File

@@ -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_")

View File

@@ -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.

View File

@@ -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")
}

View File

@@ -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
)
}

View File

@@ -20,7 +20,7 @@ private ModuleObject pickleModule() {
}
private FunctionObject pickleLoads() {
result = pickleModule().getAttribute("loads")
result = pickleModule().attr("loads")
}
/** `pickle.loads(untrusted)` vulnerability. */

View File

@@ -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. */

View File

@@ -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. */

View File

@@ -110,9 +110,19 @@ private predicate slice(ControlFlowNode fromnode, SubscriptNode tonode) {
/* tonode = os.path.join(..., fromnode, ...) */
private predicate os_path_join(ControlFlowNode fromnode, CallNode tonode) {
exists(FunctionObject path_join |
exists(ModuleObject os | os.getName() = "os" |
os.getAttribute("path").(ModuleObject).getAttribute("join") = path_join
) |
path_join = ModuleObject::named("os").attr("path").(ModuleObject).attr("join")
and
tonode = path_join.getACall() and tonode.getAnArg() = fromnode
)
}
/** A kind of "taint", representing a dictionary mapping str->"taint" */
class StringDictKind extends DictKind {
StringDictKind() {
this.getValue() instanceof StringKind
}
}

View File

@@ -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
}

View File

@@ -91,8 +91,28 @@ private predicate json_subscript_taint(SubscriptNode sub, ControlFlowNode obj, E
private predicate json_load(ControlFlowNode fromnode, CallNode tonode) {
exists(FunctionObject json_loads |
any(ModuleObject json | json.getName() = "json").getAttribute("loads") = json_loads and
any(ModuleObject json | json.getName() = "json").attr("loads") = json_loads and
json_loads.getACall() = tonode and tonode.getArg(0) = fromnode
)
}
/** A kind of "taint", representing an open file-like object from an external source. */
class ExternalFileObject extends TaintKind {
ExternalFileObject() {
this = "file[" + any(ExternalStringKind key) + "]"
}
/** Gets the taint kind for the contents of this file */
TaintKind getValue() {
this = "file[" + result + "]"
}
override TaintKind getTaintOfMethodResult(string name) {
name = "read" and result = this.getValue()
}
}

View File

@@ -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")
}

View File

@@ -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

View File

@@ -160,10 +160,7 @@ class PyFunctionObject extends FunctionObject {
/** Gets a control flow node corresponding to the value of a return statement */
ControlFlowNode getAReturnedNode() {
exists(Return ret |
ret.getScope() = this.getFunction() and
result.getNode() = ret.getValue()
)
result = this.getFunction().getAReturnValueFlowNode()
}
override string descriptiveString() {
@@ -348,21 +345,21 @@ class BuiltinFunctionObject extends BuiltinCallable {
override ClassObject getAReturnType() {
/* Enumerate the types of a few builtin functions, that the CPython analysis misses.
*/
this = builtin_object("hex") and result = theStrType()
this = Object::builtin("hex") and result = theStrType()
or
this = builtin_object("oct") and result = theStrType()
this = Object::builtin("oct") and result = theStrType()
or
this = builtin_object("intern") and result = theStrType()
this = Object::builtin("intern") and result = theStrType()
or
/* Fix a few minor inaccuracies in the CPython analysis */
ext_rettype(this, result) and not (
this = builtin_object("__import__") and result = theNoneType()
this = Object::builtin("__import__") and result = theNoneType()
or
this = builtin_object("compile") and result = theNoneType()
this = Object::builtin("compile") and result = theNoneType()
or
this = builtin_object("sum")
this = Object::builtin("sum")
or
this = builtin_object("filter")
this = Object::builtin("filter")
)
}
@@ -376,4 +373,53 @@ class BuiltinFunctionObject extends BuiltinCallable {
}
/** DEPRECATED -- Use `Object::builtin("apply")` instead. */
Object theApplyFunction() {
result = Object::builtin("apply")
}
/** DEPRECATED -- Use `Object::builtin("hasattr")` instead. */
Object theHasattrFunction() {
result = Object::builtin("hasattr")
}
/** DEPRECATED -- Use `Object::builtin("len")` instead. */
Object theLenFunction() {
result = Object::builtin("len")
}
/** DEPRECATED -- Use `Object::builtin("format")` instead. */
Object theFormatFunction() {
result = Object::builtin("format")
}
/** DEPRECATED -- Use `Object::builtin("open")` instead. */
Object theOpenFunction() {
result = Object::builtin("open")
}
/** DEPRECATED -- Use `Object::builtin("print")` instead. */
Object thePrintFunction() {
result = Object::builtin("print")
}
/** DEPRECATED -- Use `Object::builtin("input")` instead. */
Object theInputFunction() {
result = Object::builtin("input")
}
/** DEPRECATED -- Use `Object::builtin("locals")` instead. */
Object theLocalsFunction() {
result = Object::builtin("locals")
}
/** DEPRECATED -- Use `Object::builtin("globals")()` instead. */
Object theGlobalsFunction() {
result = Object::builtin("globals")
}
/** DEPRECATED -- Use `Object::builtin("sysExit()` instead. */
Object theExitFunctionObject() {
result = ModuleObject::named("sys").attr("exit")
}

View File

@@ -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() {

View File

@@ -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")
}
}

View File

@@ -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

View File

@@ -23,3 +23,69 @@ string httpVerb() {
string httpVerbLower() {
result = httpVerb().toLowerCase()
}
/** Taint kind representing the WSGI environment.
* As specified in PEP 3333. https://www.python.org/dev/peps/pep-3333/#environ-variables
*/
class WsgiEnvironment extends TaintKind {
WsgiEnvironment() { this = "wsgi.environment" }
override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
result = this and TaintFlowImplementation::copyCall(fromnode, tonode)
or
result = this and
tonode.(CallNode).getFunction().refersTo(theDictType()) and
tonode.(CallNode).getArg(0) = fromnode
or
exists(StringObject key, string text |
tonode.(CallNode).getFunction().(AttrNode).getObject("get") = fromnode and
tonode.(CallNode).getArg(0).refersTo(key)
or
tonode.(SubscriptNode).getValue() = fromnode and tonode.isLoad() and
tonode.(SubscriptNode).getIndex().refersTo(key)
|
text = key.getText() and result instanceof ExternalStringKind and
(
text = "QUERY_STRING" or
text = "PATH_INFO" or
text.prefix(5) = "HTTP_"
)
)
}
}
/** A standard morsel object from a HTTP request, a value in a cookie,
* typically an instance of `http.cookies.Morsel` */
class UntrustedMorsel extends TaintKind {
UntrustedMorsel() {
this = "http.Morsel"
}
override TaintKind getTaintOfAttribute(string name) {
result instanceof ExternalStringKind and
(
name = "value"
)
}
}
/** A standard cookie object from a HTTP request, typically an instance of `http.cookies.SimpleCookie` */
class UntrustedCookie extends TaintKind {
UntrustedCookie() {
this = "http.Cookie"
}
override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
tonode.(SubscriptNode).getValue() = fromnode and
result instanceof UntrustedMorsel
}
}

View File

@@ -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

View File

@@ -3,3 +3,7 @@ import semmle.python.web.flask.Request
import semmle.python.web.tornado.Request
import semmle.python.web.pyramid.Request
import semmle.python.web.twisted.Request
import semmle.python.web.bottle.Request
import semmle.python.web.turbogears.Request
import semmle.python.web.falcon.Request
import semmle.python.web.cherrypy.Request

View File

@@ -3,3 +3,7 @@ import semmle.python.web.flask.Response
import semmle.python.web.pyramid.Response
import semmle.python.web.tornado.Response
import semmle.python.web.twisted.Response
import semmle.python.web.bottle.Response
import semmle.python.web.turbogears.Response
import semmle.python.web.falcon.Response
import semmle.python.web.cherrypy.Response

View 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)
}
}

View 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
}
}

View 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"
}
}

View File

@@ -0,0 +1,58 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.security.strings.Untrusted
import semmle.python.web.Http
import semmle.python.web.bottle.General
/** A bottle.Response object
* This isn't really a "taint", but we use the value tracking machinery to
* track the flow of response objects.
*/
class BottleResponse extends TaintKind {
BottleResponse() {
this = "bottle.response"
}
}
private Object theBottleResponseObject() {
result = theBottleModule().attr("response")
}
class BottleResponseBodyAssignment extends TaintSink {
BottleResponseBodyAssignment() {
exists(DefinitionNode lhs |
lhs.getValue() = this and
lhs.(AttrNode).getObject("body").refersTo(theBottleResponseObject())
)
}
override predicate sinks(TaintKind kind) {
kind instanceof StringKind
}
}
class BottleHandlerFunctionResult extends TaintSink {
BottleHandlerFunctionResult() {
exists(BottleRoute route, Return ret |
ret.getScope() = route.getFunction() and
ret.getValue().getAFlowNode() = this
)
}
override predicate sinks(TaintKind kind) {
kind instanceof StringKind
}
override string toString() {
result = "bottle handler function result"
}
}

View File

@@ -0,0 +1,56 @@
import python
import semmle.python.web.Http
module CherryPy {
FunctionObject expose() {
result = ModuleObject::named("cherrypy").attr("expose")
}
}
class CherryPyExposedFunction extends Function {
CherryPyExposedFunction() {
this.getADecorator().refersTo(CherryPy::expose())
or
this.getADecorator().(Call).getFunc().refersTo(CherryPy::expose())
}
}
class CherryPyRoute extends CallNode {
CherryPyRoute() {
/* cherrypy.quickstart(root, script_name, config) */
ModuleObject::named("cherrypy").attr("quickstart").(FunctionObject).getACall() = this
or
/* cherrypy.tree.mount(root, script_name, config) */
this.getFunction().(AttrNode).getObject("mount").refersTo(ModuleObject::named("cherrypy").attr("tree"))
}
ClassObject getAppClass() {
this.getArg(0).refersTo(_, result, _)
or
this.getArgByName("root").refersTo(_, result, _)
}
string getPath() {
exists(StringObject path |
result = path.getText()
|
this.getArg(1).refersTo(path)
or
this.getArgByName("script_name").refersTo(path)
)
}
Object getConfig() {
this.getArg(2).refersTo(_, result, _)
or
this.getArgByName("config").refersTo(_, result, _)
}
}

View File

@@ -0,0 +1,69 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.security.strings.Basic
import semmle.python.web.Http
import semmle.python.web.cherrypy.General
/** The cherrypy.request local-proxy object */
class CherryPyRequest extends TaintKind {
CherryPyRequest() {
this = "cherrypy.request"
}
override TaintKind getTaintOfAttribute(string name) {
name = "params" and result instanceof ExternalStringDictKind
or
name = "cookie" and result instanceof UntrustedCookie
}
override TaintKind getTaintOfMethodResult(string name) {
(
name = "getHeader" or
name = "getCookie" or
name = "getUser" or
name = "getPassword"
) and
result instanceof ExternalStringKind
}
}
class CherryPyExposedFunctionParameter extends TaintSource {
CherryPyExposedFunctionParameter() {
exists(Parameter p |
p = any(CherryPyExposedFunction f).getAnArg() and
not p.isSelf() and
p.asName().getAFlowNode() = this
)
}
override string toString() {
result = "CherryPy handler function parameter"
}
override predicate isSourceOf(TaintKind kind) {
kind instanceof ExternalStringKind
}
}
class CherryPyRequestSource extends TaintSource {
CherryPyRequestSource() {
this.(ControlFlowNode).refersTo(ModuleObject::named("cherrypy").attr("request"))
}
override predicate isSourceOf(TaintKind kind) {
kind instanceof CherryPyRequest
}
}

View File

@@ -0,0 +1,28 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.security.strings.Untrusted
import semmle.python.web.Http
import semmle.python.web.cherrypy.General
class CherryPyExposedFunctionResult extends TaintSink {
CherryPyExposedFunctionResult() {
exists(Return ret |
ret.getScope() instanceof CherryPyExposedFunction and
ret.getValue().getAFlowNode() = this
)
}
override predicate sinks(TaintKind kind) {
kind instanceof StringKind
}
override string toString() {
result = "cherrypy handler function result"
}
}

View File

@@ -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")
}
/**

View File

@@ -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()
}
}

View File

@@ -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)
)

View File

@@ -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()
}

View File

@@ -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")
}

View File

@@ -0,0 +1,68 @@
import python
import semmle.python.web.Http
/** The falcon API class */
ClassObject theFalconAPIClass() {
result = ModuleObject::named("falcon").getAttribute("API")
}
/** Holds if `route` is routed to `resource`
*/
private predicate api_route(CallNode route_call, ControlFlowNode route, ClassObject resource) {
route_call.getFunction().(AttrNode).getObject("add_route").refersTo(_, theFalconAPIClass(), _) and
route_call.getArg(0) = route and
route_call.getArg(1).refersTo(_, resource, _)
}
private predicate route(FalconRoute route, Function target, string funcname) {
route.getResourceClass().lookupAttribute("on_" + funcname).(FunctionObject).getFunction() = target
}
class FalconRoute extends ControlFlowNode {
FalconRoute() {
api_route(this, _, _)
}
string getUrl() {
exists(StrConst url |
api_route(this, url.getAFlowNode(), _) and
result = url.getText()
)
}
ClassObject getResourceClass() {
api_route(this, _, result)
}
FalconHandlerFunction getHandlerFunction(string method) {
route(this, result, method)
}
}
class FalconHandlerFunction extends Function {
FalconHandlerFunction() {
route(_, this, _)
}
private string methodName() {
route(_, this, result)
}
string getMethod() {
result = this.methodName().toUpperCase()
}
Parameter getRequest() {
result = this.getArg(1)
}
Parameter getResponse() {
result = this.getArg(2)
}
}

View File

@@ -0,0 +1,56 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.web.Http
import semmle.python.web.falcon.General
import semmle.python.security.strings.External
/** https://falcon.readthedocs.io/en/stable/api/request_and_response.html */
class FalconRequest extends TaintKind {
FalconRequest() {
this = "falcon.request"
}
override TaintKind getTaintOfAttribute(string name) {
name = "env" and result instanceof WsgiEnvironment
or
result instanceof ExternalStringKind and
(
name = "uri" or name = "url" or
name = "forwarded_uri" or
name = "relative_uri" or
name = "query_string"
)
or
result instanceof ExternalStringDictKind and
(
name = "cookies" or name = "params"
)
or
name = "stream" and result instanceof ExternalFileObject
}
override TaintKind getTaintOfMethodResult(string name) {
name = "get_param" and result instanceof ExternalStringKind
or
name = "get_param_as_json" and result instanceof ExternalJsonKind
or
name = "get_param_as_list" and result instanceof ExternalStringSequenceKind
}
}
class FalconRequestParameter extends TaintSource {
FalconRequestParameter() {
exists(FalconHandlerFunction f |
f.getRequest() = this.(ControlFlowNode).getNode()
)
}
override predicate isSourceOf(TaintKind k) {
k instanceof FalconRequest
}
}

View File

@@ -0,0 +1,48 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.web.Http
import semmle.python.web.falcon.General
import semmle.python.security.strings.External
/** https://falcon.readthedocs.io/en/stable/api/request_and_response.html */
class FalconResponse extends TaintKind {
FalconResponse() {
this = "falcon.response"
}
}
class FalconResponseParameter extends TaintSource {
FalconResponseParameter() {
exists(FalconHandlerFunction f |
f.getResponse() = this.(ControlFlowNode).getNode()
)
}
override predicate isSourceOf(TaintKind k) {
k instanceof FalconResponse
}
}
class FalconResponseBodySink extends TaintSink {
FalconResponseBodySink() {
exists(AttrNode attr |
any(FalconResponse f).taints(attr.getObject("body")) |
attr.(DefinitionNode).getValue() = this
)
}
override predicate sinks(TaintKind kind) {
kind instanceof StringKind
}
}

View File

@@ -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`

View File

@@ -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")
}
/**

View File

@@ -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")
}

View File

@@ -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
)
}

View File

@@ -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")
}
}

View File

@@ -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) {

View File

@@ -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() {

View File

@@ -0,0 +1,33 @@
import python
import semmle.python.security.strings.Untrusted
import TurboGears
private class ValidatedMethodParameter extends Parameter {
ValidatedMethodParameter() {
exists(string name, TurboGearsControllerMethod method |
method.getArgByName(name) = this and
method.getValidationDict().getItem(_).(KeyValuePair).getKey().(StrConst).getText() = name
)
}
}
class UnvalidatedControllerMethodParameter extends TaintSource {
UnvalidatedControllerMethodParameter() {
exists(Parameter p |
any(TurboGearsControllerMethod m | not m.getName() = "onerror").getAnArg() = p and
not p instanceof ValidatedMethodParameter and
not p.isSelf() and
p.(Name).getAFlowNode() = this
)
}
override predicate isSourceOf(TaintKind kind) {
kind instanceof UntrustedStringKind
}
}

View File

@@ -0,0 +1,38 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.security.strings.Basic
import TurboGears
class ControllerMethodReturnValue extends TaintSink {
ControllerMethodReturnValue() {
exists(TurboGearsControllerMethod m |
m.getAReturnValueFlowNode() = this and
not m.isTemplated()
)
}
override predicate sinks(TaintKind kind) {
kind instanceof StringKind
}
}
class ControllerMethodTemplatedReturnValue extends TaintSink {
ControllerMethodTemplatedReturnValue() {
exists(TurboGearsControllerMethod m |
m.getAReturnValueFlowNode() = this and
m.isTemplated()
)
}
override predicate sinks(TaintKind kind) {
kind instanceof StringDictKind
}
}

View File

@@ -0,0 +1,55 @@
import python
import semmle.python.security.TaintTracking
private ClassObject theTurboGearsControllerClass() {
result = ModuleObject::named("tg").getAttribute("TGController")
}
ClassObject aTurboGearsControllerClass() {
result.getASuperType() = theTurboGearsControllerClass()
}
class TurboGearsControllerMethod extends Function {
ControlFlowNode decorator;
TurboGearsControllerMethod() {
aTurboGearsControllerClass().getPyClass() = this.getScope() and
decorator = this.getADecorator().getAFlowNode() and
/* Is decorated with @expose() or @expose(path) */
(
decorator.(CallNode).getFunction().(NameNode).getId() = "expose"
or
decorator.refersTo(_, ModuleObject::named("tg").getAttribute("expose"), _)
)
}
private ControlFlowNode templateName() {
result = decorator.(CallNode).getArg(0)
}
predicate isTemplated() {
exists(templateName())
}
string getTemplateName() {
exists(StringObject str |
templateName().refersTo(str) and
result = str.getText()
)
}
Dict getValidationDict() {
exists(Call call, Object dict |
call = this.getADecorator() and
call.getFunc().(Name).getId() = "validate" and
call.getArg(0).refersTo(dict) and
result = dict.getOrigin()
)
}
}

View File

@@ -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() {

View File

@@ -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")
}
}

View File

@@ -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;

View File

@@ -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>