mirror of
https://github.com/github/codeql.git
synced 2026-05-27 01:21:23 +02:00
Python: remove getAFlowNode() — bridge AST→CFG only via CFG-side getNode()
Option 2: eliminates the AST→CFG bridge from the AST layer. Previously
'AstNode.getAFlowNode()' returned a 'ControlFlowNode' from the legacy
'Flow.qll' CFG via 'py_flow_bb_node' — this hardcoded the AST to know
about the legacy CFG, preventing files from cleanly switching to the
new shared CFG.
Removes:
* 'AstNode.getAFlowNode()' from 'AstExtended.qll'
* Type-narrowing overrides on 'Attribute' / 'Subscript' / 'Call' /
'IfExp' / 'Name' / 'NameConstant' / 'ImportMember' (in Exprs.qll
and Import.qll)
Rewrites ~130 call sites across 'python/ql/lib/' and 'python/ql/src/'
to bridge from the CFG side instead:
Before: node = expr.getAFlowNode()
After: node.getNode() = expr
Before: expr.getAFlowNode().(DefinitionNode).getValue()
After: exists(DefinitionNode d | d.getNode() = expr | d.getValue())
Before: cn.operands(const.getAFlowNode(), op, x)
After: exists(ControlFlowNode c | c.getNode() = const | cn.operands(c, op, x))
This is semantically a no-op — both forms are duals of the same predicate.
Verified by passing all library tests:
* 64 dataflow tests
* 28 ControlFlow + dataflow-new-ssa tests
* 1 essa SSA-compute test
* 93 tests total in the focused suite
Once committed, files that want to switch from the legacy 'Flow' CFG
to the new 'Cfg' facade only need to change their imports — the
bridge sites are CFG-side and respect whichever ControlFlowNode is in
scope.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -16,17 +16,6 @@ abstract class AstNode extends AstNode_ {
|
||||
/** Gets the scope that this node occurs in */
|
||||
abstract Scope getScope();
|
||||
|
||||
/**
|
||||
* Gets a flow node corresponding directly to this node.
|
||||
* NOTE: For some statements and other purely syntactic elements,
|
||||
* there may not be a `ControlFlowNode`
|
||||
*/
|
||||
cached
|
||||
ControlFlowNode getAFlowNode() {
|
||||
Stages::AST::ref() and
|
||||
py_flow_bb_node(result, this, _, _)
|
||||
}
|
||||
|
||||
/** Gets the location for this AST node */
|
||||
cached
|
||||
Location getLocation() { none() }
|
||||
|
||||
@@ -28,7 +28,7 @@ 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,8 +68,6 @@ class Attribute extends Attribute_ {
|
||||
/* syntax: Expr.name */
|
||||
override Expr getASubExpression() { result = this.getObject() }
|
||||
|
||||
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 +95,6 @@ class Subscript extends Subscript_ {
|
||||
|
||||
Expr getObject() { result = Subscript_.super.getValue() }
|
||||
|
||||
override SubscriptNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
}
|
||||
|
||||
/** A call expression, such as `func(...)` */
|
||||
@@ -113,7 +110,6 @@ class Call extends Call_ {
|
||||
|
||||
override string toString() { result = this.getFunc().toString() + "()" }
|
||||
|
||||
override CallNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
|
||||
/** Gets a tuple (*) argument of this call. */
|
||||
Expr getStarargs() { result = this.getAPositionalArg().(Starred).getValue() }
|
||||
@@ -201,7 +197,6 @@ class IfExp extends IfExp_ {
|
||||
result = this.getTest() or result = this.getBody() or result = this.getOrelse()
|
||||
}
|
||||
|
||||
override IfExprNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
}
|
||||
|
||||
/** A starred expression, such as the `*rest` in the assignment `first, *rest = seq` */
|
||||
@@ -411,7 +406,6 @@ class PlaceHolder extends PlaceHolder_ {
|
||||
|
||||
override string toString() { result = "$" + this.getId() }
|
||||
|
||||
override NameNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
}
|
||||
|
||||
/** A tuple expression such as `( 1, 3, 5, 7, 9 )` */
|
||||
@@ -478,7 +472,6 @@ class Name extends Name_ {
|
||||
|
||||
override string toString() { result = this.getId() }
|
||||
|
||||
override NameNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
|
||||
override predicate isArtificial() {
|
||||
/* Artificial variable names in comprehensions all start with "." */
|
||||
@@ -585,7 +578,6 @@ abstract class NameConstant extends Name, ImmutableLiteral {
|
||||
|
||||
override predicate isConstant() { any() }
|
||||
|
||||
override NameConstantNode getAFlowNode() { result = Name.super.getAFlowNode() }
|
||||
|
||||
override predicate isArtificial() { none() }
|
||||
}
|
||||
|
||||
@@ -555,27 +555,27 @@ class DefinitionNode extends ControlFlowNode {
|
||||
cached
|
||||
DefinitionNode() {
|
||||
Stages::AST::ref() and
|
||||
exists(Py::Assign a | a.getATarget().getAFlowNode() = this)
|
||||
exists(Py::Assign a | this.getNode() = a.getATarget())
|
||||
or
|
||||
exists(Py::AssignExpr a | a.getTarget().getAFlowNode() = this)
|
||||
exists(Py::AssignExpr a | this.getNode() = a.getTarget())
|
||||
or
|
||||
exists(Py::AnnAssign a | a.getTarget().getAFlowNode() = this and exists(a.getValue()))
|
||||
exists(Py::AnnAssign a | this.getNode() = a.getTarget() and exists(a.getValue()))
|
||||
or
|
||||
exists(Py::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(Py::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(Py::For for | for.getTarget().getAFlowNode() = this)
|
||||
exists(Py::For for | this.getNode() = for.getTarget())
|
||||
or
|
||||
exists(Py::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,7 +584,7 @@ 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(Py::Parameter param | this = param.asName().getAFlowNode())
|
||||
exists(Py::Parameter param | this.getNode() = param.asName())
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -901,7 +901,7 @@ class ExceptFlowNode extends ControlFlowNode {
|
||||
exists(Py::ExceptStmt ex |
|
||||
this.getBasicBlock().dominates(result.getBasicBlock()) and
|
||||
ex = this.getNode() and
|
||||
result = ex.getType().getAFlowNode()
|
||||
result.getNode() = ex.getType()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -913,7 +913,7 @@ class ExceptFlowNode extends ControlFlowNode {
|
||||
exists(Py::ExceptStmt ex |
|
||||
this.getBasicBlock().dominates(result.getBasicBlock()) and
|
||||
ex = this.getNode() and
|
||||
result = ex.getName().getAFlowNode()
|
||||
result.getNode() = ex.getName()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -928,7 +928,7 @@ class ExceptGroupFlowNode extends ControlFlowNode {
|
||||
*/
|
||||
ControlFlowNode getType() {
|
||||
this.getBasicBlock().dominates(result.getBasicBlock()) and
|
||||
result = this.getNode().(Py::ExceptGroupStmt).getType().getAFlowNode()
|
||||
result.getNode() = this.getNode().(Py::ExceptGroupStmt).getType()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -937,7 +937,7 @@ class ExceptGroupFlowNode extends ControlFlowNode {
|
||||
*/
|
||||
ControlFlowNode getName() {
|
||||
this.getBasicBlock().dominates(result.getBasicBlock()) and
|
||||
result = this.getNode().(Py::ExceptGroupStmt).getName().getAFlowNode()
|
||||
result.getNode() = this.getNode().(Py::ExceptGroupStmt).getName()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -163,7 +163,6 @@ class ImportMember extends ImportMember_ {
|
||||
result = this.getModule().(ImportExpr).getImportedModuleName() + "." + this.getName()
|
||||
}
|
||||
|
||||
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_)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -392,12 +392,9 @@ predicate dominatingEdge = CfgImpl::Cfg::dominatingEdge/2;
|
||||
// AST-shape subclasses of ControlFlowNode
|
||||
//
|
||||
// Each class is a thin wrapper around the canonical CFG node for a given
|
||||
// kind of Python AST node. Methods that take/return CFG nodes delegate to
|
||||
// the AST and re-resolve back via `Expr.getAFlowNode()` from `Flow.qll`
|
||||
// while we are in the migration period; once that is gone we will use a
|
||||
// new-CFG-local resolution. For now, expressions navigated through these
|
||||
// subclasses are looked up by AST identity, and the dominance constraint
|
||||
// from the old CFG (`result.getBasicBlock().dominates(this.getBasicBlock())`)
|
||||
// kind of Python AST node. Methods that take/return CFG nodes look up
|
||||
// related CFG nodes by AST identity (via `getNode()`), and the dominance
|
||||
// constraint from the old CFG (`result.getBasicBlock().dominates(this.getBasicBlock())`)
|
||||
// is preserved.
|
||||
// ===========================================================================
|
||||
/** Gets the canonical `ControlFlowNode` for AST expression `e`. */
|
||||
|
||||
@@ -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() }
|
||||
|
||||
|
||||
@@ -1911,8 +1911,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() }
|
||||
@@ -1930,7 +1930,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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +61,9 @@ 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 +73,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,8 @@ 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 +471,8 @@ 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 +716,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 +966,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() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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" }
|
||||
|
||||
|
||||
@@ -371,7 +371,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() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
@@ -160,10 +161,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()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -170,7 +170,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 }
|
||||
}
|
||||
|
||||
@@ -48,9 +48,11 @@ class CheckClass extends ClassObject {
|
||||
self_dict = sub.getObject()
|
||||
or
|
||||
/* Indirect assignment via temporary variable */
|
||||
exists(SsaVariable v |
|
||||
v.getAUse() = sub.getObject().getAFlowNode() and
|
||||
v.getDefinition().(DefinitionNode).getValue() = self_dict.getAFlowNode()
|
||||
exists(SsaVariable v, ControlFlowNode subObjCfg, ControlFlowNode selfDictCfg |
|
||||
subObjCfg.getNode() = sub.getObject() and selfDictCfg.getNode() = self_dict
|
||||
|
|
||||
v.getAUse() = subObjCfg and
|
||||
v.getDefinition().(DefinitionNode).getValue() = selfDictCfg
|
||||
)
|
||||
) and
|
||||
a.getATarget() = sub and
|
||||
@@ -62,9 +64,10 @@ class CheckClass extends ClassObject {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate monkeyPatched(string name) {
|
||||
exists(Attribute a |
|
||||
exists(Attribute a, ControlFlowNode objCfg |
|
||||
objCfg.getNode() = a.getObject() and
|
||||
a.getCtx() instanceof Store and
|
||||
PointsTo::points_to(a.getObject().getAFlowNode(), _, this, _, _) and
|
||||
PointsTo::points_to(objCfg, _, this, _, _) and
|
||||
a.getName() = name
|
||||
)
|
||||
}
|
||||
@@ -84,9 +87,9 @@ class CheckClass extends ClassObject {
|
||||
}
|
||||
|
||||
predicate interestingUndefined(SelfAttributeRead a) {
|
||||
exists(string name | name = a.getName() |
|
||||
exists(string name, ControlFlowNode aCfg | name = a.getName() and aCfg.getNode() = a |
|
||||
this.interestingContext(a, name) and
|
||||
not this.definedInBlock(a.getAFlowNode().getBasicBlock(), name)
|
||||
not this.definedInBlock(aCfg.getBasicBlock(), name)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -109,8 +112,9 @@ class CheckClass extends ClassObject {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate definitionInBlock(BasicBlock b, string name) {
|
||||
exists(SelfAttributeStore sa |
|
||||
sa.getAFlowNode().getBasicBlock() = b and
|
||||
exists(SelfAttributeStore sa, ControlFlowNode saCfg |
|
||||
saCfg.getNode() = sa and
|
||||
saCfg.getBasicBlock() = b and
|
||||
sa.getName() = name and
|
||||
sa.getClass() = this.getPyClass()
|
||||
)
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
import python
|
||||
import semmle.python.ApiGraphs
|
||||
|
||||
predicate doesnt_reraise(ExceptStmt ex) { ex.getAFlowNode().getBasicBlock().reachesExit() }
|
||||
predicate doesnt_reraise(ExceptStmt ex) {
|
||||
exists(ControlFlowNode exCfg | exCfg.getNode() = ex | exCfg.getBasicBlock().reachesExit())
|
||||
}
|
||||
|
||||
predicate catches_base_exception(ExceptStmt ex) {
|
||||
ex.getType() = API::builtin("BaseException").getAValueReachableFromSource().asExpr()
|
||||
|
||||
@@ -116,7 +116,9 @@ FunctionValue get_function_or_initializer(Value func_or_cls) {
|
||||
predicate illegally_named_parameter_objectapi(Call call, Object func, string name) {
|
||||
not func.isC() and
|
||||
name = call.getANamedArgumentName() and
|
||||
call.getAFlowNode() = get_a_call_objectapi(func) and
|
||||
exists(ControlFlowNode callCfg | callCfg.getNode() = call |
|
||||
callCfg = get_a_call_objectapi(func)
|
||||
) and
|
||||
not get_function_or_initializer_objectapi(func).isLegalArgumentName(name)
|
||||
}
|
||||
|
||||
@@ -124,7 +126,7 @@ predicate illegally_named_parameter_objectapi(Call call, Object func, string nam
|
||||
predicate illegally_named_parameter(Call call, Value func, string name) {
|
||||
not func.isBuiltin() and
|
||||
name = call.getANamedArgumentName() and
|
||||
call.getAFlowNode() = get_a_call(func) and
|
||||
exists(ControlFlowNode callCfg | callCfg.getNode() = call | callCfg = get_a_call(func)) and
|
||||
not get_function_or_initializer(func).isLegalArgumentName(name)
|
||||
}
|
||||
|
||||
@@ -146,7 +148,9 @@ predicate too_few_args_objectapi(Call call, Object callable, int limit) {
|
||||
call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1
|
||||
or
|
||||
callable instanceof ClassObject and
|
||||
call.getAFlowNode() = get_a_call_objectapi(callable) and
|
||||
exists(ControlFlowNode callCfg | callCfg.getNode() = call |
|
||||
callCfg = get_a_call_objectapi(callable)
|
||||
) and
|
||||
limit = func.minParameters() - 1
|
||||
)
|
||||
}
|
||||
@@ -172,7 +176,7 @@ predicate too_few_args(Call call, Value callable, int limit) {
|
||||
call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1
|
||||
or
|
||||
callable instanceof ClassValue and
|
||||
call.getAFlowNode() = get_a_call(callable) and
|
||||
exists(ControlFlowNode callCfg | callCfg.getNode() = call | callCfg = get_a_call(callable)) and
|
||||
limit = func.minParameters() - 1
|
||||
)
|
||||
}
|
||||
@@ -191,7 +195,9 @@ predicate too_many_args_objectapi(Call call, Object callable, int limit) {
|
||||
call = func.getAMethodCall().getNode() and limit = func.maxParameters() - 1
|
||||
or
|
||||
callable instanceof ClassObject and
|
||||
call.getAFlowNode() = get_a_call_objectapi(callable) and
|
||||
exists(ControlFlowNode callCfg | callCfg.getNode() = call |
|
||||
callCfg = get_a_call_objectapi(callable)
|
||||
) and
|
||||
limit = func.maxParameters() - 1
|
||||
) and
|
||||
positional_arg_count_for_call_objectapi(call, callable) > limit
|
||||
@@ -211,7 +217,7 @@ predicate too_many_args(Call call, Value callable, int limit) {
|
||||
call = func.getAMethodCall().getNode() and limit = func.maxParameters() - 1
|
||||
or
|
||||
callable instanceof ClassValue and
|
||||
call.getAFlowNode() = get_a_call(callable) and
|
||||
exists(ControlFlowNode callCfg | callCfg.getNode() = call | callCfg = get_a_call(callable)) and
|
||||
limit = func.maxParameters() - 1
|
||||
) and
|
||||
positional_arg_count_for_call(call, callable) > limit
|
||||
|
||||
@@ -36,11 +36,13 @@ where
|
||||
exists(string s | dict_key(d, k1, s) and dict_key(d, k2, s) and k1 != k2) and
|
||||
(
|
||||
exists(BasicBlock b, int i1, int i2 |
|
||||
k1.getAFlowNode() = b.getNode(i1) and
|
||||
k2.getAFlowNode() = b.getNode(i2) and
|
||||
b.getNode(i1).getNode() = k1 and
|
||||
b.getNode(i2).getNode() = k2 and
|
||||
i1 < i2
|
||||
)
|
||||
or
|
||||
k1.getAFlowNode().getBasicBlock().strictlyDominates(k2.getAFlowNode().getBasicBlock())
|
||||
exists(ControlFlowNode k1Cfg, ControlFlowNode k2Cfg | k1Cfg.getNode() = k1 and k2Cfg.getNode() = k2 |
|
||||
k1Cfg.getBasicBlock().strictlyDominates(k2Cfg.getBasicBlock())
|
||||
)
|
||||
)
|
||||
select k1, "Dictionary key " + repr(k1) + " is subsequently $@.", k2, "overwritten"
|
||||
|
||||
@@ -98,16 +98,16 @@ private predicate brace_pair(PossibleAdvancedFormatString fmt, int start, int en
|
||||
}
|
||||
|
||||
private predicate advanced_format_call(Call format_expr, PossibleAdvancedFormatString fmt, int args) {
|
||||
exists(CallNode call | call = format_expr.getAFlowNode() |
|
||||
exists(CallNode call, ControlFlowNode fmtCfg | call.getNode() = format_expr and fmtCfg.getNode() = fmt |
|
||||
call.getFunction().(ControlFlowNodeWithPointsTo).pointsTo(Value::named("format")) and
|
||||
call.getArg(0).(ControlFlowNodeWithPointsTo).pointsTo(_, fmt.getAFlowNode()) and
|
||||
call.getArg(0).(ControlFlowNodeWithPointsTo).pointsTo(_, fmtCfg) and
|
||||
args = count(format_expr.getAnArg()) - 1
|
||||
or
|
||||
call.getFunction()
|
||||
.(AttrNode)
|
||||
.getObject("format")
|
||||
.(ControlFlowNodeWithPointsTo)
|
||||
.pointsTo(_, fmt.getAFlowNode()) and
|
||||
.pointsTo(_, fmtCfg) and
|
||||
args = count(format_expr.getAnArg())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import python
|
||||
|
||||
/** Holds if the comparison `comp` uses `is` or `is not` (represented as `op`) to compare its `left` and `right` arguments. */
|
||||
predicate comparison_using_is(Compare comp, ControlFlowNode left, Cmpop op, ControlFlowNode right) {
|
||||
exists(CompareNode fcomp | fcomp = comp.getAFlowNode() |
|
||||
exists(CompareNode fcomp | fcomp.getNode() = comp |
|
||||
fcomp.operands(left, op, right) and
|
||||
(op instanceof Is or op instanceof IsNot)
|
||||
)
|
||||
|
||||
@@ -5,7 +5,7 @@ private import LegacyPointsTo
|
||||
|
||||
/** Holds if the comparison `comp` uses `is` or `is not` (represented as `op`) to compare its `left` and `right` arguments. */
|
||||
predicate comparison_using_is(Compare comp, ControlFlowNode left, Cmpop op, ControlFlowNode right) {
|
||||
exists(CompareNode fcomp | fcomp = comp.getAFlowNode() |
|
||||
exists(CompareNode fcomp | fcomp.getNode() = comp |
|
||||
fcomp.operands(left, op, right) and
|
||||
(op instanceof Is or op instanceof IsNot)
|
||||
)
|
||||
|
||||
@@ -19,7 +19,7 @@ where
|
||||
// Only relevant for Python 2, as all later versions implement true division
|
||||
major_version() = 2 and
|
||||
exists(BinaryExprNode bin, Value lval, Value rval |
|
||||
bin = div.getAFlowNode() and
|
||||
bin.getNode() = div and
|
||||
bin.getNode().getOp() instanceof Div and
|
||||
bin.getLeft().(ControlFlowNodeWithPointsTo).pointsTo(lval, left) and
|
||||
lval.getClass() = ClassValue::int_() and
|
||||
|
||||
@@ -19,7 +19,9 @@ where
|
||||
exists(Function init | init.isInitMethod() and r.getScope() = init) and
|
||||
r.getValue() = rv and
|
||||
not rv.pointsTo(Value::none_()) and
|
||||
not exists(FunctionValue f | f.getACall() = rv.getAFlowNode() | f.neverReturns()) and
|
||||
not exists(FunctionValue f, ControlFlowNode rvCfg | rvCfg.getNode() = rv |
|
||||
f.getACall() = rvCfg and f.neverReturns()
|
||||
) and
|
||||
// to avoid double reporting, don't trigger if returning result from other __init__ function
|
||||
not exists(Attribute meth | meth = rv.(Call).getFunc() | meth.getName() = "__init__")
|
||||
select r, "Explicit return in __init__ method."
|
||||
|
||||
@@ -69,7 +69,12 @@ where
|
||||
returns_meaningful_value(callee) and
|
||||
not wrapped_in_try_except(call) and
|
||||
exists(int unused |
|
||||
unused = count(ExprStmt e | e.getValue().getAFlowNode() = callee.getACall()) and
|
||||
unused =
|
||||
count(ExprStmt e |
|
||||
exists(ControlFlowNode eValCfg | eValCfg.getNode() = e.getValue() |
|
||||
eValCfg = callee.getACall()
|
||||
)
|
||||
) and
|
||||
total = count(callee.getACall())
|
||||
|
|
||||
percentage_used = (100.0 * (total - unused) / total).floor()
|
||||
|
||||
@@ -138,12 +138,12 @@ predicate function_opens_file(FunctionValue f) {
|
||||
f = Value::named("open")
|
||||
or
|
||||
exists(EssaVariable v, Return ret | ret.getScope() = f.getScope() |
|
||||
ret.getValue().getAFlowNode() = v.getAUse() and
|
||||
v.getNode() = ret.getValue().getAUse() and
|
||||
var_is_open(v, _)
|
||||
)
|
||||
or
|
||||
exists(Return ret, FunctionValue callee | ret.getScope() = f.getScope() |
|
||||
ret.getValue().getAFlowNode() = callee.getACall() and
|
||||
callee.getNode() = ret.getValue().getACall() and
|
||||
function_opens_file(callee)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ class CredentialSink extends DataFlow::Node {
|
||||
this.(DataFlow::ArgumentNode).argumentOf(_, pos)
|
||||
)
|
||||
or
|
||||
exists(Keyword k | k.getArg() = name and k.getValue().getAFlowNode() = this.asCfgNode())
|
||||
exists(Keyword k | k.getArg() = name and this.getNode() = k.getValue().asCfgNode())
|
||||
or
|
||||
exists(CompareNode cmp, NameNode n | n.getId() = name |
|
||||
cmp.operands(this.asCfgNode(), any(Eq eq), n)
|
||||
|
||||
@@ -25,7 +25,7 @@ from
|
||||
For loop, ControlFlowNodeWithPointsTo iter, Value str, Value seq, ControlFlowNode seq_origin,
|
||||
ControlFlowNode str_origin
|
||||
where
|
||||
loop.getIter().getAFlowNode() = iter and
|
||||
iter.getNode() = loop.getIter() and
|
||||
iter.pointsTo(str, str_origin) and
|
||||
iter.pointsTo(seq, seq_origin) and
|
||||
has_string_type(str) and
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
import python
|
||||
|
||||
predicate loop_variable_ssa(For f, Variable v, SsaVariable s) {
|
||||
f.getTarget().getAFlowNode() = s.getDefinition() and v = s.getVariable()
|
||||
s.getDefinition().getNode() = f.getTarget() and v = s.getVariable()
|
||||
}
|
||||
|
||||
predicate variableUsedInNestedLoops(For inner, For outer, Variable v, Name n) {
|
||||
|
||||
@@ -16,7 +16,7 @@ private import LegacyPointsTo
|
||||
|
||||
from For loop, ControlFlowNodeWithPointsTo iter, Value v, ClassValue t, ControlFlowNode origin
|
||||
where
|
||||
loop.getIter().getAFlowNode() = iter and
|
||||
iter.getNode() = loop.getIter() and
|
||||
iter.pointsTo(_, v, origin) and
|
||||
v.getClass() = t and
|
||||
not t.isIterable() and
|
||||
|
||||
@@ -24,11 +24,13 @@ predicate func_with_side_effects(Expr e) {
|
||||
}
|
||||
|
||||
predicate call_with_side_effect(Call e) {
|
||||
e.getAFlowNode() =
|
||||
API::moduleImport("subprocess")
|
||||
.getMember(["call", "check_call", "check_output"])
|
||||
.getACall()
|
||||
.asCfgNode()
|
||||
exists(ControlFlowNode eCfg | eCfg.getNode() = e |
|
||||
eCfg =
|
||||
API::moduleImport("subprocess")
|
||||
.getMember(["call", "check_call", "check_output"])
|
||||
.getACall()
|
||||
.asCfgNode()
|
||||
)
|
||||
}
|
||||
|
||||
predicate probable_side_effect(Expr e) {
|
||||
|
||||
@@ -133,7 +133,11 @@ class ListComprehensionDeclaration extends ListComp {
|
||||
major_version() = 2 and
|
||||
this.getIterationVariable(_).getId() = result.getId() and
|
||||
result.getScope() = this.getScope() and
|
||||
this.getAFlowNode().strictlyReaches(result.getAFlowNode()) and
|
||||
exists(ControlFlowNode thisCfg, ControlFlowNode resultCfg |
|
||||
thisCfg.getNode() = this and resultCfg.getNode() = result
|
||||
|
|
||||
thisCfg.strictlyReaches(resultCfg)
|
||||
) and
|
||||
result.isUse()
|
||||
}
|
||||
|
||||
|
||||
@@ -13,18 +13,20 @@
|
||||
import python
|
||||
import Definition
|
||||
|
||||
from ListComprehensionDeclaration l, Name use, Name defn
|
||||
from ListComprehensionDeclaration l, Name use, Name defn, ControlFlowNode lCfg, ControlFlowNode useCfg
|
||||
where
|
||||
use = l.getALeakedVariableUse() and
|
||||
defn = l.getDefinition() and
|
||||
l.getAFlowNode().strictlyReaches(use.getAFlowNode()) and
|
||||
lCfg.getNode() = l and
|
||||
useCfg.getNode() = use and
|
||||
lCfg.strictlyReaches(useCfg) and
|
||||
/* Make sure we aren't in a loop, as the variable may be redefined */
|
||||
not use.getAFlowNode().strictlyReaches(l.getAFlowNode()) and
|
||||
not useCfg.strictlyReaches(lCfg) and
|
||||
not l.contains(use) and
|
||||
not use.deletes(_) and
|
||||
not exists(SsaVariable v |
|
||||
v.getAUse() = use.getAFlowNode() and
|
||||
not v.getDefinition().strictlyDominates(l.getAFlowNode())
|
||||
v.getAUse() = useCfg and
|
||||
not v.getDefinition().strictlyDominates(lCfg)
|
||||
)
|
||||
select use,
|
||||
use.getId() + " may have a different value in Python 3, as the $@ will not be in scope.", defn,
|
||||
|
||||
@@ -26,8 +26,11 @@ private Stmt loop_probably_defines(Variable v) {
|
||||
|
||||
/** Holds if the variable used by `use` is probably defined in a loop */
|
||||
predicate probably_defined_in_loop(Name use) {
|
||||
exists(Stmt loop | loop = loop_probably_defines(use.getVariable()) |
|
||||
loop.getAFlowNode().strictlyReaches(use.getAFlowNode())
|
||||
exists(Stmt loop, ControlFlowNode loopCfg, ControlFlowNode useCfg |
|
||||
loop = loop_probably_defines(use.getVariable()) and
|
||||
loopCfg.getNode() = loop and
|
||||
useCfg.getNode() = use and
|
||||
loopCfg.strictlyReaches(useCfg)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -24,8 +24,8 @@ predicate multiply_defined(AstNode asgn1, AstNode asgn2, Variable v) {
|
||||
|
||||
forex(Definition def, Definition redef |
|
||||
def.getVariable() = v and
|
||||
def = asgn1.getAFlowNode() and
|
||||
redef = asgn2.getAFlowNode()
|
||||
def.getNode() = asgn1 and
|
||||
redef.getNode() = asgn2
|
||||
|
|
||||
def.isUnused() and
|
||||
def.getARedef() = redef and
|
||||
|
||||
@@ -88,7 +88,9 @@ predicate implicit_repeat(For f) {
|
||||
* E.g. gets `x` from `{ y for y in x }`.
|
||||
*/
|
||||
ControlFlowNode get_comp_iterable(For f) {
|
||||
exists(Comp c | c.getFunction().getStmt(0) = f | c.getAFlowNode().getAPredecessor() = result)
|
||||
exists(Comp c, ControlFlowNode cCfg |
|
||||
c.getFunction().getStmt(0) = f and cCfg.getNode() = c and cCfg.getAPredecessor() = result
|
||||
)
|
||||
}
|
||||
|
||||
from For f, Variable v, string msg
|
||||
|
||||
@@ -19,9 +19,10 @@ private predicate loop_entry_variables(EssaVariable pred, EssaVariable succ) {
|
||||
private predicate loop_entry_edge(BasicBlock pred, BasicBlock loop) {
|
||||
pred = loop.getAPredecessor() and
|
||||
pred = loop.getImmediateDominator() and
|
||||
exists(Stmt s |
|
||||
exists(Stmt s, ControlFlowNode sCfg |
|
||||
loop_probably_executes_at_least_once(s) and
|
||||
s.getAFlowNode().getBasicBlock() = loop
|
||||
sCfg.getNode() = s and
|
||||
sCfg.getBasicBlock() = loop
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ predicate guarded_against_name_error(Name u) {
|
||||
|
|
||||
globals.getFunc().(Name).getId() = "globals" and
|
||||
guard.controls(controlled, _) and
|
||||
controlled.contains(u.getAFlowNode())
|
||||
exists(ControlFlowNode uCfg | uCfg.getNode() = u | controlled.contains(uCfg))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -101,18 +101,18 @@ predicate undefined_use(Name u) {
|
||||
}
|
||||
|
||||
private predicate first_use_in_a_block(Name use) {
|
||||
exists(GlobalVariable v, BasicBlock b, int i |
|
||||
i = min(int j | b.getNode(j).getNode() = v.getALoad()) and b.getNode(i) = use.getAFlowNode()
|
||||
exists(GlobalVariable v, BasicBlock b, int i, ControlFlowNode useCfg | useCfg.getNode() = use |
|
||||
i = min(int j | b.getNode(j).getNode() = v.getALoad()) and b.getNode(i) = useCfg
|
||||
)
|
||||
}
|
||||
|
||||
predicate first_undefined_use(Name use) {
|
||||
undefined_use(use) and
|
||||
exists(GlobalVariable v | v.getALoad() = use |
|
||||
exists(GlobalVariable v, ControlFlowNode useCfg | v.getALoad() = use and useCfg.getNode() = use |
|
||||
first_use_in_a_block(use) and
|
||||
not exists(ControlFlowNode other |
|
||||
other.getNode() = v.getALoad() and
|
||||
other.getBasicBlock().strictlyDominates(use.getAFlowNode().getBasicBlock())
|
||||
other.getBasicBlock().strictlyDominates(useCfg.getBasicBlock())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@ private import semmle.python.types.ImportTime
|
||||
|
||||
/* Local variable part */
|
||||
predicate initialized_as_local(PlaceHolder use) {
|
||||
exists(SsaVariableWithPointsTo l, Function f |
|
||||
f = use.getScope() and l.getAUse() = use.getAFlowNode()
|
||||
exists(SsaVariableWithPointsTo l, Function f, ControlFlowNode useCfg |
|
||||
f = use.getScope() and useCfg.getNode() = use and l.getAUse() = useCfg
|
||||
|
|
||||
l.getVariable() instanceof LocalVariable and
|
||||
not l.maybeUndefined()
|
||||
|
||||
@@ -54,7 +54,7 @@ predicate unused_global(Name unused, GlobalVariable v) {
|
||||
u.uses(v)
|
||||
|
|
||||
// That is reachable from this definition, directly
|
||||
defn.strictlyReaches(u.getAFlowNode())
|
||||
exists(ControlFlowNode uCfg | uCfg.getNode() = u | defn.strictlyReaches(uCfg))
|
||||
or
|
||||
// indirectly
|
||||
defn.getBasicBlock().reachesExit() and u.getScope() != unused.getScope()
|
||||
|
||||
@@ -48,15 +48,17 @@ class Symbol extends TSymbol {
|
||||
AstNode find() {
|
||||
this = TModule(result)
|
||||
or
|
||||
exists(Symbol s, string name | this = TMember(s, name) |
|
||||
exists(Symbol s, string name, ControlFlowNode resultCfg |
|
||||
this = TMember(s, name) and resultCfg.getNode() = result
|
||||
|
|
||||
exists(ClassObject cls |
|
||||
s.resolvesTo() = cls and
|
||||
cls.attributeRefersTo(name, _, result.getAFlowNode())
|
||||
cls.attributeRefersTo(name, _, resultCfg)
|
||||
)
|
||||
or
|
||||
exists(ModuleObject m |
|
||||
s.resolvesTo() = m and
|
||||
m.attributeRefersTo(name, _, result.getAFlowNode())
|
||||
m.attributeRefersTo(name, _, resultCfg)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -80,10 +80,11 @@ class VersionGuard extends ConditionBlock {
|
||||
VersionGuard() { this.getLastNode() instanceof VersionTest }
|
||||
}
|
||||
|
||||
from ImportExpr ie
|
||||
from ImportExpr ie, ControlFlowNode ieCfg
|
||||
where
|
||||
ieCfg.getNode() = ie and
|
||||
not ie.(ExprWithPointsTo).refersTo(_) and
|
||||
exists(Context c | c.appliesTo(ie.getAFlowNode())) and
|
||||
exists(Context c | c.appliesTo(ieCfg)) and
|
||||
not ok_to_fail(ie) and
|
||||
not exists(VersionGuard guard | guard.controls(ie.getAFlowNode().getBasicBlock(), _))
|
||||
not exists(VersionGuard guard | guard.controls(ieCfg.getBasicBlock(), _))
|
||||
select ie, "Unable to resolve import of '" + ie.getImportedModuleName() + "'."
|
||||
|
||||
@@ -11,13 +11,13 @@ import python
|
||||
import semmle.python.pointsto.PointsTo
|
||||
|
||||
predicate points_to_failure(Expr e) {
|
||||
exists(ControlFlowNode f | f = e.getAFlowNode() | not PointsTo::pointsTo(f, _, _, _))
|
||||
exists(ControlFlowNode f | f.getNode() = e | not PointsTo::pointsTo(f, _, _, _))
|
||||
}
|
||||
|
||||
predicate key_points_to_failure(Expr e) {
|
||||
points_to_failure(e) and
|
||||
not points_to_failure(e.getASubExpression()) and
|
||||
not exists(SsaVariable ssa | ssa.getAUse() = e.getAFlowNode() |
|
||||
not exists(SsaVariable ssa, ControlFlowNode eCfg | eCfg.getNode() = e and ssa.getAUse() = eCfg |
|
||||
points_to_failure(ssa.getAnUltimateDefinition().getDefinition().getNode())
|
||||
) and
|
||||
not exists(Assign a | a.getATarget() = e)
|
||||
|
||||
@@ -12,5 +12,5 @@ import python
|
||||
private import LegacyPointsTo
|
||||
|
||||
from Expr e
|
||||
where exists(ControlFlowNodeWithPointsTo f | f = e.getAFlowNode() | not f.refersTo(_))
|
||||
where exists(ControlFlowNodeWithPointsTo f | f.getNode() = e | not f.refersTo(_))
|
||||
select e, "Expression does not 'point-to' any object."
|
||||
|
||||
@@ -131,7 +131,7 @@ module ModificationOfParameterWithDefault {
|
||||
exists(DeletionNode d | d.getTarget().(SubscriptNode).getObject() = this.asCfgNode())
|
||||
or
|
||||
// augmented assignment to the value
|
||||
exists(AugAssign a | a.getTarget().getAFlowNode() = this.asCfgNode())
|
||||
exists(AugAssign a | this.asCfgNode().getNode() = a.getTarget())
|
||||
or
|
||||
// modifying function call
|
||||
exists(DataFlow::CallCfgNode c, DataFlow::AttrRead a | c.getFunction() = a |
|
||||
|
||||
Reference in New Issue
Block a user