mirror of
https://github.com/github/codeql.git
synced 2026-07-03 10:35:29 +02:00
Merge branch 'bazookamusic/python-prompt-injection' of https://github.com/github/codeql into bazookamusic/python-prompt-injection
This commit is contained in:
@@ -1,3 +1,16 @@
|
||||
## 7.2.0
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* The `Function.getAReturnValueFlowNode()` predicate has been deprecated. Bind a `Return` node explicitly instead — `exists(Return ret | ret.getScope() = f and n.getNode() = ret.getValue())`. This is a preparatory step towards migrating the dataflow library off the legacy CFG; it has no semantic effect.
|
||||
* The `AstNode.getAFlowNode()` predicate has been deprecated. Use `ControlFlowNode.getNode()` from the other direction instead: replace `e.getAFlowNode() = n` with `n.getNode() = e`. This is a preparatory step towards migrating the dataflow library off the legacy CFG; it has no semantic effect.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Python type tracking now follows values stored in instance attributes such as `self.attr` across instance methods, including across a class hierarchy (for example, a value stored on `self.attr` in a base class and read in a subclass, or vice versa). As a result, analysis is more likely to recognize user-defined objects that are stored on `self` and used later in other methods, which may produce additional results.
|
||||
* Simplified the internal predicates that detect `@staticmethod`, `@classmethod` and `@property` decorators to match the decorator's AST `Name` directly, rather than going through the CFG and requiring the name to resolve globally. Code that shadows these three builtin decorators at the module-scope will now be classified by the decorator name alone; in practice, shadowing these names is extremely rare and the call-graph results are unchanged.
|
||||
* Python taint tracking is now more precise for values flowing through container contents, such as list, set, tuple, and dictionary elements. This may remove some false positive alerts.
|
||||
|
||||
## 7.1.2
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
@@ -213,9 +213,11 @@ class ExprWithPointsTo extends Expr {
|
||||
* Gets what this expression might "refer-to" in the given `context`.
|
||||
*/
|
||||
predicate refersTo(Context context, Object obj, ClassObject cls, AstNode origin) {
|
||||
this.getAFlowNode()
|
||||
.(ControlFlowNodeWithPointsTo)
|
||||
.refersTo(context, obj, cls, origin.getAFlowNode())
|
||||
exists(ControlFlowNode this_, ControlFlowNode origin_ |
|
||||
this_.getNode() = this and origin_.getNode() = origin
|
||||
|
|
||||
this_.(ControlFlowNodeWithPointsTo).refersTo(context, obj, cls, origin_)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -226,7 +228,11 @@ class ExprWithPointsTo extends Expr {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate refersTo(Object obj, AstNode origin) {
|
||||
this.getAFlowNode().(ControlFlowNodeWithPointsTo).refersTo(obj, origin.getAFlowNode())
|
||||
exists(ControlFlowNode this_, ControlFlowNode origin_ |
|
||||
this_.getNode() = this and origin_.getNode() = origin
|
||||
|
|
||||
this_.(ControlFlowNodeWithPointsTo).refersTo(obj, origin_)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -240,16 +246,22 @@ class ExprWithPointsTo extends Expr {
|
||||
* in the given `context`.
|
||||
*/
|
||||
predicate pointsTo(Context context, Value value, AstNode origin) {
|
||||
this.getAFlowNode()
|
||||
.(ControlFlowNodeWithPointsTo)
|
||||
.pointsTo(context, value, origin.getAFlowNode())
|
||||
exists(ControlFlowNode this_, ControlFlowNode origin_ |
|
||||
this_.getNode() = this and origin_.getNode() = origin
|
||||
|
|
||||
this_.(ControlFlowNodeWithPointsTo).pointsTo(context, value, origin_)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this expression might "point-to" to `value` which is from `origin`.
|
||||
*/
|
||||
predicate pointsTo(Value value, AstNode origin) {
|
||||
this.getAFlowNode().(ControlFlowNodeWithPointsTo).pointsTo(value, origin.getAFlowNode())
|
||||
exists(ControlFlowNode this_, ControlFlowNode origin_ |
|
||||
this_.getNode() = this and origin_.getNode() = origin
|
||||
|
|
||||
this_.(ControlFlowNodeWithPointsTo).pointsTo(value, origin_)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -475,7 +487,10 @@ class FunctionMetricsWithPointsTo extends FunctionMetrics {
|
||||
not non_coupling_method(result) and
|
||||
exists(Call call | call.getScope() = this |
|
||||
exists(FunctionObject callee | callee.getFunction() = result |
|
||||
call.getAFlowNode().getFunction().(ControlFlowNodeWithPointsTo).refersTo(callee)
|
||||
exists(CallNode call_ |
|
||||
call_.getNode() = call and
|
||||
call_.getFunction().(ControlFlowNodeWithPointsTo).refersTo(callee)
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(Attribute a | call.getFunc() = a |
|
||||
|
||||
@@ -64,7 +64,7 @@ private predicate jump_to_defn(ControlFlowNode use, Definition defn) {
|
||||
private predicate preferred_jump_to_defn(Expr use, Definition def) {
|
||||
not use instanceof ClassExpr and
|
||||
not use instanceof FunctionExpr and
|
||||
jump_to_defn(use.getAFlowNode(), def)
|
||||
exists(ControlFlowNode useNode | useNode.getNode() = use | jump_to_defn(useNode, def))
|
||||
}
|
||||
|
||||
private predicate unique_jump_to_defn(Expr use, Definition def) {
|
||||
@@ -452,7 +452,7 @@ private predicate self_parameter_jump_to_defn_attribute(
|
||||
* This exists primarily for testing use `getPreferredDefinition()` instead.
|
||||
*/
|
||||
Definition getADefinition(Expr use) {
|
||||
jump_to_defn(use.getAFlowNode(), result) and
|
||||
exists(ControlFlowNode useNode | useNode.getNode() = use | jump_to_defn(useNode, result)) and
|
||||
not use instanceof Call and
|
||||
not use.isArtificial() and
|
||||
// Not the use itself
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* `Flask::FlaskApp::instance()` will now also return instances of subclasses defined in the source tree. Previously, these were filtered out. `Flask::FlaskApp::classRef()` has been deprecated in favor of `Flask::FlaskApp::subclassRef()` since it already returned some subclasses.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Python taint tracking is now more precise for values flowing through container contents, such as list, set, tuple, and dictionary elements. This may remove some false positive alerts.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Simplified the internal predicates that detect `@staticmethod`, `@classmethod` and `@property` decorators to match the decorator's AST `Name` directly, rather than going through the CFG and requiring the name to resolve globally. Code that shadows these three builtin decorators at the module-scope will now be classified by the decorator name alone; in practice, shadowing these names is extremely rare and the call-graph results are unchanged.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Python type tracking now follows values stored in instance attributes such as `self.attr` across instance methods, including across a class hierarchy (for example, a value stored on `self.attr` in a base class and read in a subclass, or vice versa). As a result, analysis is more likely to recognize user-defined objects that are stored on `self` and used later in other methods, which may produce additional results.
|
||||
12
python/ql/lib/change-notes/released/7.2.0.md
Normal file
12
python/ql/lib/change-notes/released/7.2.0.md
Normal file
@@ -0,0 +1,12 @@
|
||||
## 7.2.0
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* The `Function.getAReturnValueFlowNode()` predicate has been deprecated. Bind a `Return` node explicitly instead — `exists(Return ret | ret.getScope() = f and n.getNode() = ret.getValue())`. This is a preparatory step towards migrating the dataflow library off the legacy CFG; it has no semantic effect.
|
||||
* The `AstNode.getAFlowNode()` predicate has been deprecated. Use `ControlFlowNode.getNode()` from the other direction instead: replace `e.getAFlowNode() = n` with `n.getNode() = e`. This is a preparatory step towards migrating the dataflow library off the legacy CFG; it has no semantic effect.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Python type tracking now follows values stored in instance attributes such as `self.attr` across instance methods, including across a class hierarchy (for example, a value stored on `self.attr` in a base class and read in a subclass, or vice versa). As a result, analysis is more likely to recognize user-defined objects that are stored on `self` and used later in other methods, which may produce additional results.
|
||||
* Simplified the internal predicates that detect `@staticmethod`, `@classmethod` and `@property` decorators to match the decorator's AST `Name` directly, rather than going through the CFG and requiring the name to resolve globally. Code that shadows these three builtin decorators at the module-scope will now be classified by the decorator name alone; in practice, shadowing these names is extremely rare and the call-graph results are unchanged.
|
||||
* Python taint tracking is now more precise for values flowing through container contents, such as list, set, tuple, and dictionary elements. This may remove some false positive alerts.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 7.1.2
|
||||
lastReleaseVersion: 7.2.0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/python-all
|
||||
version: 7.1.3-dev
|
||||
version: 7.2.1-dev
|
||||
groups: python
|
||||
dbscheme: semmlecode.python.dbscheme
|
||||
extractor: python
|
||||
|
||||
@@ -17,12 +17,17 @@ abstract class AstNode extends AstNode_ {
|
||||
abstract Scope getScope();
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `ControlFlowNode.getNode()` from the other direction instead;
|
||||
* that is, replace `e.getAFlowNode() = n` with `n.getNode() = e`. This API is
|
||||
* being removed to untangle the AST and CFG hierarchies in preparation for
|
||||
* migrating the dataflow library off the legacy CFG.
|
||||
*
|
||||
* Gets a flow node corresponding directly to this node.
|
||||
* NOTE: For some statements and other purely syntactic elements,
|
||||
* there may not be a `ControlFlowNode`
|
||||
* there may not be a `ControlFlowNode`.
|
||||
*/
|
||||
cached
|
||||
ControlFlowNode getAFlowNode() {
|
||||
deprecated ControlFlowNode getAFlowNode() {
|
||||
Stages::AST::ref() and
|
||||
py_flow_bb_node(result, this, _, _)
|
||||
}
|
||||
|
||||
@@ -28,7 +28,9 @@ class Expr extends Expr_, AstNode {
|
||||
/** Whether this expression may have a side effect (as determined purely from its syntax) */
|
||||
predicate hasSideEffects() {
|
||||
/* If an exception raised by this expression handled, count that as a side effect */
|
||||
this.getAFlowNode().getASuccessor().getNode() instanceof ExceptStmt
|
||||
exists(ControlFlowNode n | n.getNode() = this |
|
||||
n.getASuccessor().getNode() instanceof ExceptStmt
|
||||
)
|
||||
or
|
||||
this.getASubExpression().hasSideEffects()
|
||||
}
|
||||
@@ -68,7 +70,7 @@ class Attribute extends Attribute_ {
|
||||
/* syntax: Expr.name */
|
||||
override Expr getASubExpression() { result = this.getObject() }
|
||||
|
||||
override AttrNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
deprecated override AttrNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
|
||||
/** Gets the name of this attribute. That is the `name` in `obj.name` */
|
||||
string getName() { result = Attribute_.super.getAttr() }
|
||||
@@ -97,7 +99,7 @@ class Subscript extends Subscript_ {
|
||||
|
||||
Expr getObject() { result = Subscript_.super.getValue() }
|
||||
|
||||
override SubscriptNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
deprecated override SubscriptNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
}
|
||||
|
||||
/** A call expression, such as `func(...)` */
|
||||
@@ -113,7 +115,7 @@ class Call extends Call_ {
|
||||
|
||||
override string toString() { result = this.getFunc().toString() + "()" }
|
||||
|
||||
override CallNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
deprecated override CallNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
|
||||
/** Gets a tuple (*) argument of this call. */
|
||||
Expr getStarargs() { result = this.getAPositionalArg().(Starred).getValue() }
|
||||
@@ -201,7 +203,7 @@ class IfExp extends IfExp_ {
|
||||
result = this.getTest() or result = this.getBody() or result = this.getOrelse()
|
||||
}
|
||||
|
||||
override IfExprNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
deprecated override IfExprNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
}
|
||||
|
||||
/** A starred expression, such as the `*rest` in the assignment `first, *rest = seq` */
|
||||
@@ -411,7 +413,7 @@ class PlaceHolder extends PlaceHolder_ {
|
||||
|
||||
override string toString() { result = "$" + this.getId() }
|
||||
|
||||
override NameNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
deprecated override NameNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
}
|
||||
|
||||
/** A tuple expression such as `( 1, 3, 5, 7, 9 )` */
|
||||
@@ -478,7 +480,7 @@ class Name extends Name_ {
|
||||
|
||||
override string toString() { result = this.getId() }
|
||||
|
||||
override NameNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
deprecated override NameNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
|
||||
override predicate isArtificial() {
|
||||
/* Artificial variable names in comprehensions all start with "." */
|
||||
@@ -585,7 +587,7 @@ abstract class NameConstant extends Name, ImmutableLiteral {
|
||||
|
||||
override predicate isConstant() { any() }
|
||||
|
||||
override NameConstantNode getAFlowNode() { result = Name.super.getAFlowNode() }
|
||||
deprecated override NameConstantNode getAFlowNode() { result = Name.super.getAFlowNode() }
|
||||
|
||||
override predicate isArtificial() { none() }
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import python
|
||||
import python as Py
|
||||
private import semmle.python.internal.CachedStages
|
||||
private import codeql.controlflow.BasicBlock as BB
|
||||
|
||||
@@ -17,7 +17,7 @@ private import codeql.controlflow.BasicBlock as BB
|
||||
*/
|
||||
|
||||
private predicate augstore(ControlFlowNode load, ControlFlowNode store) {
|
||||
exists(Expr load_store | exists(AugAssign aa | aa.getTarget() = load_store) |
|
||||
exists(Py::Expr load_store | exists(Py::AugAssign aa | aa.getTarget() = load_store) |
|
||||
toAst(load) = load_store and
|
||||
toAst(store) = load_store and
|
||||
load.strictlyDominates(store)
|
||||
@@ -25,7 +25,7 @@ private predicate augstore(ControlFlowNode load, ControlFlowNode store) {
|
||||
}
|
||||
|
||||
/** A non-dispatched getNode() to avoid negative recursion issues */
|
||||
private AstNode toAst(ControlFlowNode n) { py_flow_bb_node(n, result, _, _) }
|
||||
private Py::AstNode toAst(ControlFlowNode n) { py_flow_bb_node(n, result, _, _) }
|
||||
|
||||
/**
|
||||
* A control flow node. Control flow nodes have a many-to-one relation with syntactic nodes,
|
||||
@@ -35,19 +35,19 @@ private AstNode toAst(ControlFlowNode n) { py_flow_bb_node(n, result, _, _) }
|
||||
class ControlFlowNode extends @py_flow_node {
|
||||
/** Whether this control flow node is a load (including those in augmented assignments) */
|
||||
predicate isLoad() {
|
||||
exists(Expr e | e = toAst(this) | py_expr_contexts(_, 3, e) and not augstore(_, this))
|
||||
exists(Py::Expr e | e = toAst(this) | py_expr_contexts(_, 3, e) and not augstore(_, this))
|
||||
}
|
||||
|
||||
/** Whether this control flow node is a store (including those in augmented assignments) */
|
||||
predicate isStore() {
|
||||
exists(Expr e | e = toAst(this) | py_expr_contexts(_, 5, e) or augstore(_, this))
|
||||
exists(Py::Expr e | e = toAst(this) | py_expr_contexts(_, 5, e) or augstore(_, this))
|
||||
}
|
||||
|
||||
/** Whether this control flow node is a delete */
|
||||
predicate isDelete() { exists(Expr e | e = toAst(this) | py_expr_contexts(_, 2, e)) }
|
||||
predicate isDelete() { exists(Py::Expr e | e = toAst(this) | py_expr_contexts(_, 2, e)) }
|
||||
|
||||
/** Whether this control flow node is a parameter */
|
||||
predicate isParameter() { exists(Expr e | e = toAst(this) | py_expr_contexts(_, 4, e)) }
|
||||
predicate isParameter() { exists(Py::Expr e | e = toAst(this) | py_expr_contexts(_, 4, e)) }
|
||||
|
||||
/** Whether this control flow node is a store in an augmented assignment */
|
||||
predicate isAugStore() { augstore(_, this) }
|
||||
@@ -57,61 +57,61 @@ class ControlFlowNode extends @py_flow_node {
|
||||
|
||||
/** Whether this flow node corresponds to a literal */
|
||||
predicate isLiteral() {
|
||||
toAst(this) instanceof Bytes
|
||||
toAst(this) instanceof Py::Bytes
|
||||
or
|
||||
toAst(this) instanceof Dict
|
||||
toAst(this) instanceof Py::Dict
|
||||
or
|
||||
toAst(this) instanceof DictComp
|
||||
toAst(this) instanceof Py::DictComp
|
||||
or
|
||||
toAst(this) instanceof Set
|
||||
toAst(this) instanceof Py::Set
|
||||
or
|
||||
toAst(this) instanceof SetComp
|
||||
toAst(this) instanceof Py::SetComp
|
||||
or
|
||||
toAst(this) instanceof Ellipsis
|
||||
toAst(this) instanceof Py::Ellipsis
|
||||
or
|
||||
toAst(this) instanceof GeneratorExp
|
||||
toAst(this) instanceof Py::GeneratorExp
|
||||
or
|
||||
toAst(this) instanceof Lambda
|
||||
toAst(this) instanceof Py::Lambda
|
||||
or
|
||||
toAst(this) instanceof ListComp
|
||||
toAst(this) instanceof Py::ListComp
|
||||
or
|
||||
toAst(this) instanceof List
|
||||
toAst(this) instanceof Py::List
|
||||
or
|
||||
toAst(this) instanceof Num
|
||||
toAst(this) instanceof Py::Num
|
||||
or
|
||||
toAst(this) instanceof Tuple
|
||||
toAst(this) instanceof Py::Tuple
|
||||
or
|
||||
toAst(this) instanceof Unicode
|
||||
toAst(this) instanceof Py::Unicode
|
||||
or
|
||||
toAst(this) instanceof NameConstant
|
||||
toAst(this) instanceof Py::NameConstant
|
||||
}
|
||||
|
||||
/** Whether this flow node corresponds to an attribute expression */
|
||||
predicate isAttribute() { toAst(this) instanceof Attribute }
|
||||
predicate isAttribute() { toAst(this) instanceof Py::Attribute }
|
||||
|
||||
/** Whether this flow node corresponds to an subscript expression */
|
||||
predicate isSubscript() { toAst(this) instanceof Subscript }
|
||||
predicate isSubscript() { toAst(this) instanceof Py::Subscript }
|
||||
|
||||
/** Whether this flow node corresponds to an import member */
|
||||
predicate isImportMember() { toAst(this) instanceof ImportMember }
|
||||
predicate isImportMember() { toAst(this) instanceof Py::ImportMember }
|
||||
|
||||
/** Whether this flow node corresponds to a call */
|
||||
predicate isCall() { toAst(this) instanceof Call }
|
||||
predicate isCall() { toAst(this) instanceof Py::Call }
|
||||
|
||||
/** Whether this flow node is the first in a module */
|
||||
predicate isModuleEntry() { this.isEntryNode() and toAst(this) instanceof Module }
|
||||
predicate isModuleEntry() { this.isEntryNode() and toAst(this) instanceof Py::Module }
|
||||
|
||||
/** Whether this flow node corresponds to an import */
|
||||
predicate isImport() { toAst(this) instanceof ImportExpr }
|
||||
predicate isImport() { toAst(this) instanceof Py::ImportExpr }
|
||||
|
||||
/** Whether this flow node corresponds to a conditional expression */
|
||||
predicate isIfExp() { toAst(this) instanceof IfExp }
|
||||
predicate isIfExp() { toAst(this) instanceof Py::IfExp }
|
||||
|
||||
/** Whether this flow node corresponds to a function definition expression */
|
||||
predicate isFunction() { toAst(this) instanceof FunctionExpr }
|
||||
predicate isFunction() { toAst(this) instanceof Py::FunctionExpr }
|
||||
|
||||
/** Whether this flow node corresponds to a class definition expression */
|
||||
predicate isClass() { toAst(this) instanceof ClassExpr }
|
||||
predicate isClass() { toAst(this) instanceof Py::ClassExpr }
|
||||
|
||||
/** Gets a predecessor of this flow node */
|
||||
ControlFlowNode getAPredecessor() { this = result.getASuccessor() }
|
||||
@@ -123,25 +123,25 @@ class ControlFlowNode extends @py_flow_node {
|
||||
ControlFlowNode getImmediateDominator() { py_idoms(this, result) }
|
||||
|
||||
/** Gets the syntactic element corresponding to this flow node */
|
||||
AstNode getNode() { py_flow_bb_node(this, result, _, _) }
|
||||
Py::AstNode getNode() { py_flow_bb_node(this, result, _, _) }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
cached
|
||||
string toString() {
|
||||
Stages::AST::ref() and
|
||||
// Since modules can have ambigous names, entry nodes can too, if we do not collate them.
|
||||
exists(Scope s | s.getEntryNode() = this |
|
||||
exists(Py::Scope s | s.getEntryNode() = this |
|
||||
result = "Entry node for " + concat( | | s.toString(), ",")
|
||||
)
|
||||
or
|
||||
exists(Scope s | s.getANormalExit() = this | result = "Exit node for " + s.toString())
|
||||
exists(Py::Scope s | s.getANormalExit() = this | result = "Exit node for " + s.toString())
|
||||
or
|
||||
not exists(Scope s | s.getEntryNode() = this or s.getANormalExit() = this) and
|
||||
not exists(Py::Scope s | s.getEntryNode() = this or s.getANormalExit() = this) and
|
||||
result = "ControlFlowNode for " + this.getNode().toString()
|
||||
}
|
||||
|
||||
/** Gets the location of this ControlFlowNode */
|
||||
Location getLocation() { result = this.getNode().getLocation() }
|
||||
Py::Location getLocation() { result = this.getNode().getLocation() }
|
||||
|
||||
/** Whether this flow node is the first in its scope */
|
||||
predicate isEntryNode() { py_scope_flow(this, _, -1) }
|
||||
@@ -151,9 +151,9 @@ class ControlFlowNode extends @py_flow_node {
|
||||
|
||||
/** Gets the scope containing this flow node */
|
||||
cached
|
||||
Scope getScope() {
|
||||
Py::Scope getScope() {
|
||||
Stages::AST::ref() and
|
||||
if this.getNode() instanceof Scope
|
||||
if this.getNode() instanceof Py::Scope
|
||||
then
|
||||
/* Entry or exit node */
|
||||
result = this.getNode()
|
||||
@@ -161,7 +161,7 @@ class ControlFlowNode extends @py_flow_node {
|
||||
}
|
||||
|
||||
/** Gets the enclosing module */
|
||||
Module getEnclosingModule() { result = this.getScope().getEnclosingModule() }
|
||||
Py::Module getEnclosingModule() { result = this.getScope().getEnclosingModule() }
|
||||
|
||||
/** Gets a successor for this node if the relevant condition is True. */
|
||||
ControlFlowNode getATrueSuccessor() {
|
||||
@@ -188,7 +188,7 @@ class ControlFlowNode extends @py_flow_node {
|
||||
}
|
||||
|
||||
/** Whether the scope may be exited as a result of this node raising an exception */
|
||||
predicate isExceptionalExit(Scope s) { py_scope_flow(this, s, 1) }
|
||||
predicate isExceptionalExit(Py::Scope s) { py_scope_flow(this, s, 1) }
|
||||
|
||||
/** Whether this node is a normal (non-exceptional) exit */
|
||||
predicate isNormalExit() { py_scope_flow(this, _, 0) or py_scope_flow(this, _, 2) }
|
||||
@@ -236,7 +236,7 @@ class ControlFlowNode extends @py_flow_node {
|
||||
/* join-ordering helper for `getAChild() */
|
||||
pragma[noinline]
|
||||
private ControlFlowNode getExprChild(BasicBlock dom) {
|
||||
this.getNode().(Expr).getAChildNode() = result.getNode() and
|
||||
this.getNode().(Py::Expr).getAChildNode() = result.getNode() and
|
||||
result.getBasicBlock().dominates(dom) and
|
||||
not this instanceof UnaryExprNode
|
||||
}
|
||||
@@ -249,16 +249,16 @@ class ControlFlowNode extends @py_flow_node {
|
||||
*/
|
||||
|
||||
private class AnyNode extends ControlFlowNode {
|
||||
override AstNode getNode() { result = super.getNode() }
|
||||
override Py::AstNode getNode() { result = super.getNode() }
|
||||
}
|
||||
|
||||
/** A control flow node corresponding to a call expression, such as `func(...)` */
|
||||
class CallNode extends ControlFlowNode {
|
||||
CallNode() { toAst(this) instanceof Call }
|
||||
CallNode() { toAst(this) instanceof Py::Call }
|
||||
|
||||
/** Gets the flow node corresponding to the function expression for the call corresponding to this flow node */
|
||||
ControlFlowNode getFunction() {
|
||||
exists(Call c |
|
||||
exists(Py::Call c |
|
||||
this.getNode() = c and
|
||||
c.getFunc() = result.getNode() and
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
@@ -267,7 +267,7 @@ class CallNode extends ControlFlowNode {
|
||||
|
||||
/** Gets the flow node corresponding to the n'th positional argument of the call corresponding to this flow node */
|
||||
ControlFlowNode getArg(int n) {
|
||||
exists(Call c |
|
||||
exists(Py::Call c |
|
||||
this.getNode() = c and
|
||||
c.getArg(n) = result.getNode() and
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
@@ -276,7 +276,7 @@ class CallNode extends ControlFlowNode {
|
||||
|
||||
/** Gets the flow node corresponding to the named argument of the call corresponding to this flow node */
|
||||
ControlFlowNode getArgByName(string name) {
|
||||
exists(Call c, Keyword k |
|
||||
exists(Py::Call c, Py::Keyword k |
|
||||
this.getNode() = c and
|
||||
k = c.getANamedArg() and
|
||||
k.getValue() = result.getNode() and
|
||||
@@ -292,7 +292,7 @@ class CallNode extends ControlFlowNode {
|
||||
result = this.getArgByName(_)
|
||||
}
|
||||
|
||||
override Call getNode() { result = super.getNode() }
|
||||
override Py::Call getNode() { result = super.getNode() }
|
||||
|
||||
predicate isDecoratorCall() {
|
||||
this.isClassDecoratorCall()
|
||||
@@ -301,11 +301,11 @@ class CallNode extends ControlFlowNode {
|
||||
}
|
||||
|
||||
predicate isClassDecoratorCall() {
|
||||
exists(ClassExpr cls | this.getNode() = cls.getADecoratorCall())
|
||||
exists(Py::ClassExpr cls | this.getNode() = cls.getADecoratorCall())
|
||||
}
|
||||
|
||||
predicate isFunctionDecoratorCall() {
|
||||
exists(FunctionExpr func | this.getNode() = func.getADecoratorCall())
|
||||
exists(Py::FunctionExpr func | this.getNode() = func.getADecoratorCall())
|
||||
}
|
||||
|
||||
/** Gets the first tuple (*) argument of this call, if any. */
|
||||
@@ -323,11 +323,11 @@ class CallNode extends ControlFlowNode {
|
||||
|
||||
/** A control flow corresponding to an attribute expression, such as `value.attr` */
|
||||
class AttrNode extends ControlFlowNode {
|
||||
AttrNode() { toAst(this) instanceof Attribute }
|
||||
AttrNode() { toAst(this) instanceof Py::Attribute }
|
||||
|
||||
/** Gets the flow node corresponding to the object of the attribute expression corresponding to this flow node */
|
||||
ControlFlowNode getObject() {
|
||||
exists(Attribute a |
|
||||
exists(Py::Attribute a |
|
||||
this.getNode() = a and
|
||||
a.getObject() = result.getNode() and
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
@@ -339,7 +339,7 @@ class AttrNode extends ControlFlowNode {
|
||||
* with the matching name
|
||||
*/
|
||||
ControlFlowNode getObject(string name) {
|
||||
exists(Attribute a |
|
||||
exists(Py::Attribute a |
|
||||
this.getNode() = a and
|
||||
a.getObject() = result.getNode() and
|
||||
a.getName() = name and
|
||||
@@ -348,57 +348,57 @@ class AttrNode extends ControlFlowNode {
|
||||
}
|
||||
|
||||
/** Gets the attribute name of the attribute expression corresponding to this flow node */
|
||||
string getName() { exists(Attribute a | this.getNode() = a and a.getName() = result) }
|
||||
string getName() { exists(Py::Attribute a | this.getNode() = a and a.getName() = result) }
|
||||
|
||||
override Attribute getNode() { result = super.getNode() }
|
||||
override Py::Attribute getNode() { result = super.getNode() }
|
||||
}
|
||||
|
||||
/** A control flow node corresponding to a `from ... import ...` expression */
|
||||
class ImportMemberNode extends ControlFlowNode {
|
||||
ImportMemberNode() { toAst(this) instanceof ImportMember }
|
||||
ImportMemberNode() { toAst(this) instanceof Py::ImportMember }
|
||||
|
||||
/**
|
||||
* Gets the flow node corresponding to the module in the import-member expression corresponding to this flow node,
|
||||
* with the matching name
|
||||
*/
|
||||
ControlFlowNode getModule(string name) {
|
||||
exists(ImportMember i | this.getNode() = i and i.getModule() = result.getNode() |
|
||||
exists(Py::ImportMember i | this.getNode() = i and i.getModule() = result.getNode() |
|
||||
i.getName() = name and
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
)
|
||||
}
|
||||
|
||||
override ImportMember getNode() { result = super.getNode() }
|
||||
override Py::ImportMember getNode() { result = super.getNode() }
|
||||
}
|
||||
|
||||
/** A control flow node corresponding to an artificial expression representing an import */
|
||||
class ImportExprNode extends ControlFlowNode {
|
||||
ImportExprNode() { toAst(this) instanceof ImportExpr }
|
||||
ImportExprNode() { toAst(this) instanceof Py::ImportExpr }
|
||||
|
||||
override ImportExpr getNode() { result = super.getNode() }
|
||||
override Py::ImportExpr getNode() { result = super.getNode() }
|
||||
}
|
||||
|
||||
/** A control flow node corresponding to a `from ... import *` statement */
|
||||
class ImportStarNode extends ControlFlowNode {
|
||||
ImportStarNode() { toAst(this) instanceof ImportStar }
|
||||
ImportStarNode() { toAst(this) instanceof Py::ImportStar }
|
||||
|
||||
/** Gets the flow node corresponding to the module in the import-star corresponding to this flow node */
|
||||
ControlFlowNode getModule() {
|
||||
exists(ImportStar i | this.getNode() = i and i.getModuleExpr() = result.getNode() |
|
||||
exists(Py::ImportStar i | this.getNode() = i and i.getModuleExpr() = result.getNode() |
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
)
|
||||
}
|
||||
|
||||
override ImportStar getNode() { result = super.getNode() }
|
||||
override Py::ImportStar getNode() { result = super.getNode() }
|
||||
}
|
||||
|
||||
/** A control flow node corresponding to a subscript expression, such as `value[slice]` */
|
||||
class SubscriptNode extends ControlFlowNode {
|
||||
SubscriptNode() { toAst(this) instanceof Subscript }
|
||||
SubscriptNode() { toAst(this) instanceof Py::Subscript }
|
||||
|
||||
/** flow node corresponding to the value of the sequence in a subscript operation */
|
||||
ControlFlowNode getObject() {
|
||||
exists(Subscript s |
|
||||
exists(Py::Subscript s |
|
||||
this.getNode() = s and
|
||||
s.getObject() = result.getNode() and
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
@@ -407,23 +407,23 @@ class SubscriptNode extends ControlFlowNode {
|
||||
|
||||
/** flow node corresponding to the index in a subscript operation */
|
||||
ControlFlowNode getIndex() {
|
||||
exists(Subscript s |
|
||||
exists(Py::Subscript s |
|
||||
this.getNode() = s and
|
||||
s.getIndex() = result.getNode() and
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
)
|
||||
}
|
||||
|
||||
override Subscript getNode() { result = super.getNode() }
|
||||
override Py::Subscript getNode() { result = super.getNode() }
|
||||
}
|
||||
|
||||
/** A control flow node corresponding to a comparison operation, such as `x<y` */
|
||||
class CompareNode extends ControlFlowNode {
|
||||
CompareNode() { toAst(this) instanceof Compare }
|
||||
CompareNode() { toAst(this) instanceof Py::Compare }
|
||||
|
||||
/** Whether left and right are a pair of operands for this comparison */
|
||||
predicate operands(ControlFlowNode left, Cmpop op, ControlFlowNode right) {
|
||||
exists(Compare c, Expr eleft, Expr eright |
|
||||
predicate operands(ControlFlowNode left, Py::Cmpop op, ControlFlowNode right) {
|
||||
exists(Py::Compare c, Py::Expr eleft, Py::Expr eright |
|
||||
this.getNode() = c and left.getNode() = eleft and right.getNode() = eright
|
||||
|
|
||||
eleft = c.getLeft() and eright = c.getComparator(0) and op = c.getOp(0)
|
||||
@@ -436,26 +436,26 @@ class CompareNode extends ControlFlowNode {
|
||||
right.getBasicBlock().dominates(this.getBasicBlock())
|
||||
}
|
||||
|
||||
override Compare getNode() { result = super.getNode() }
|
||||
override Py::Compare getNode() { result = super.getNode() }
|
||||
}
|
||||
|
||||
/** A control flow node corresponding to a conditional expression such as, `body if test else orelse` */
|
||||
class IfExprNode extends ControlFlowNode {
|
||||
IfExprNode() { toAst(this) instanceof IfExp }
|
||||
IfExprNode() { toAst(this) instanceof Py::IfExp }
|
||||
|
||||
/** flow node corresponding to one of the operands of an if-expression */
|
||||
ControlFlowNode getAnOperand() { result = this.getAPredecessor() }
|
||||
|
||||
override IfExp getNode() { result = super.getNode() }
|
||||
override Py::IfExp getNode() { result = super.getNode() }
|
||||
}
|
||||
|
||||
/** A control flow node corresponding to an assignment expression such as `lhs := rhs`. */
|
||||
class AssignmentExprNode extends ControlFlowNode {
|
||||
AssignmentExprNode() { toAst(this) instanceof AssignExpr }
|
||||
AssignmentExprNode() { toAst(this) instanceof Py::AssignExpr }
|
||||
|
||||
/** Gets the flow node corresponding to the left-hand side of the assignment expression */
|
||||
ControlFlowNode getTarget() {
|
||||
exists(AssignExpr a |
|
||||
exists(Py::AssignExpr a |
|
||||
this.getNode() = a and
|
||||
a.getTarget() = result.getNode() and
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
@@ -464,27 +464,27 @@ class AssignmentExprNode extends ControlFlowNode {
|
||||
|
||||
/** Gets the flow node corresponding to the right-hand side of the assignment expression */
|
||||
ControlFlowNode getValue() {
|
||||
exists(AssignExpr a |
|
||||
exists(Py::AssignExpr a |
|
||||
this.getNode() = a and
|
||||
a.getValue() = result.getNode() and
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
)
|
||||
}
|
||||
|
||||
override AssignExpr getNode() { result = super.getNode() }
|
||||
override Py::AssignExpr getNode() { result = super.getNode() }
|
||||
}
|
||||
|
||||
/** A control flow node corresponding to a binary expression, such as `x + y` */
|
||||
class BinaryExprNode extends ControlFlowNode {
|
||||
BinaryExprNode() { toAst(this) instanceof BinaryExpr }
|
||||
BinaryExprNode() { toAst(this) instanceof Py::BinaryExpr }
|
||||
|
||||
/** flow node corresponding to one of the operands of a binary expression */
|
||||
ControlFlowNode getAnOperand() { result = this.getLeft() or result = this.getRight() }
|
||||
|
||||
override BinaryExpr getNode() { result = super.getNode() }
|
||||
override Py::BinaryExpr getNode() { result = super.getNode() }
|
||||
|
||||
ControlFlowNode getLeft() {
|
||||
exists(BinaryExpr b |
|
||||
exists(Py::BinaryExpr b |
|
||||
this.getNode() = b and
|
||||
result.getNode() = b.getLeft() and
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
@@ -492,7 +492,7 @@ class BinaryExprNode extends ControlFlowNode {
|
||||
}
|
||||
|
||||
ControlFlowNode getRight() {
|
||||
exists(BinaryExpr b |
|
||||
exists(Py::BinaryExpr b |
|
||||
this.getNode() = b and
|
||||
result.getNode() = b.getRight() and
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
@@ -500,11 +500,11 @@ class BinaryExprNode extends ControlFlowNode {
|
||||
}
|
||||
|
||||
/** Gets the operator of this binary expression node. */
|
||||
Operator getOp() { result = this.getNode().getOp() }
|
||||
Py::Operator getOp() { result = this.getNode().getOp() }
|
||||
|
||||
/** Whether left and right are a pair of operands for this binary expression */
|
||||
predicate operands(ControlFlowNode left, Operator op, ControlFlowNode right) {
|
||||
exists(BinaryExpr b, Expr eleft, Expr eright |
|
||||
predicate operands(ControlFlowNode left, Py::Operator op, ControlFlowNode right) {
|
||||
exists(Py::BinaryExpr b, Py::Expr eleft, Py::Expr eright |
|
||||
this.getNode() = b and left.getNode() = eleft and right.getNode() = eright
|
||||
|
|
||||
eleft = b.getLeft() and eright = b.getRight() and op = b.getOp()
|
||||
@@ -516,20 +516,20 @@ class BinaryExprNode extends ControlFlowNode {
|
||||
|
||||
/** A control flow node corresponding to a boolean shortcut (and/or) operation */
|
||||
class BoolExprNode extends ControlFlowNode {
|
||||
BoolExprNode() { toAst(this) instanceof BoolExpr }
|
||||
BoolExprNode() { toAst(this) instanceof Py::BoolExpr }
|
||||
|
||||
/** flow node corresponding to one of the operands of a boolean expression */
|
||||
ControlFlowNode getAnOperand() {
|
||||
exists(BoolExpr b | this.getNode() = b and result.getNode() = b.getAValue()) and
|
||||
exists(Py::BoolExpr b | this.getNode() = b and result.getNode() = b.getAValue()) and
|
||||
this.getBasicBlock().dominates(result.getBasicBlock())
|
||||
}
|
||||
|
||||
override BoolExpr getNode() { result = super.getNode() }
|
||||
override Py::BoolExpr getNode() { result = super.getNode() }
|
||||
}
|
||||
|
||||
/** A control flow node corresponding to a unary expression: (`+x`), (`-x`) or (`~x`) */
|
||||
class UnaryExprNode extends ControlFlowNode {
|
||||
UnaryExprNode() { toAst(this) instanceof UnaryExpr }
|
||||
UnaryExprNode() { toAst(this) instanceof Py::UnaryExpr }
|
||||
|
||||
/**
|
||||
* Gets flow node corresponding to the operand of a unary expression.
|
||||
@@ -540,7 +540,7 @@ class UnaryExprNode extends ControlFlowNode {
|
||||
*/
|
||||
ControlFlowNode getOperand() { result = this.getAPredecessor() }
|
||||
|
||||
override UnaryExpr getNode() { result = super.getNode() }
|
||||
override Py::UnaryExpr getNode() { result = super.getNode() }
|
||||
|
||||
override ControlFlowNode getAChild() { result = this.getAPredecessor() }
|
||||
}
|
||||
@@ -555,27 +555,27 @@ class DefinitionNode extends ControlFlowNode {
|
||||
cached
|
||||
DefinitionNode() {
|
||||
Stages::AST::ref() and
|
||||
exists(Assign a | a.getATarget().getAFlowNode() = this)
|
||||
exists(Py::Assign a | this.getNode() = a.getATarget())
|
||||
or
|
||||
exists(AssignExpr a | a.getTarget().getAFlowNode() = this)
|
||||
exists(Py::AssignExpr a | this.getNode() = a.getTarget())
|
||||
or
|
||||
exists(AnnAssign a | a.getTarget().getAFlowNode() = this and exists(a.getValue()))
|
||||
exists(Py::AnnAssign a | this.getNode() = a.getTarget() and exists(a.getValue()))
|
||||
or
|
||||
exists(Alias a | a.getAsname().getAFlowNode() = this)
|
||||
exists(Py::Alias a | this.getNode() = a.getAsname())
|
||||
or
|
||||
augstore(_, this)
|
||||
or
|
||||
// `x, y = 1, 2` where LHS is a combination of list or tuples
|
||||
exists(Assign a | list_or_tuple_nested_element(a.getATarget()).getAFlowNode() = this)
|
||||
exists(Py::Assign a | this.getNode() = list_or_tuple_nested_element(a.getATarget()))
|
||||
or
|
||||
exists(For for | for.getTarget().getAFlowNode() = this)
|
||||
exists(Py::For for | this.getNode() = for.getTarget())
|
||||
or
|
||||
exists(Parameter param | this = param.asName().getAFlowNode() and exists(param.getDefault()))
|
||||
exists(Py::Parameter param | this.getNode() = param.asName() and exists(param.getDefault()))
|
||||
}
|
||||
|
||||
/** flow node corresponding to the value assigned for the definition corresponding to this flow node */
|
||||
ControlFlowNode getValue() {
|
||||
result = assigned_value(this.getNode()).getAFlowNode() and
|
||||
result.getNode() = assigned_value(this.getNode()) and
|
||||
(
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
or
|
||||
@@ -584,16 +584,16 @@ class DefinitionNode extends ControlFlowNode {
|
||||
// since the default value for a parameter is evaluated in the same basic block as
|
||||
// the function definition, but the parameter belongs to the basic block of the function,
|
||||
// there is no dominance relationship between the two.
|
||||
exists(Parameter param | this = param.asName().getAFlowNode())
|
||||
exists(Py::Parameter param | this.getNode() = param.asName())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private Expr list_or_tuple_nested_element(Expr list_or_tuple) {
|
||||
exists(Expr elt |
|
||||
elt = list_or_tuple.(Tuple).getAnElt()
|
||||
private Py::Expr list_or_tuple_nested_element(Py::Expr list_or_tuple) {
|
||||
exists(Py::Expr elt |
|
||||
elt = list_or_tuple.(Py::Tuple).getAnElt()
|
||||
or
|
||||
elt = list_or_tuple.(List).getAnElt()
|
||||
elt = list_or_tuple.(Py::List).getAnElt()
|
||||
|
|
||||
result = elt
|
||||
or
|
||||
@@ -603,12 +603,12 @@ private Expr list_or_tuple_nested_element(Expr list_or_tuple) {
|
||||
|
||||
/**
|
||||
* A control flow node corresponding to a deletion statement, such as `del x`.
|
||||
* There can be multiple `DeletionNode`s for each `Delete` such that each
|
||||
* There can be multiple `DeletionNode`s for each `Py::Delete` such that each
|
||||
* target has own `DeletionNode`. The CFG for `del a, x.y` looks like:
|
||||
* `NameNode('a') -> DeletionNode -> NameNode('b') -> AttrNode('y') -> DeletionNode`.
|
||||
*/
|
||||
class DeletionNode extends ControlFlowNode {
|
||||
DeletionNode() { toAst(this) instanceof Delete }
|
||||
DeletionNode() { toAst(this) instanceof Py::Delete }
|
||||
|
||||
/** Gets the unique target of this deletion node. */
|
||||
ControlFlowNode getTarget() { result.getASuccessor() = this }
|
||||
@@ -617,9 +617,9 @@ class DeletionNode extends ControlFlowNode {
|
||||
/** A control flow node corresponding to a sequence (tuple or list) literal */
|
||||
abstract class SequenceNode extends ControlFlowNode {
|
||||
SequenceNode() {
|
||||
toAst(this) instanceof Tuple
|
||||
toAst(this) instanceof Py::Tuple
|
||||
or
|
||||
toAst(this) instanceof List
|
||||
toAst(this) instanceof Py::List
|
||||
}
|
||||
|
||||
/** Gets the control flow node for an element of this sequence */
|
||||
@@ -632,11 +632,11 @@ abstract class SequenceNode extends ControlFlowNode {
|
||||
|
||||
/** A control flow node corresponding to a tuple expression such as `( 1, 3, 5, 7, 9 )` */
|
||||
class TupleNode extends SequenceNode {
|
||||
TupleNode() { toAst(this) instanceof Tuple }
|
||||
TupleNode() { toAst(this) instanceof Py::Tuple }
|
||||
|
||||
override ControlFlowNode getElement(int n) {
|
||||
Stages::AST::ref() and
|
||||
exists(Tuple t | this.getNode() = t and result.getNode() = t.getElt(n)) and
|
||||
exists(Py::Tuple t | this.getNode() = t and result.getNode() = t.getElt(n)) and
|
||||
(
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
or
|
||||
@@ -647,10 +647,10 @@ class TupleNode extends SequenceNode {
|
||||
|
||||
/** A control flow node corresponding to a list expression, such as `[ 1, 3, 5, 7, 9 ]` */
|
||||
class ListNode extends SequenceNode {
|
||||
ListNode() { toAst(this) instanceof List }
|
||||
ListNode() { toAst(this) instanceof Py::List }
|
||||
|
||||
override ControlFlowNode getElement(int n) {
|
||||
exists(List l | this.getNode() = l and result.getNode() = l.getElt(n)) and
|
||||
exists(Py::List l | this.getNode() = l and result.getNode() = l.getElt(n)) and
|
||||
(
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
or
|
||||
@@ -661,10 +661,10 @@ class ListNode extends SequenceNode {
|
||||
|
||||
/** A control flow node corresponding to a set expression, such as `{ 1, 3, 5, 7, 9 }` */
|
||||
class SetNode extends ControlFlowNode {
|
||||
SetNode() { toAst(this) instanceof Set }
|
||||
SetNode() { toAst(this) instanceof Py::Set }
|
||||
|
||||
ControlFlowNode getAnElement() {
|
||||
exists(Set s | this.getNode() = s and result.getNode() = s.getElt(_)) and
|
||||
exists(Py::Set s | this.getNode() = s and result.getNode() = s.getElt(_)) and
|
||||
(
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
or
|
||||
@@ -675,20 +675,20 @@ class SetNode extends ControlFlowNode {
|
||||
|
||||
/** A control flow node corresponding to a dictionary literal, such as `{ 'a': 1, 'b': 2 }` */
|
||||
class DictNode extends ControlFlowNode {
|
||||
DictNode() { toAst(this) instanceof Dict }
|
||||
DictNode() { toAst(this) instanceof Py::Dict }
|
||||
|
||||
/**
|
||||
* Gets a key of this dictionary literal node, for those items that have keys
|
||||
* E.g, in {'a':1, **b} this returns only 'a'
|
||||
*/
|
||||
ControlFlowNode getAKey() {
|
||||
exists(Dict d | this.getNode() = d and result.getNode() = d.getAKey()) and
|
||||
exists(Py::Dict d | this.getNode() = d and result.getNode() = d.getAKey()) and
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
}
|
||||
|
||||
/** Gets a value of this dictionary literal node */
|
||||
ControlFlowNode getAValue() {
|
||||
exists(Dict d | this.getNode() = d and result.getNode() = d.getAValue()) and
|
||||
exists(Py::Dict d | this.getNode() = d and result.getNode() = d.getAValue()) and
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
}
|
||||
}
|
||||
@@ -712,21 +712,23 @@ class IterableNode extends ControlFlowNode {
|
||||
}
|
||||
}
|
||||
|
||||
private AstNode assigned_value(Expr lhs) {
|
||||
private Py::AstNode assigned_value(Py::Expr lhs) {
|
||||
/* lhs = result */
|
||||
exists(Assign a | a.getATarget() = lhs and result = a.getValue())
|
||||
exists(Py::Assign a | a.getATarget() = lhs and result = a.getValue())
|
||||
or
|
||||
/* lhs := result */
|
||||
exists(AssignExpr a | a.getTarget() = lhs and result = a.getValue())
|
||||
exists(Py::AssignExpr a | a.getTarget() = lhs and result = a.getValue())
|
||||
or
|
||||
/* lhs : annotation = result */
|
||||
exists(AnnAssign a | a.getTarget() = lhs and result = a.getValue())
|
||||
exists(Py::AnnAssign a | a.getTarget() = lhs and result = a.getValue())
|
||||
or
|
||||
/* import result as lhs */
|
||||
exists(Alias a | a.getAsname() = lhs and result = a.getValue())
|
||||
exists(Py::Alias a | a.getAsname() = lhs and result = a.getValue())
|
||||
or
|
||||
/* lhs += x => result = (lhs + x) */
|
||||
exists(AugAssign a, BinaryExpr b | b = a.getOperation() and result = b and lhs = b.getLeft())
|
||||
exists(Py::AugAssign a, Py::BinaryExpr b |
|
||||
b = a.getOperation() and result = b and lhs = b.getLeft()
|
||||
)
|
||||
or
|
||||
/*
|
||||
* ..., lhs, ... = ..., result, ...
|
||||
@@ -734,31 +736,31 @@ private AstNode assigned_value(Expr lhs) {
|
||||
* ..., (..., lhs, ...), ... = ..., (..., result, ...), ...
|
||||
*/
|
||||
|
||||
exists(Assign a | nested_sequence_assign(a.getATarget(), a.getValue(), lhs, result))
|
||||
exists(Py::Assign a | nested_sequence_assign(a.getATarget(), a.getValue(), lhs, result))
|
||||
or
|
||||
/* for lhs in seq: => `result` is the `for` node, representing the `iter(next(seq))` operation. */
|
||||
result.(For).getTarget() = lhs
|
||||
result.(Py::For).getTarget() = lhs
|
||||
or
|
||||
exists(Parameter param | lhs = param.asName() and result = param.getDefault())
|
||||
exists(Py::Parameter param | lhs = param.asName() and result = param.getDefault())
|
||||
}
|
||||
|
||||
predicate nested_sequence_assign(
|
||||
Expr left_parent, Expr right_parent, Expr left_result, Expr right_result
|
||||
Py::Expr left_parent, Py::Expr right_parent, Py::Expr left_result, Py::Expr right_result
|
||||
) {
|
||||
exists(Assign a |
|
||||
exists(Py::Assign a |
|
||||
a.getATarget().getASubExpression*() = left_parent and
|
||||
a.getValue().getASubExpression*() = right_parent
|
||||
) and
|
||||
exists(int i, Expr left_elem, Expr right_elem |
|
||||
exists(int i, Py::Expr left_elem, Py::Expr right_elem |
|
||||
(
|
||||
left_elem = left_parent.(Tuple).getElt(i)
|
||||
left_elem = left_parent.(Py::Tuple).getElt(i)
|
||||
or
|
||||
left_elem = left_parent.(List).getElt(i)
|
||||
left_elem = left_parent.(Py::List).getElt(i)
|
||||
) and
|
||||
(
|
||||
right_elem = right_parent.(Tuple).getElt(i)
|
||||
right_elem = right_parent.(Py::Tuple).getElt(i)
|
||||
or
|
||||
right_elem = right_parent.(List).getElt(i)
|
||||
right_elem = right_parent.(Py::List).getElt(i)
|
||||
)
|
||||
|
|
||||
left_result = left_elem and right_result = right_elem
|
||||
@@ -769,9 +771,9 @@ predicate nested_sequence_assign(
|
||||
|
||||
/** A flow node for a `for` statement. */
|
||||
class ForNode extends ControlFlowNode {
|
||||
ForNode() { toAst(this) instanceof For }
|
||||
ForNode() { toAst(this) instanceof Py::For }
|
||||
|
||||
override For getNode() { result = super.getNode() }
|
||||
override Py::For getNode() { result = super.getNode() }
|
||||
|
||||
/** Holds if this `for` statement causes iteration over `sequence` storing each step of the iteration in `target` */
|
||||
predicate iterates(ControlFlowNode target, ControlFlowNode sequence) {
|
||||
@@ -782,7 +784,7 @@ class ForNode extends ControlFlowNode {
|
||||
|
||||
/** Gets the sequence node for this `for` statement. */
|
||||
ControlFlowNode getSequence() {
|
||||
exists(For for |
|
||||
exists(Py::For for |
|
||||
toAst(this) = for and
|
||||
for.getIter() = result.getNode()
|
||||
|
|
||||
@@ -792,7 +794,7 @@ class ForNode extends ControlFlowNode {
|
||||
|
||||
/** A possible `target` for this `for` statement, not accounting for loop unrolling */
|
||||
private ControlFlowNode possibleTarget() {
|
||||
exists(For for |
|
||||
exists(Py::For for |
|
||||
toAst(this) = for and
|
||||
for.getTarget() = result.getNode() and
|
||||
this.getBasicBlock().dominates(result.getBasicBlock())
|
||||
@@ -809,11 +811,11 @@ class ForNode extends ControlFlowNode {
|
||||
|
||||
/** A flow node for a `raise` statement */
|
||||
class RaiseStmtNode extends ControlFlowNode {
|
||||
RaiseStmtNode() { toAst(this) instanceof Raise }
|
||||
RaiseStmtNode() { toAst(this) instanceof Py::Raise }
|
||||
|
||||
/** Gets the control flow node for the exception raised by this raise statement */
|
||||
ControlFlowNode getException() {
|
||||
exists(Raise r |
|
||||
exists(Py::Raise r |
|
||||
r = toAst(this) and
|
||||
r.getException() = toAst(result) and
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
@@ -827,36 +829,36 @@ class RaiseStmtNode extends ControlFlowNode {
|
||||
*/
|
||||
class NameNode extends ControlFlowNode {
|
||||
NameNode() {
|
||||
exists(Name n | py_flow_bb_node(this, n, _, _))
|
||||
exists(Py::Name n | py_flow_bb_node(this, n, _, _))
|
||||
or
|
||||
exists(PlaceHolder p | py_flow_bb_node(this, p, _, _))
|
||||
exists(Py::PlaceHolder p | py_flow_bb_node(this, p, _, _))
|
||||
}
|
||||
|
||||
/** Whether this flow node defines the variable `v`. */
|
||||
predicate defines(Variable v) {
|
||||
exists(Name d | this.getNode() = d and d.defines(v)) and
|
||||
predicate defines(Py::Variable v) {
|
||||
exists(Py::Name d | this.getNode() = d and d.defines(v)) and
|
||||
not this.isLoad()
|
||||
}
|
||||
|
||||
/** Whether this flow node deletes the variable `v`. */
|
||||
predicate deletes(Variable v) { exists(Name d | this.getNode() = d and d.deletes(v)) }
|
||||
predicate deletes(Py::Variable v) { exists(Py::Name d | this.getNode() = d and d.deletes(v)) }
|
||||
|
||||
/** Whether this flow node uses the variable `v`. */
|
||||
predicate uses(Variable v) {
|
||||
predicate uses(Py::Variable v) {
|
||||
this.isLoad() and
|
||||
exists(Name u | this.getNode() = u and u.uses(v))
|
||||
exists(Py::Name u | this.getNode() = u and u.uses(v))
|
||||
or
|
||||
exists(PlaceHolder u |
|
||||
this.getNode() = u and u.getVariable() = v and u.getCtx() instanceof Load
|
||||
exists(Py::PlaceHolder u |
|
||||
this.getNode() = u and u.getVariable() = v and u.getCtx() instanceof Py::Load
|
||||
)
|
||||
or
|
||||
Scopes::use_of_global_variable(this, v.getScope(), v.getId())
|
||||
}
|
||||
|
||||
string getId() {
|
||||
result = this.getNode().(Name).getId()
|
||||
result = this.getNode().(Py::Name).getId()
|
||||
or
|
||||
result = this.getNode().(PlaceHolder).getId()
|
||||
result = this.getNode().(Py::PlaceHolder).getId()
|
||||
}
|
||||
|
||||
/** Whether this is a use of a local variable. */
|
||||
@@ -868,82 +870,84 @@ class NameNode extends ControlFlowNode {
|
||||
/** Whether this is a use of a global (including builtin) variable. */
|
||||
predicate isGlobal() { Scopes::use_of_global_variable(this, _, _) }
|
||||
|
||||
predicate isSelf() { exists(SsaVariable selfvar | selfvar.isSelf() and selfvar.getAUse() = this) }
|
||||
predicate isSelf() {
|
||||
exists(Py::SsaVariable selfvar | selfvar.isSelf() and selfvar.getAUse() = this)
|
||||
}
|
||||
}
|
||||
|
||||
/** A control flow node corresponding to a named constant, one of `None`, `True` or `False`. */
|
||||
class NameConstantNode extends NameNode {
|
||||
NameConstantNode() { exists(NameConstant n | py_flow_bb_node(this, n, _, _)) }
|
||||
NameConstantNode() { exists(Py::NameConstant n | py_flow_bb_node(this, n, _, _)) }
|
||||
/*
|
||||
* We ought to override uses as well, but that has
|
||||
* a serious performance impact.
|
||||
* deprecated predicate uses(Variable v) { none() }
|
||||
* deprecated predicate uses(Py::Variable v) { none() }
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
/** A control flow node corresponding to a starred expression, `*a`. */
|
||||
class StarredNode extends ControlFlowNode {
|
||||
StarredNode() { toAst(this) instanceof Starred }
|
||||
StarredNode() { toAst(this) instanceof Py::Starred }
|
||||
|
||||
ControlFlowNode getValue() { toAst(result) = toAst(this).(Starred).getValue() }
|
||||
ControlFlowNode getValue() { toAst(result) = toAst(this).(Py::Starred).getValue() }
|
||||
}
|
||||
|
||||
/** The ControlFlowNode for an 'except' statement. */
|
||||
class ExceptFlowNode extends ControlFlowNode {
|
||||
ExceptFlowNode() { this.getNode() instanceof ExceptStmt }
|
||||
ExceptFlowNode() { this.getNode() instanceof Py::ExceptStmt }
|
||||
|
||||
/**
|
||||
* Gets the type handled by this exception handler.
|
||||
* `ExceptionType` in `except ExceptionType as e:`
|
||||
* `Py::ExceptionType` in `except Py::ExceptionType as e:`
|
||||
*/
|
||||
ControlFlowNode getType() {
|
||||
exists(ExceptStmt ex |
|
||||
exists(Py::ExceptStmt ex |
|
||||
this.getBasicBlock().dominates(result.getBasicBlock()) and
|
||||
ex = this.getNode() and
|
||||
result = ex.getType().getAFlowNode()
|
||||
result.getNode() = ex.getType()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name assigned to the handled exception, if any.
|
||||
* `e` in `except ExceptionType as e:`
|
||||
* `e` in `except Py::ExceptionType as e:`
|
||||
*/
|
||||
ControlFlowNode getName() {
|
||||
exists(ExceptStmt ex |
|
||||
exists(Py::ExceptStmt ex |
|
||||
this.getBasicBlock().dominates(result.getBasicBlock()) and
|
||||
ex = this.getNode() and
|
||||
result = ex.getName().getAFlowNode()
|
||||
result.getNode() = ex.getName()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** The ControlFlowNode for an 'except*' statement. */
|
||||
class ExceptGroupFlowNode extends ControlFlowNode {
|
||||
ExceptGroupFlowNode() { this.getNode() instanceof ExceptGroupStmt }
|
||||
ExceptGroupFlowNode() { this.getNode() instanceof Py::ExceptGroupStmt }
|
||||
|
||||
/**
|
||||
* Gets the type handled by this exception handler.
|
||||
* `ExceptionType` in `except* ExceptionType as e:`
|
||||
* `Py::ExceptionType` in `except* Py::ExceptionType as e:`
|
||||
*/
|
||||
ControlFlowNode getType() {
|
||||
this.getBasicBlock().dominates(result.getBasicBlock()) and
|
||||
result = this.getNode().(ExceptGroupStmt).getType().getAFlowNode()
|
||||
result.getNode() = this.getNode().(Py::ExceptGroupStmt).getType()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name assigned to the handled exception, if any.
|
||||
* `e` in `except* ExceptionType as e:`
|
||||
* `e` in `except* Py::ExceptionType as e:`
|
||||
*/
|
||||
ControlFlowNode getName() {
|
||||
this.getBasicBlock().dominates(result.getBasicBlock()) and
|
||||
result = this.getNode().(ExceptGroupStmt).getName().getAFlowNode()
|
||||
result.getNode() = this.getNode().(Py::ExceptGroupStmt).getName()
|
||||
}
|
||||
}
|
||||
|
||||
private module Scopes {
|
||||
private predicate fast_local(NameNode n) {
|
||||
exists(FastLocalVariable v |
|
||||
exists(Py::FastLocalVariable v |
|
||||
n.uses(v) and
|
||||
v.getScope() = n.getScope()
|
||||
)
|
||||
@@ -952,15 +956,15 @@ private module Scopes {
|
||||
predicate local(NameNode n) {
|
||||
fast_local(n)
|
||||
or
|
||||
exists(SsaVariable var |
|
||||
exists(Py::SsaVariable var |
|
||||
var.getAUse() = n and
|
||||
n.getScope() instanceof Class and
|
||||
n.getScope() instanceof Py::Class and
|
||||
exists(var.getDefinition())
|
||||
)
|
||||
}
|
||||
|
||||
predicate non_local(NameNode n) {
|
||||
exists(FastLocalVariable flv |
|
||||
exists(Py::FastLocalVariable flv |
|
||||
flv.getALoad() = n.getNode() and
|
||||
not flv.getScope() = n.getScope()
|
||||
)
|
||||
@@ -968,20 +972,20 @@ private module Scopes {
|
||||
|
||||
// magic is fine, but we get questionable join-ordering of it
|
||||
pragma[nomagic]
|
||||
predicate use_of_global_variable(NameNode n, Module scope, string name) {
|
||||
predicate use_of_global_variable(NameNode n, Py::Module scope, string name) {
|
||||
n.isLoad() and
|
||||
not non_local(n) and
|
||||
not exists(SsaVariable var | var.getAUse() = n |
|
||||
var.getVariable() instanceof FastLocalVariable
|
||||
not exists(Py::SsaVariable var | var.getAUse() = n |
|
||||
var.getVariable() instanceof Py::FastLocalVariable
|
||||
or
|
||||
n.getScope() instanceof Class and
|
||||
n.getScope() instanceof Py::Class and
|
||||
not maybe_undefined(var)
|
||||
) and
|
||||
name = n.getId() and
|
||||
scope = n.getEnclosingModule()
|
||||
}
|
||||
|
||||
private predicate maybe_undefined(SsaVariable var) {
|
||||
private predicate maybe_undefined(Py::SsaVariable var) {
|
||||
not exists(var.getDefinition()) and not py_ssa_phi(var, _)
|
||||
or
|
||||
var.getDefinition().isDelete()
|
||||
@@ -1058,13 +1062,13 @@ class BasicBlock extends @py_flow_node {
|
||||
private predicate oneNodeBlock() { this.firstNode() = this.getLastNode() }
|
||||
|
||||
private predicate startLocationInfo(string file, int line, int col) {
|
||||
if this.firstNode().getNode() instanceof Scope
|
||||
if this.firstNode().getNode() instanceof Py::Scope
|
||||
then this.firstNode().getASuccessor().getLocation().hasLocationInfo(file, line, col, _, _)
|
||||
else this.firstNode().getLocation().hasLocationInfo(file, line, col, _, _)
|
||||
}
|
||||
|
||||
private predicate endLocationInfo(int endl, int endc) {
|
||||
if this.getLastNode().getNode() instanceof Scope and not this.oneNodeBlock()
|
||||
if this.getLastNode().getNode() instanceof Py::Scope and not this.oneNodeBlock()
|
||||
then this.getLastNode().getAPredecessor().getLocation().hasLocationInfo(_, _, _, endl, endc)
|
||||
else this.getLastNode().getLocation().hasLocationInfo(_, _, _, endl, endc)
|
||||
}
|
||||
@@ -1081,7 +1085,7 @@ class BasicBlock extends @py_flow_node {
|
||||
|
||||
/** Whether flow from this basic block reaches a normal exit from its scope */
|
||||
predicate reachesExit() {
|
||||
exists(Scope s | s.getANormalExit().getBasicBlock() = this)
|
||||
exists(Py::Scope s | s.getANormalExit().getBasicBlock() = this)
|
||||
or
|
||||
this.getASuccessor().reachesExit()
|
||||
}
|
||||
@@ -1122,7 +1126,7 @@ class BasicBlock extends @py_flow_node {
|
||||
|
||||
/** Gets the scope of this block */
|
||||
pragma[nomagic]
|
||||
Scope getScope() {
|
||||
Py::Scope getScope() {
|
||||
exists(ControlFlowNode n | n.getBasicBlock() = this |
|
||||
/* Take care not to use an entry or exit node as that node's scope will be the outer scope */
|
||||
not py_scope_flow(n, _, -1) and
|
||||
@@ -1145,17 +1149,17 @@ class BasicBlock extends @py_flow_node {
|
||||
predicate reaches(BasicBlock other) { this = other or this.strictlyReaches(other) }
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Gets the `Py::ConditionBlock`, if any, that controls this block and
|
||||
* does not control any other `Py::ConditionBlock`s that control this block.
|
||||
* That is the `Py::ConditionBlock` that is closest dominator.
|
||||
*/
|
||||
ConditionBlock getImmediatelyControllingBlock() {
|
||||
Py::ConditionBlock getImmediatelyControllingBlock() {
|
||||
result = this.nonControllingImmediateDominator*().getImmediateDominator()
|
||||
}
|
||||
|
||||
private BasicBlock nonControllingImmediateDominator() {
|
||||
result = this.getImmediateDominator() and
|
||||
not result.(ConditionBlock).controls(this, _)
|
||||
not result.(Py::ConditionBlock).controls(this, _)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1175,7 +1179,7 @@ private class ControlFlowNodeAlias = ControlFlowNode;
|
||||
|
||||
final private class FinalBasicBlock = BasicBlock;
|
||||
|
||||
module Cfg implements BB::CfgSig<Location> {
|
||||
module Cfg implements BB::CfgSig<Py::Location> {
|
||||
private import codeql.controlflow.SuccessorType
|
||||
|
||||
class ControlFlowNode = ControlFlowNodeAlias;
|
||||
@@ -1186,7 +1190,7 @@ module Cfg implements BB::CfgSig<Location> {
|
||||
// Using the location of the first node is simple
|
||||
// and we just need a way to identify the basic block
|
||||
// during debugging, so this will be serviceable.
|
||||
Location getLocation() { result = super.getNode(0).getLocation() }
|
||||
Py::Location getLocation() { result = super.getNode(0).getLocation() }
|
||||
|
||||
int length() { result = count(int i | exists(this.getNode(i))) }
|
||||
|
||||
|
||||
@@ -153,8 +153,16 @@ class Function extends Function_, Scope, AstNode {
|
||||
|
||||
override predicate contains(AstNode inner) { Scope.super.contains(inner) }
|
||||
|
||||
/** Gets a control flow node for a return value of this function */
|
||||
ControlFlowNode getAReturnValueFlowNode() {
|
||||
/**
|
||||
* DEPRECATED: bind a `Return` node explicitly instead, e.g.
|
||||
* `exists(Return ret | ret.getScope() = this and n.getNode() = ret.getValue())`.
|
||||
* This API is being phased out together with `AstNode.getAFlowNode()` to
|
||||
* untangle the AST and CFG hierarchies in preparation for migrating the
|
||||
* dataflow library off the legacy CFG.
|
||||
*
|
||||
* Gets a control flow node for a return value of this function.
|
||||
*/
|
||||
deprecated ControlFlowNode getAReturnValueFlowNode() {
|
||||
exists(Return ret |
|
||||
ret.getScope() = this and
|
||||
ret.getValue() = result.getNode()
|
||||
|
||||
@@ -163,7 +163,7 @@ class ImportMember extends ImportMember_ {
|
||||
result = this.getModule().(ImportExpr).getImportedModuleName() + "." + this.getName()
|
||||
}
|
||||
|
||||
override ImportMemberNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
deprecated override ImportMemberNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
}
|
||||
|
||||
/** An import statement */
|
||||
|
||||
@@ -46,20 +46,23 @@ class SelfAttributeRead extends SelfAttribute {
|
||||
}
|
||||
|
||||
predicate guardedByHasattr() {
|
||||
exists(Variable var, ControlFlowNode n |
|
||||
var.getAUse() = this.getObject().getAFlowNode() and
|
||||
exists(Variable var, ControlFlowNode n, ControlFlowNode this_, ControlFlowNode obj_ |
|
||||
this_.getNode() = this and obj_.getNode() = this.getObject()
|
||||
|
|
||||
var.getAUse() = obj_ and
|
||||
hasattr(n, var.getAUse(), this.getName()) and
|
||||
n.strictlyDominates(this.getAFlowNode())
|
||||
n.strictlyDominates(this_)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
predicate locallyDefined() {
|
||||
exists(SelfAttributeStore store |
|
||||
this.getName() = store.getName() and
|
||||
this.getScope() = store.getScope()
|
||||
exists(SelfAttributeStore store, ControlFlowNode store_, ControlFlowNode this_ |
|
||||
store_.getNode() = store and this_.getNode() = this
|
||||
|
|
||||
store.getAFlowNode().strictlyDominates(this.getAFlowNode())
|
||||
this.getName() = store.getName() and
|
||||
this.getScope() = store.getScope() and
|
||||
store_.strictlyDominates(this_)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,24 +5,30 @@ private import semmle.python.dataflow.new.DataFlow
|
||||
|
||||
private predicate constCompare(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) {
|
||||
exists(CompareNode cn | cn = g |
|
||||
exists(ImmutableLiteral const, Cmpop op |
|
||||
op = any(Eq eq) and branch = true
|
||||
or
|
||||
op = any(NotEq ne) and branch = false
|
||||
exists(ImmutableLiteral const, Cmpop op, ControlFlowNode c |
|
||||
c.getNode() = const and
|
||||
(
|
||||
op = any(Eq eq) and branch = true
|
||||
or
|
||||
op = any(NotEq ne) and branch = false
|
||||
)
|
||||
|
|
||||
cn.operands(const.getAFlowNode(), op, node)
|
||||
cn.operands(c, op, node)
|
||||
or
|
||||
cn.operands(node, op, const.getAFlowNode())
|
||||
cn.operands(node, op, c)
|
||||
)
|
||||
or
|
||||
exists(NameConstant const, Cmpop op |
|
||||
op = any(Is is_) and branch = true
|
||||
or
|
||||
op = any(IsNot isn) and branch = false
|
||||
exists(NameConstant const, Cmpop op, ControlFlowNode c |
|
||||
c.getNode() = const and
|
||||
(
|
||||
op = any(Is is_) and branch = true
|
||||
or
|
||||
op = any(IsNot isn) and branch = false
|
||||
)
|
||||
|
|
||||
cn.operands(const.getAFlowNode(), op, node)
|
||||
cn.operands(c, op, node)
|
||||
or
|
||||
cn.operands(node, op, const.getAFlowNode())
|
||||
cn.operands(node, op, c)
|
||||
)
|
||||
or
|
||||
exists(IterableNode const_iterable, Cmpop op |
|
||||
|
||||
@@ -228,7 +228,7 @@ private class ClassDefinitionAsAttrWrite extends AttrWrite, CfgNode {
|
||||
|
||||
override Node getValue() { result.asCfgNode() = node.getValue() }
|
||||
|
||||
override Node getObject() { result.asCfgNode() = cls.getAFlowNode() }
|
||||
override Node getObject() { result.asCfgNode().getNode() = cls }
|
||||
|
||||
override ExprNode getAttributeNameExpr() { none() }
|
||||
|
||||
|
||||
@@ -1913,8 +1913,8 @@ abstract class ReturnNode extends Node {
|
||||
class ExtractedReturnNode extends ReturnNode, CfgNode {
|
||||
// See `TaintTrackingImplementation::returnFlowStep`
|
||||
ExtractedReturnNode() {
|
||||
node = any(Return ret).getValue().getAFlowNode() or
|
||||
node = any(Yield yield).getAFlowNode()
|
||||
node.getNode() = any(Return ret).getValue() or
|
||||
node.getNode() = any(Yield yield)
|
||||
}
|
||||
|
||||
override ReturnKind getKind() { any() }
|
||||
@@ -1932,7 +1932,7 @@ class ExtractedReturnNode extends ReturnNode, CfgNode {
|
||||
class YieldNodeInContextManagerFunction extends ReturnNode, CfgNode {
|
||||
YieldNodeInContextManagerFunction() {
|
||||
hasContextmanagerDecorator(node.getScope()) and
|
||||
node = any(Yield yield).getValue().getAFlowNode()
|
||||
node.getNode() = any(Yield yield).getValue()
|
||||
}
|
||||
|
||||
override ReturnKind getKind() { any() }
|
||||
|
||||
@@ -185,8 +185,8 @@ private predicate synthDictSplatArgumentNodeStoreStep(
|
||||
*/
|
||||
predicate yieldStoreStep(Node nodeFrom, Content c, Node nodeTo) {
|
||||
exists(Yield yield |
|
||||
nodeTo.asCfgNode() = yield.getAFlowNode() and
|
||||
nodeFrom.asCfgNode() = yield.getValue().getAFlowNode() and
|
||||
nodeTo.asCfgNode().getNode() = yield and
|
||||
nodeFrom.asCfgNode().getNode() = yield.getValue() and
|
||||
// TODO: Consider if this will also need to transfer dictionary content
|
||||
// once dictionary comprehensions are supported.
|
||||
c instanceof ListElementContent
|
||||
@@ -529,7 +529,7 @@ predicate simpleLocalFlowStepForTypetracking(Node nodeFrom, Node nodeTo) {
|
||||
}
|
||||
|
||||
private predicate summaryLocalStep(Node nodeFrom, Node nodeTo, string model) {
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom,
|
||||
nodeTo.(FlowSummaryNode).getSummaryNode(), true, model)
|
||||
}
|
||||
|
||||
@@ -1138,7 +1138,9 @@ predicate clearsContent(Node n, ContentSet cs) {
|
||||
* Holds if the value that is being tracked is expected to be stored inside content `c`
|
||||
* at node `n`.
|
||||
*/
|
||||
predicate expectsContent(Node n, ContentSet c) { none() }
|
||||
predicate expectsContent(Node n, ContentSet c) {
|
||||
FlowSummaryImpl::Private::Steps::summaryExpectsContent(n.(FlowSummaryNode).getSummaryNode(), c)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if values stored inside attribute `c` are cleared at node `n`.
|
||||
|
||||
@@ -485,7 +485,7 @@ class ModuleVariableNode extends Node, TModuleVariableNode {
|
||||
|
||||
/** Gets a node that reads this variable, excluding reads that happen through `from ... import *`. */
|
||||
Node getALocalRead() {
|
||||
result.asCfgNode() = var.getALoad().getAFlowNode() and
|
||||
result.asCfgNode().getNode() = var.getALoad() and
|
||||
not result.getScope() = mod
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ module Input implements InputSig<Location, DataFlowImplSpecific::PythonDataFlow>
|
||||
|
||||
class SinkBase = Void;
|
||||
|
||||
class FlowSummaryCallBase = Void;
|
||||
|
||||
predicate callableFromSource(SummarizedCallableBase c) { none() }
|
||||
|
||||
ArgumentPosition callbackSelfParameterPosition() { result.isLambdaSelf() }
|
||||
@@ -91,6 +93,8 @@ module Input implements InputSig<Location, DataFlowImplSpecific::PythonDataFlow>
|
||||
cs.isAnyTupleOrDictionaryElement() and result = "AnyTupleOrDictionaryElement" and arg = ""
|
||||
}
|
||||
|
||||
string encodeWithContent(ContentSet c, string arg) { result = "With" + encodeContent(c, arg) }
|
||||
|
||||
bindingset[token]
|
||||
ParameterPosition decodeUnknownParameterPosition(AccessPath::AccessPathTokenBase token) {
|
||||
// needed to support `Argument[x..y]` ranges
|
||||
@@ -109,6 +113,10 @@ module Input implements InputSig<Location, DataFlowImplSpecific::PythonDataFlow>
|
||||
private import Make<Location, DataFlowImplSpecific::PythonDataFlow, Input> as Impl
|
||||
|
||||
private module StepsInput implements Impl::Private::StepsInputSig {
|
||||
Impl::Private::SummaryNode getSummaryNode(Node n) {
|
||||
result = n.(FlowSummaryNode).getSummaryNode()
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
DataFlowCall getACall(Public::SummarizedCallable sc) {
|
||||
result =
|
||||
|
||||
@@ -9,7 +9,19 @@ private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.internal.ImportStar
|
||||
private import semmle.python.dataflow.new.TypeTracking
|
||||
private import semmle.python.dataflow.new.internal.DataFlowPrivate
|
||||
private import semmle.python.essa.SsaDefinitions
|
||||
|
||||
/**
|
||||
* Holds if `init` is a package's `__init__.py` and `var` is a global variable in
|
||||
* `init` whose name matches a submodule of the package.
|
||||
*
|
||||
* Inlined from `SsaSource::init_module_submodule_defn` to avoid pulling
|
||||
* `semmle.python.essa.SsaDefinitions` into the new dataflow stack.
|
||||
*/
|
||||
private predicate initModuleSubmoduleDefn(GlobalVariable var, Module init) {
|
||||
init.isPackageInit() and
|
||||
exists(init.getPackage().getSubModule(var.getId())) and
|
||||
var.getScope() = init
|
||||
}
|
||||
|
||||
/**
|
||||
* Python modules and the way imports are resolved are... complicated. Here's a crash course in how
|
||||
@@ -326,7 +338,7 @@ module ImportResolution {
|
||||
// imported yet.
|
||||
exists(string submodule, Module package, EssaVariable var |
|
||||
submodule = var.getName() and
|
||||
SsaSource::init_module_submodule_defn(var.getSourceVariable(), package.getEntryNode()) and
|
||||
initModuleSubmoduleDefn(var.getSourceVariable(), package) and
|
||||
m = getModuleFromName(package.getPackageName() + "." + submodule) and
|
||||
result.asCfgNode() = var.getDefinition().(EssaNodeDefinition).getDefiningNode()
|
||||
)
|
||||
|
||||
@@ -80,10 +80,8 @@ private module Cached {
|
||||
) and
|
||||
model = ""
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom
|
||||
.(DataFlowPrivate::FlowSummaryNode)
|
||||
.getSummaryNode(), nodeTo.(DataFlowPrivate::FlowSummaryNode).getSummaryNode(), false,
|
||||
model)
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom,
|
||||
nodeTo.(DataFlowPrivate::FlowSummaryNode).getSummaryNode(), false, model)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -94,8 +94,10 @@ private module SummaryTypeTrackerInput implements SummaryTypeTracker::Input {
|
||||
Node returnOf(Node callable, SummaryComponent return) {
|
||||
return = FlowSummaryImpl::Private::SummaryComponent::return() and
|
||||
// `result` should be the return value of a callable expression (lambda or function) referenced by `callable`
|
||||
result.asCfgNode() =
|
||||
callable.getALocalSource().asExpr().(CallableExpr).getInnerScope().getAReturnValueFlowNode()
|
||||
exists(Return ret |
|
||||
ret.getScope() = callable.getALocalSource().asExpr().(CallableExpr).getInnerScope() and
|
||||
result.asCfgNode().getNode() = ret.getValue()
|
||||
)
|
||||
}
|
||||
|
||||
// Relating callables to nodes
|
||||
|
||||
@@ -61,7 +61,7 @@ private module CaptureInput implements Shared::InputSig<Location, Cfg::BasicBloc
|
||||
class VariableWrite extends ControlFlowNode {
|
||||
CapturedVariable v;
|
||||
|
||||
VariableWrite() { this = v.getAStore().getAFlowNode().(DefinitionNode).getValue() }
|
||||
VariableWrite() { exists(DefinitionNode d | d.getNode() = v.getAStore() | this = d.getValue()) }
|
||||
|
||||
CapturedVariable getVariable() { result = v }
|
||||
|
||||
@@ -71,7 +71,7 @@ private module CaptureInput implements Shared::InputSig<Location, Cfg::BasicBloc
|
||||
class VariableRead extends Expr {
|
||||
CapturedVariable v;
|
||||
|
||||
VariableRead() { this = v.getALoad().getAFlowNode() }
|
||||
VariableRead() { this.getNode() = v.getALoad() }
|
||||
|
||||
CapturedVariable getVariable() { result = v }
|
||||
}
|
||||
|
||||
@@ -448,8 +448,7 @@ class TaintTrackingImplementation extends string instanceof TaintTracking::Confi
|
||||
context = TNoParam() and
|
||||
src = TTaintTrackingNode_(retval, TNoParam(), path, kind, this) and
|
||||
node.asCfgNode() = call and
|
||||
retval.asCfgNode() =
|
||||
any(Return ret | ret.getScope() = pyfunc.getScope()).getValue().getAFlowNode()
|
||||
retval.asCfgNode().getNode() = any(Return ret | ret.getScope() = pyfunc.getScope()).getValue()
|
||||
) and
|
||||
edgeLabel = "return"
|
||||
}
|
||||
@@ -471,8 +470,7 @@ class TaintTrackingImplementation extends string instanceof TaintTracking::Confi
|
||||
this.callContexts(call, src, pyfunc, context, callee) and
|
||||
retnode = TTaintTrackingNode_(retval, callee, path, kind, this) and
|
||||
node.asCfgNode() = call and
|
||||
retval.asCfgNode() =
|
||||
any(Return ret | ret.getScope() = pyfunc.getScope()).getValue().getAFlowNode()
|
||||
retval.asCfgNode().getNode() = any(Return ret | ret.getScope() = pyfunc.getScope()).getValue()
|
||||
) and
|
||||
edgeLabel = "call"
|
||||
}
|
||||
@@ -716,8 +714,10 @@ private class EssaTaintTracking extends string instanceof TaintTracking::Configu
|
||||
src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and
|
||||
path.noAttribute()
|
||||
|
|
||||
assign.getValue().getAFlowNode() = srcnode.asCfgNode() and
|
||||
depth = iterable_unpacking_descent(assign.getATarget().getAFlowNode(), defn.getDefiningNode()) and
|
||||
srcnode.asCfgNode().getNode() = assign.getValue() and
|
||||
exists(SequenceNode left_parent | left_parent.getNode() = assign.getATarget() |
|
||||
depth = iterable_unpacking_descent(left_parent, defn.getDefiningNode())
|
||||
) and
|
||||
kind = taint_at_depth(srckind, depth)
|
||||
)
|
||||
}
|
||||
@@ -964,7 +964,7 @@ private TaintKind taint_at_depth(SequenceKind parent_kind, int depth) {
|
||||
* - with `left_defn` = `*y`, `left_parent` = `((x, *y), ...)`, result = 1
|
||||
*/
|
||||
int iterable_unpacking_descent(SequenceNode left_parent, ControlFlowNode left_defn) {
|
||||
exists(Assign a | a.getATarget().getASubExpression*().getAFlowNode() = left_parent) and
|
||||
exists(Assign a | left_parent.getNode() = a.getATarget().getASubExpression*()) and
|
||||
left_parent.getAnElement() = left_defn and
|
||||
// Handle `a, *b = some_iterable`
|
||||
if left_defn instanceof StarredNode then result = 0 else result = 1
|
||||
|
||||
@@ -56,7 +56,7 @@ module SsaSource {
|
||||
predicate with_definition(Variable v, ControlFlowNode defn) {
|
||||
exists(With with, Name var |
|
||||
with.getOptionalVars() = var and
|
||||
var.getAFlowNode() = defn
|
||||
defn.getNode() = var
|
||||
|
|
||||
var = v.getAStore()
|
||||
)
|
||||
@@ -67,7 +67,7 @@ module SsaSource {
|
||||
predicate pattern_capture_definition(Variable v, ControlFlowNode defn) {
|
||||
exists(MatchCapturePattern capture, Name var |
|
||||
capture.getVariable() = var and
|
||||
var.getAFlowNode() = defn
|
||||
defn.getNode() = var
|
||||
|
|
||||
var = v.getAStore()
|
||||
)
|
||||
@@ -78,7 +78,7 @@ module SsaSource {
|
||||
predicate pattern_alias_definition(Variable v, ControlFlowNode defn) {
|
||||
exists(MatchAsPattern pattern, Name var |
|
||||
pattern.getAlias() = var and
|
||||
var.getAFlowNode() = defn
|
||||
defn.getNode() = var
|
||||
|
|
||||
var = v.getAStore()
|
||||
)
|
||||
|
||||
@@ -59,7 +59,7 @@ module Bottle {
|
||||
|
||||
override Parameter getARoutedParameter() { none() }
|
||||
|
||||
override Function getARequestHandler() { result.getADecorator().getAFlowNode() = node }
|
||||
override Function getARequestHandler() { node.getNode() = result.getADecorator() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,10 @@ module Bottle {
|
||||
/** A response returned by a view callable. */
|
||||
class BottleReturnResponse extends Http::Server::HttpResponse::Range {
|
||||
BottleReturnResponse() {
|
||||
this.asCfgNode() = any(View::ViewCallable vc).getAReturnValueFlowNode()
|
||||
exists(Return ret |
|
||||
ret.getScope() = any(View::ViewCallable vc) and
|
||||
this.asCfgNode().getNode() = ret.getValue()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getBody() { result = this }
|
||||
|
||||
@@ -2872,7 +2872,10 @@ module PrivateDjango {
|
||||
DataFlow::CfgNode
|
||||
{
|
||||
DjangoRedirectViewGetRedirectUrlReturn() {
|
||||
node = any(GetRedirectUrlFunction f).getAReturnValueFlowNode()
|
||||
exists(Return ret |
|
||||
ret.getScope() = any(GetRedirectUrlFunction f) and
|
||||
node.getNode() = ret.getValue()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getRedirectLocation() { result = this }
|
||||
|
||||
@@ -129,7 +129,7 @@ module FastApi {
|
||||
result in [this.getArg(0), this.getArgByName("path")]
|
||||
}
|
||||
|
||||
override Function getARequestHandler() { result.getADecorator().getAFlowNode() = node }
|
||||
override Function getARequestHandler() { node.getNode() = result.getADecorator() }
|
||||
|
||||
override string getFramework() { result = "FastAPI" }
|
||||
|
||||
@@ -309,7 +309,10 @@ module FastApi {
|
||||
FastApiRouteSetup routeSetup;
|
||||
|
||||
FastApiRequestHandlerReturn() {
|
||||
node = routeSetup.getARequestHandler().getAReturnValueFlowNode()
|
||||
exists(Return ret |
|
||||
ret.getScope() = routeSetup.getARequestHandler() and
|
||||
node.getNode() = ret.getValue()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getBody() { result = this }
|
||||
|
||||
@@ -71,14 +71,21 @@ module Flask {
|
||||
* See https://flask.palletsprojects.com/en/1.1.x/api/#flask.Flask.
|
||||
*/
|
||||
module FlaskApp {
|
||||
/** Gets a reference to the `flask.Flask` class. */
|
||||
API::Node classRef() {
|
||||
result = API::moduleImport("flask").getMember("Flask") or
|
||||
/**
|
||||
* Gets a reference to the `flask.Flask` class or any subclass.
|
||||
*
|
||||
* Deprecated: Use `subclassRef()` instead, this predicate always returned some subclasses.
|
||||
*/
|
||||
deprecated API::Node classRef() { result = subclassRef() }
|
||||
|
||||
/** Gets a reference to the `flask.Flask` class or any subclass. */
|
||||
API::Node subclassRef() {
|
||||
result = API::moduleImport("flask").getMember("Flask").getASubclass*() or
|
||||
result = ModelOutput::getATypeNode("flask.Flask~Subclass").getASubclass*()
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `flask.Flask` (a flask application). */
|
||||
API::Node instance() { result = classRef().getReturn() }
|
||||
API::Node instance() { result = subclassRef().getReturn() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,7 +139,7 @@ module Flask {
|
||||
API::Node classRef() {
|
||||
result = API::moduleImport("flask").getMember("Response")
|
||||
or
|
||||
result = [FlaskApp::classRef(), FlaskApp::instance()].getMember("response_class")
|
||||
result = [FlaskApp::subclassRef(), FlaskApp::instance()].getMember("response_class")
|
||||
or
|
||||
result = ModelOutput::getATypeNode("flask.Response~Subclass").getASubclass*()
|
||||
}
|
||||
@@ -371,7 +378,7 @@ module Flask {
|
||||
result in [this.getArg(0), this.getArgByName("rule")]
|
||||
}
|
||||
|
||||
override Function getARequestHandler() { result.getADecorator().getAFlowNode() = node }
|
||||
override Function getARequestHandler() { node.getNode() = result.getADecorator() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -536,7 +543,7 @@ module Flask {
|
||||
FlaskRouteHandlerReturn() {
|
||||
exists(Function routeHandler |
|
||||
routeHandler = any(FlaskRouteSetup rs).getARequestHandler() and
|
||||
node = routeHandler.getAReturnValueFlowNode() and
|
||||
exists(Return ret | ret.getScope() = routeHandler and node.getNode() = ret.getValue()) and
|
||||
not this instanceof Flask::Response::InstanceSource
|
||||
)
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ private module FlaskAdmin {
|
||||
result in [this.getArg(0), this.getArgByName("url")]
|
||||
}
|
||||
|
||||
override Function getARequestHandler() { result.getADecorator().getAFlowNode() = node }
|
||||
override Function getARequestHandler() { node.getNode() = result.getADecorator() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,7 +71,7 @@ private module FlaskAdmin {
|
||||
|
||||
override Function getARequestHandler() {
|
||||
exists(Flask::FlaskViewClass cls |
|
||||
cls.getADecorator().getAFlowNode() = node and
|
||||
node.getNode() = cls.getADecorator() and
|
||||
result = cls.getARequestHandler()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -93,7 +93,11 @@ module OpenAI {
|
||||
/** Gets the message dictionaries passed as a list to `responses.create`. */
|
||||
private API::Node responsesMessage() {
|
||||
result =
|
||||
classRef().getMember("responses").getMember("create").getKeywordParameter("input").getASubscript()
|
||||
classRef()
|
||||
.getMember("responses")
|
||||
.getMember("create")
|
||||
.getKeywordParameter("input")
|
||||
.getASubscript()
|
||||
}
|
||||
|
||||
/** Gets the content sink of a message dictionary, including the `text` of structured content. */
|
||||
|
||||
@@ -25,7 +25,11 @@ module OpenRouter {
|
||||
/** Gets the message dictionaries passed to `chat.send`. */
|
||||
private API::Node chatMessage() {
|
||||
result =
|
||||
clientRef().getMember("chat").getMember("send").getKeywordParameter("messages").getASubscript()
|
||||
clientRef()
|
||||
.getMember("chat")
|
||||
.getMember("send")
|
||||
.getKeywordParameter("messages")
|
||||
.getASubscript()
|
||||
}
|
||||
|
||||
/** Gets the content sink of a message dictionary, including the `text` of structured content. */
|
||||
|
||||
@@ -166,7 +166,10 @@ module Pyramid {
|
||||
/** A response returned by a view callable. */
|
||||
private class PyramidReturnResponse extends Http::Server::HttpResponse::Range {
|
||||
PyramidReturnResponse() {
|
||||
this.asCfgNode() = any(View::ViewCallable vc).getAReturnValueFlowNode() and
|
||||
exists(Return ret |
|
||||
ret.getScope() = any(View::ViewCallable vc) and
|
||||
this.asCfgNode().getNode() = ret.getValue()
|
||||
) and
|
||||
not this = instance()
|
||||
}
|
||||
|
||||
|
||||
@@ -2254,8 +2254,9 @@ module StdlibPrivate {
|
||||
DataFlow::CfgNode
|
||||
{
|
||||
WsgirefSimpleServerApplicationReturn() {
|
||||
exists(WsgirefSimpleServerApplication requestHandler |
|
||||
node = requestHandler.getAReturnValueFlowNode()
|
||||
exists(WsgirefSimpleServerApplication requestHandler, Return ret |
|
||||
ret.getScope() = requestHandler and
|
||||
node.getNode() = ret.getValue()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4198,11 +4199,9 @@ module StdlibPrivate {
|
||||
// The positional argument contains a mapping.
|
||||
// TODO: these values can be overwritten by keyword arguments
|
||||
// - dict mapping
|
||||
exists(DataFlow::DictionaryElementContent dc, string key | key = dc.getKey() |
|
||||
input = "Argument[0].DictionaryElement[" + key + "]" and
|
||||
output = "ReturnValue.DictionaryElement[" + key + "]" and
|
||||
preservesValue = true
|
||||
)
|
||||
input = "Argument[0].WithAnyDictionaryElement" and
|
||||
output = "ReturnValue" and
|
||||
preservesValue = true
|
||||
or
|
||||
// - list-of-pairs mapping
|
||||
input = "Argument[0].ListElement.TupleElement[1]" and
|
||||
@@ -4239,9 +4238,7 @@ module StdlibPrivate {
|
||||
or
|
||||
input = "Argument[0].SetElement"
|
||||
or
|
||||
exists(DataFlow::TupleElementContent tc, int i | i = tc.getIndex() |
|
||||
input = "Argument[0].TupleElement[" + i.toString() + "]"
|
||||
)
|
||||
input = "Argument[0].AnyTupleElement"
|
||||
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
|
||||
) and
|
||||
// Element content is mutated into list element content
|
||||
@@ -4265,11 +4262,9 @@ module StdlibPrivate {
|
||||
}
|
||||
|
||||
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||
exists(DataFlow::TupleElementContent tc, int i | i = tc.getIndex() |
|
||||
input = "Argument[0].TupleElement[" + i.toString() + "]" and
|
||||
output = "ReturnValue.TupleElement[" + i.toString() + "]" and
|
||||
preservesValue = true
|
||||
)
|
||||
input = "Argument[0].WithAnyTupleElement" and
|
||||
output = "ReturnValue" and
|
||||
preservesValue = true
|
||||
or
|
||||
input = "Argument[0].ListElement" and
|
||||
output = "ReturnValue" and
|
||||
@@ -4293,9 +4288,7 @@ module StdlibPrivate {
|
||||
or
|
||||
input = "Argument[0].SetElement"
|
||||
or
|
||||
exists(DataFlow::TupleElementContent tc, int i | i = tc.getIndex() |
|
||||
input = "Argument[0].TupleElement[" + i.toString() + "]"
|
||||
)
|
||||
input = "Argument[0].AnyTupleElement"
|
||||
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
|
||||
) and
|
||||
output = "ReturnValue.SetElement" and
|
||||
@@ -4341,9 +4334,7 @@ module StdlibPrivate {
|
||||
or
|
||||
input = "Argument[0].SetElement"
|
||||
or
|
||||
exists(DataFlow::TupleElementContent tc, int i | i = tc.getIndex() |
|
||||
input = "Argument[0].TupleElement[" + i.toString() + "]"
|
||||
)
|
||||
input = "Argument[0].AnyTupleElement"
|
||||
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
|
||||
) and
|
||||
output = "ReturnValue.ListElement" and
|
||||
@@ -4371,9 +4362,7 @@ module StdlibPrivate {
|
||||
or
|
||||
content = "SetElement"
|
||||
or
|
||||
exists(DataFlow::TupleElementContent tc, int i | i = tc.getIndex() |
|
||||
content = "TupleElement[" + i.toString() + "]"
|
||||
)
|
||||
content = "AnyTupleElement"
|
||||
|
|
||||
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
|
||||
input = "Argument[0]." + content and
|
||||
@@ -4403,9 +4392,7 @@ module StdlibPrivate {
|
||||
or
|
||||
input = "Argument[0].SetElement"
|
||||
or
|
||||
exists(DataFlow::TupleElementContent tc, int i | i = tc.getIndex() |
|
||||
input = "Argument[0].TupleElement[" + i.toString() + "]"
|
||||
)
|
||||
input = "Argument[0].AnyTupleElement"
|
||||
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
|
||||
) and
|
||||
output = "ReturnValue.ListElement" and
|
||||
@@ -4433,9 +4420,7 @@ module StdlibPrivate {
|
||||
or
|
||||
input = "Argument[0].SetElement"
|
||||
or
|
||||
exists(DataFlow::TupleElementContent tc, int i | i = tc.getIndex() |
|
||||
input = "Argument[0].TupleElement[" + i.toString() + "]"
|
||||
)
|
||||
input = "Argument[0].AnyTupleElement"
|
||||
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
|
||||
) and
|
||||
output = "ReturnValue" and
|
||||
@@ -4467,9 +4452,7 @@ module StdlibPrivate {
|
||||
// We reduce generality slightly by not tracking tuple contents on list arguments beyond the first, for performance.
|
||||
// TODO: Once we have TupleElementAny, this generality can be increased.
|
||||
i = 0 and
|
||||
exists(DataFlow::TupleElementContent tc, int j | j = tc.getIndex() |
|
||||
input = "Argument[1].TupleElement[" + j.toString() + "]"
|
||||
)
|
||||
input = "Argument[1].AnyTupleElement"
|
||||
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
|
||||
) and
|
||||
output = "Argument[0].Parameter[" + i.toString() + "]" and
|
||||
@@ -4498,9 +4481,7 @@ module StdlibPrivate {
|
||||
or
|
||||
input = "Argument[1].SetElement"
|
||||
or
|
||||
exists(DataFlow::TupleElementContent tc, int i | i = tc.getIndex() |
|
||||
input = "Argument[1].TupleElement[" + i.toString() + "]"
|
||||
)
|
||||
input = "Argument[1].AnyTupleElement"
|
||||
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
|
||||
) and
|
||||
(output = "Argument[0].Parameter[0]" or output = "ReturnValue.ListElement") and
|
||||
@@ -4524,9 +4505,7 @@ module StdlibPrivate {
|
||||
or
|
||||
input = "Argument[0].SetElement"
|
||||
or
|
||||
exists(DataFlow::TupleElementContent tc, int i | i = tc.getIndex() |
|
||||
input = "Argument[0].TupleElement[" + i.toString() + "]"
|
||||
)
|
||||
input = "Argument[0].AnyTupleElement"
|
||||
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
|
||||
) and
|
||||
output = "ReturnValue.ListElement.TupleElement[1]" and
|
||||
@@ -4551,12 +4530,7 @@ module StdlibPrivate {
|
||||
or
|
||||
input = "Argument[" + i.toString() + "].SetElement"
|
||||
or
|
||||
// We reduce generality slightly by not tracking tuple contents on arguments beyond the first two, for performance.
|
||||
// TODO: Once we have TupleElementAny, this generality can be increased.
|
||||
i in [0 .. 1] and
|
||||
exists(DataFlow::TupleElementContent tc, int j | j = tc.getIndex() |
|
||||
input = "Argument[" + i.toString() + "].TupleElement[" + j.toString() + "]"
|
||||
)
|
||||
input = "Argument[" + i.toString() + "].AnyTupleElement"
|
||||
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
|
||||
) and
|
||||
output = "ReturnValue.ListElement.TupleElement[" + i.toString() + "]" and
|
||||
@@ -4579,12 +4553,6 @@ module StdlibPrivate {
|
||||
override DataFlow::ArgumentNode getACallback() { none() }
|
||||
|
||||
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||
exists(DataFlow::Content c |
|
||||
input = "Argument[self]." + c.getMaDRepresentation() and
|
||||
output = "ReturnValue." + c.getMaDRepresentation() and
|
||||
preservesValue = true
|
||||
)
|
||||
or
|
||||
input = "Argument[self]" and
|
||||
output = "ReturnValue" and
|
||||
preservesValue = true
|
||||
@@ -4740,12 +4708,10 @@ module StdlibPrivate {
|
||||
override DataFlow::ArgumentNode getACallback() { none() }
|
||||
|
||||
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||
exists(DataFlow::DictionaryElementContent dc, string key | key = dc.getKey() |
|
||||
input = "Argument[self].DictionaryElement[" + key + "]" and
|
||||
output = "ReturnValue.TupleElement[1]" and
|
||||
preservesValue = true
|
||||
// TODO: put `key` into "ReturnValue.TupleElement[0]"
|
||||
)
|
||||
input = "Argument[self].AnyDictionaryElement" and
|
||||
output = "ReturnValue.TupleElement[1]" and
|
||||
preservesValue = true
|
||||
// TODO: put `key` into "ReturnValue.TupleElement[0]"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4824,11 +4790,9 @@ module StdlibPrivate {
|
||||
}
|
||||
|
||||
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||
exists(DataFlow::DictionaryElementContent dc, string key | key = dc.getKey() |
|
||||
input = "Argument[self].DictionaryElement[" + key + "]" and
|
||||
output = "ReturnValue.ListElement" and
|
||||
preservesValue = true
|
||||
)
|
||||
input = "Argument[self].AnyDictionaryElement" and
|
||||
output = "ReturnValue.ListElement" and
|
||||
preservesValue = true
|
||||
or
|
||||
input = "Argument[self]" and
|
||||
output = "ReturnValue" and
|
||||
@@ -4875,11 +4839,9 @@ module StdlibPrivate {
|
||||
}
|
||||
|
||||
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||
exists(DataFlow::DictionaryElementContent dc, string key | key = dc.getKey() |
|
||||
input = "Argument[self].DictionaryElement[" + key + "]" and
|
||||
output = "ReturnValue.ListElement.TupleElement[1]" and
|
||||
preservesValue = true
|
||||
)
|
||||
input = "Argument[self].AnyDictionaryElement" and
|
||||
output = "ReturnValue.ListElement.TupleElement[1]" and
|
||||
preservesValue = true
|
||||
or
|
||||
// TODO: Add the keys to output list
|
||||
input = "Argument[self]" and
|
||||
|
||||
@@ -182,7 +182,10 @@ private module Twisted {
|
||||
DataFlow::CfgNode
|
||||
{
|
||||
TwistedResourceRenderMethodReturn() {
|
||||
this.asCfgNode() = any(TwistedResourceRenderMethod meth).getAReturnValueFlowNode()
|
||||
exists(Return ret |
|
||||
ret.getScope() = any(TwistedResourceRenderMethod meth) and
|
||||
this.asCfgNode().getNode() = ret.getValue()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getBody() { result = this }
|
||||
|
||||
@@ -77,7 +77,7 @@ module Stages {
|
||||
or
|
||||
exists(any(AstExtended::AstNode n).getParentNode())
|
||||
or
|
||||
exists(any(AstExtended::AstNode n).getAFlowNode())
|
||||
exists(PyFlow::ControlFlowNode cfg, AstExtended::AstNode n | cfg.getNode() = n)
|
||||
or
|
||||
exists(any(PyFlow::BasicBlock b).getImmediateDominator())
|
||||
or
|
||||
|
||||
@@ -56,8 +56,9 @@ abstract class CallableObjectInternal extends ObjectInternal {
|
||||
/** A Python function. */
|
||||
class PythonFunctionObjectInternal extends CallableObjectInternal, TPythonFunctionObject {
|
||||
override Function getScope() {
|
||||
exists(CallableExpr expr |
|
||||
this = TPythonFunctionObject(expr.getAFlowNode()) and
|
||||
exists(CallableExpr expr, ControlFlowNode exprCfg |
|
||||
exprCfg.getNode() = expr and
|
||||
this = TPythonFunctionObject(exprCfg) and
|
||||
result = expr.getInnerScope()
|
||||
)
|
||||
}
|
||||
@@ -80,11 +81,12 @@ class PythonFunctionObjectInternal extends CallableObjectInternal, TPythonFuncti
|
||||
|
||||
pragma[nomagic]
|
||||
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
|
||||
exists(Function func, ControlFlowNode rval, ControlFlowNode forigin |
|
||||
exists(Function func, Return ret, ControlFlowNode rval, ControlFlowNode forigin |
|
||||
func = this.getScope() and
|
||||
callee.appliesToScope(func)
|
||||
|
|
||||
rval = func.getAReturnValueFlowNode() and
|
||||
ret.getScope() = func and
|
||||
rval.getNode() = ret.getValue() and
|
||||
PointsToInternal::pointsTo(rval, callee, obj, forigin) and
|
||||
origin = CfgOrigin::fromCfgNode(forigin)
|
||||
)
|
||||
@@ -160,10 +162,11 @@ class PythonFunctionObjectInternal extends CallableObjectInternal, TPythonFuncti
|
||||
}
|
||||
|
||||
private BasicBlock blockReturningNone(Function func) {
|
||||
exists(Return ret |
|
||||
exists(Return ret, ControlFlowNode ret_ |
|
||||
not exists(ret.getValue()) and
|
||||
ret.getScope() = func and
|
||||
result = ret.getAFlowNode().getBasicBlock()
|
||||
ret_.getNode() = ret and
|
||||
result = ret_.getBasicBlock()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -113,8 +113,9 @@ abstract class ClassObjectInternal extends ObjectInternal {
|
||||
class PythonClassObjectInternal extends ClassObjectInternal, TPythonClassObject {
|
||||
/** Gets the scope for this Python class */
|
||||
Class getScope() {
|
||||
exists(ClassExpr expr |
|
||||
this = TPythonClassObject(expr.getAFlowNode()) and
|
||||
exists(ClassExpr expr, ControlFlowNode exprCfg |
|
||||
exprCfg.getNode() = expr and
|
||||
this = TPythonClassObject(exprCfg) and
|
||||
result = expr.getInnerScope()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -745,7 +745,12 @@ class PythonFunctionValue extends FunctionValue {
|
||||
override int maxParameters() { result = this.getScope().getMaxPositionalArguments() }
|
||||
|
||||
/** Gets a control flow node corresponding to a return statement in this function */
|
||||
ControlFlowNode getAReturnedNode() { result = this.getScope().getAReturnValueFlowNode() }
|
||||
ControlFlowNode getAReturnedNode() {
|
||||
exists(Return ret |
|
||||
ret.getScope() = this.getScope() and
|
||||
result.getNode() = ret.getValue()
|
||||
)
|
||||
}
|
||||
|
||||
override ClassValue getARaisedType() { scope_raises(result, this.getScope()) }
|
||||
|
||||
|
||||
@@ -387,7 +387,7 @@ private PythonClassObjectInternal abcMetaClassObject() {
|
||||
private predicate neither_class_nor_static_method(Function f) {
|
||||
not exists(f.getADecorator())
|
||||
or
|
||||
exists(ControlFlowNode deco | deco = f.getADecorator().getAFlowNode() |
|
||||
exists(ControlFlowNode deco | deco.getNode() = f.getADecorator() |
|
||||
exists(ObjectInternal o | PointsToInternal::pointsTo(deco, _, o, _) |
|
||||
o != ObjectInternal::staticMethod() and
|
||||
o != ObjectInternal::classMethod()
|
||||
|
||||
@@ -711,7 +711,7 @@ private module InterModulePointsTo {
|
||||
ControlFlowNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin
|
||||
) {
|
||||
exists(string name, ImportExpr i |
|
||||
i.getAFlowNode() = f and
|
||||
f.getNode() = i and
|
||||
i.getImportedModuleName() = name and
|
||||
PointsToInternal::module_imported_as(value, name) and
|
||||
origin = f and
|
||||
@@ -2118,8 +2118,9 @@ module Types {
|
||||
result.getBuiltin() = cls.getBuiltin().getBaseClass() and n = 0
|
||||
or
|
||||
exists(Class pycls | pycls = cls.(PythonClassObjectInternal).getScope() |
|
||||
exists(ObjectInternal base |
|
||||
PointsToInternal::pointsTo(pycls.getBase(n).getAFlowNode(), _, base, _)
|
||||
exists(ObjectInternal base, ControlFlowNode baseNode |
|
||||
baseNode.getNode() = pycls.getBase(n) and
|
||||
PointsToInternal::pointsTo(baseNode, _, base, _)
|
||||
|
|
||||
result = base and base != ObjectInternal::unknown()
|
||||
or
|
||||
@@ -2223,7 +2224,10 @@ module Types {
|
||||
}
|
||||
|
||||
private ControlFlowNode decorator_call_callee(PythonClassObjectInternal cls) {
|
||||
result = cls.getScope().getADecorator().getAFlowNode().(CallNode).getFunction()
|
||||
exists(CallNode deco |
|
||||
deco.getNode() = cls.getScope().getADecorator() and
|
||||
result = deco.getFunction()
|
||||
)
|
||||
}
|
||||
|
||||
private boolean has_six_add_metaclass(PythonClassObjectInternal cls) {
|
||||
@@ -2262,7 +2266,7 @@ module Types {
|
||||
}
|
||||
|
||||
private EssaVariable metaclass_var(Class cls) {
|
||||
result.getASourceUse() = cls.getMetaClass().getAFlowNode()
|
||||
result.getASourceUse().getNode() = cls.getMetaClass()
|
||||
or
|
||||
major_version() = 2 and
|
||||
not exists(cls.getMetaClass()) and
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
*/
|
||||
|
||||
import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
|
||||
@@ -181,7 +181,7 @@ class ClassObject extends Object {
|
||||
)
|
||||
}
|
||||
|
||||
ControlFlowNode declaredMetaClass() { result = this.getPyClass().getMetaClass().getAFlowNode() }
|
||||
ControlFlowNode declaredMetaClass() { result.getNode() = this.getPyClass().getMetaClass() }
|
||||
|
||||
/** Has type inference failed to compute the full class hierarchy for this class for the reason given. */
|
||||
predicate failedInference(string reason) { Types::failedInference(this.theClass(), reason) }
|
||||
@@ -195,8 +195,9 @@ class ClassObject extends Object {
|
||||
* It is guaranteed that getProbableSingletonInstance() returns at most one Object for each ClassObject.
|
||||
*/
|
||||
Object getProbableSingletonInstance() {
|
||||
exists(ControlFlowNodeWithPointsTo use, Expr origin |
|
||||
use.refersTo(result, this, origin.getAFlowNode())
|
||||
exists(ControlFlowNodeWithPointsTo use, Expr origin, ControlFlowNode origin_ |
|
||||
origin_.getNode() = origin and
|
||||
use.refersTo(result, this, origin_)
|
||||
|
|
||||
this.hasStaticallyUniqueInstance() and
|
||||
/* Ensure that original expression will be executed only one. */
|
||||
|
||||
@@ -427,7 +427,7 @@ class ExceptFlowNodeWithPointsTo extends ExceptFlowNode {
|
||||
}
|
||||
|
||||
private ControlFlowNodeWithPointsTo element_from_tuple_objectapi(Object tuple) {
|
||||
exists(Tuple t | t = tuple.getOrigin() and result = t.getAnElt().getAFlowNode())
|
||||
exists(Tuple t | t = tuple.getOrigin() and result.getNode() = t.getAnElt())
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -36,8 +36,8 @@ class RangeIterationVariableFact extends PointsToExtension {
|
||||
RangeIterationVariableFact() {
|
||||
exists(For f, ControlFlowNode iterable |
|
||||
iterable.getBasicBlock().dominates(this.(ControlFlowNode).getBasicBlock()) and
|
||||
f.getIter().getAFlowNode() = iterable and
|
||||
f.getTarget().getAFlowNode() = this and
|
||||
iterable.getNode() = f.getIter() and
|
||||
this.(ControlFlowNode).getNode() = f.getTarget() and
|
||||
exists(ObjectInternal range |
|
||||
PointsTo::pointsTo(iterable, _, range, _) and
|
||||
range.getClass() = ObjectInternal::builtin("range")
|
||||
|
||||
@@ -137,7 +137,10 @@ class PyFunctionObject extends FunctionObject {
|
||||
|
||||
/** Gets a control flow node corresponding to the value of a return statement */
|
||||
ControlFlowNodeWithPointsTo getAReturnedNode() {
|
||||
result = this.getFunction().getAReturnValueFlowNode()
|
||||
exists(Return ret |
|
||||
ret.getScope() = this.getFunction() and
|
||||
result.getNode() = ret.getValue()
|
||||
)
|
||||
}
|
||||
|
||||
override string descriptiveString() {
|
||||
@@ -170,7 +173,7 @@ class PyFunctionObject extends FunctionObject {
|
||||
predicate unconditionallyReturnsParameter(int n) {
|
||||
exists(SsaVariable pvar |
|
||||
exists(Parameter p | p = this.getFunction().getArg(n) |
|
||||
p.asName().getAFlowNode() = pvar.getDefinition()
|
||||
pvar.getDefinition().getNode() = p.asName()
|
||||
) and
|
||||
exists(NameNode rval |
|
||||
rval = pvar.getAUse() and
|
||||
|
||||
@@ -337,7 +337,7 @@ class TupleObject extends SequenceObject {
|
||||
or
|
||||
this instanceof TupleNode
|
||||
or
|
||||
exists(Function func | func.getVararg().getAFlowNode() = this)
|
||||
exists(Function func | this.(ControlFlowNode).getNode() = func.getVararg())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,7 +352,9 @@ module TupleObject {
|
||||
}
|
||||
|
||||
class NonEmptyTupleObject extends TupleObject {
|
||||
NonEmptyTupleObject() { exists(Function func | func.getVararg().getAFlowNode() = this) }
|
||||
NonEmptyTupleObject() {
|
||||
exists(Function func | this.(ControlFlowNode).getNode() = func.getVararg())
|
||||
}
|
||||
|
||||
override boolean booleanValue() { result = true }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user