Merge branch 'main' of https://github.com/github/codeql into oscarsj/mergeback-rc-3-20-into-main

This commit is contained in:
Óscar San José
2025-12-12 13:22:08 +01:00
1129 changed files with 32377 additions and 8332 deletions

View File

@@ -7,6 +7,7 @@
*/
import python
private import LegacyPointsTo
from Value len, CallNode call
where len.getName() = "len" and len.getACall() = call

View File

@@ -11,6 +11,7 @@
*/
import python
private import LegacyPointsTo
from ClassObject sub, ClassObject base
where

View File

@@ -7,6 +7,7 @@
*/
import python
private import LegacyPointsTo
from AstNode call, PythonFunctionValue method
where

View File

@@ -7,6 +7,7 @@
*/
import python
private import LegacyPointsTo
from FunctionObject m, FunctionObject n
where m != n and m.getACallee() = n and n.getACallee() = m

View File

@@ -7,6 +7,7 @@
*/
import python
private import LegacyPointsTo
from FunctionObject override, FunctionObject base
where

View File

@@ -7,6 +7,7 @@
*/
import python
private import LegacyPointsTo
from PythonFunctionValue f
where f.getACall().getScope() = f.getScope()

View File

@@ -20,8 +20,21 @@
*/
private import python
private import semmle.python.pointsto.PointsTo
private import semmle.python.objects.Modules
import semmle.python.pointsto.Base
import semmle.python.pointsto.Context
import semmle.python.pointsto.PointsTo
import semmle.python.pointsto.PointsToContext
import semmle.python.objects.ObjectAPI
import semmle.python.objects.ObjectInternal
import semmle.python.types.Object
import semmle.python.types.ClassObject
import semmle.python.types.FunctionObject
import semmle.python.types.ModuleObject
import semmle.python.types.Exceptions
import semmle.python.types.Properties
import semmle.python.types.Descriptors
import semmle.python.SelfAttribute
import semmle.python.Metrics
/**
* An extension of `ControlFlowNode` that provides points-to predicates.
@@ -93,6 +106,24 @@ class ControlFlowNodeWithPointsTo extends ControlFlowNode {
// for that variable.
exists(SsaVariable v | v.getAUse() = this | varHasCompletePointsToSet(v))
}
/** Whether it is unlikely that this ControlFlowNode can be reached */
predicate unlikelyReachable() {
not start_bb_likely_reachable(this.getBasicBlock())
or
exists(BasicBlock b |
start_bb_likely_reachable(b) and
not end_bb_likely_reachable(b) and
// If there is an unlikely successor edge earlier in the BB
// than this node, then this node must be unreachable.
exists(ControlFlowNode p, int i, int j |
p.(RaisingNode).unlikelySuccessor(_) and
p = b.getNode(i) and
this = b.getNode(j) and
i < j
)
)
}
}
/**
@@ -121,6 +152,45 @@ private predicate varHasCompletePointsToSet(SsaVariable var) {
)
}
private predicate start_bb_likely_reachable(BasicBlock b) {
exists(Scope s | s.getEntryNode() = b.getNode(_))
or
exists(BasicBlock pred |
pred = b.getAPredecessor() and
end_bb_likely_reachable(pred) and
not pred.getLastNode().(RaisingNode).unlikelySuccessor(b)
)
}
private predicate end_bb_likely_reachable(BasicBlock b) {
start_bb_likely_reachable(b) and
not exists(ControlFlowNode p, ControlFlowNode s |
p.(RaisingNode).unlikelySuccessor(s) and
p = b.getNode(_) and
s = b.getNode(_) and
not p = b.getLastNode()
)
}
/**
* An extension of `BasicBlock` that provides points-to related methods.
*/
class BasicBlockWithPointsTo extends BasicBlock {
/**
* Whether (as inferred by type inference) it is highly unlikely (or impossible) for control to flow from this to succ.
*/
predicate unlikelySuccessor(BasicBlockWithPointsTo succ) {
this.getLastNode().(RaisingNode).unlikelySuccessor(succ.firstNode())
or
not end_bb_likely_reachable(this) and succ = this.getASuccessor()
}
/**
* Whether (as inferred by type inference) this basic block is likely to be reachable.
*/
predicate likelyReachable() { start_bb_likely_reachable(this) }
}
/**
* An extension of `Expr` that provides points-to predicates.
*/
@@ -208,3 +278,155 @@ class ModuleWithPointsTo extends Module {
override string getAQlClass() { none() }
}
/**
* An extension of `Function` that provides points-to related methods.
*/
class FunctionWithPointsTo extends Function {
/** Gets the FunctionObject corresponding to this function */
FunctionObject getFunctionObject() { result.getOrigin() = this.getDefinition() }
override string getAQlClass() { none() }
}
/**
* An extension of `Class` that provides points-to related methods.
*/
class ClassWithPointsTo extends Class {
/** Gets the ClassObject corresponding to this class */
ClassObject getClassObject() { result.getOrigin() = this.getParent() }
override string getAQlClass() { none() }
}
/** Gets the `Object` corresponding to the immutable literal `l`. */
Object getLiteralObject(ImmutableLiteral l) {
l instanceof IntegerLiteral and
(
py_cobjecttypes(result, theIntType()) and py_cobjectnames(result, l.(Num).getN())
or
py_cobjecttypes(result, theLongType()) and py_cobjectnames(result, l.(Num).getN())
)
or
l instanceof FloatLiteral and
py_cobjecttypes(result, theFloatType()) and
py_cobjectnames(result, l.(Num).getN())
or
l instanceof ImaginaryLiteral and
py_cobjecttypes(result, theComplexType()) and
py_cobjectnames(result, l.(Num).getN())
or
l instanceof NegativeIntegerLiteral and
(
(py_cobjecttypes(result, theIntType()) or py_cobjecttypes(result, theLongType())) and
py_cobjectnames(result, "-" + l.(UnaryExpr).getOperand().(IntegerLiteral).getN())
)
or
l instanceof Bytes and
py_cobjecttypes(result, theBytesType()) and
py_cobjectnames(result, l.(Bytes).quotedString())
or
l instanceof Unicode and
py_cobjecttypes(result, theUnicodeType()) and
py_cobjectnames(result, l.(Unicode).quotedString())
or
l instanceof True and
name_consts(l, "True") and
result = theTrueObject()
or
l instanceof False and
name_consts(l, "False") and
result = theFalseObject()
or
l instanceof None and
name_consts(l, "None") and
result = theNoneObject()
}
private predicate gettext_installed() {
// Good enough (and fast) approximation
exists(Module m | m.getName() = "gettext")
}
private predicate builtin_constant(string name) {
exists(Object::builtin(name))
or
name = "WindowsError"
or
name = "_" and gettext_installed()
}
/** Whether this name is (almost) always defined, ie. it is a builtin or VM defined name */
predicate globallyDefinedName(string name) { builtin_constant(name) or auto_name(name) }
private predicate auto_name(string name) {
name = "__file__" or name = "__builtins__" or name = "__name__"
}
/** An extension of `SsaVariable` that provides points-to related methods. */
class SsaVariableWithPointsTo extends SsaVariable {
/** Gets an argument of the phi function defining this variable, pruned of unlikely edges. */
SsaVariable getAPrunedPhiInput() {
result = this.getAPhiInput() and
exists(BasicBlock incoming | incoming = this.getPredecessorBlockForPhiArgument(result) |
not incoming.getLastNode().(RaisingNode).unlikelySuccessor(this.getDefinition())
)
}
/** Gets the incoming edges for a Phi node, pruned of unlikely edges. */
private BasicBlockWithPointsTo getAPrunedPredecessorBlockForPhi() {
result = this.getAPredecessorBlockForPhi() and
not result.unlikelySuccessor(this.getDefinition().getBasicBlock())
}
private predicate implicitlyDefined() {
not exists(this.getDefinition()) and
not py_ssa_phi(this, _) and
exists(GlobalVariable var | this.getVariable() = var |
globallyDefinedName(var.getId())
or
var.getId() = "__path__" and var.getScope().(Module).isPackageInit()
)
}
/** Whether this variable may be undefined */
predicate maybeUndefined() {
not exists(this.getDefinition()) and not py_ssa_phi(this, _) and not this.implicitlyDefined()
or
this.getDefinition().isDelete()
or
exists(SsaVariableWithPointsTo var | var = this.getAPrunedPhiInput() | var.maybeUndefined())
or
/*
* For phi-nodes, there must be a corresponding phi-input for each control-flow
* predecessor. Otherwise, the variable will be undefined on that incoming edge.
* WARNING: the same phi-input may cover multiple predecessors, so this check
* cannot be done by counting.
*/
exists(BasicBlock incoming |
reaches_end(incoming) and
incoming = this.getAPrunedPredecessorBlockForPhi() and
not this.getAPhiInput().getDefinition().getBasicBlock().dominates(incoming)
)
}
override string getAQlClass() { none() }
}
private predicate reaches_end(BasicBlock b) {
not exits_early(b) and
(
/* Entry point */
not exists(BasicBlock prev | prev.getASuccessor() = b)
or
exists(BasicBlock prev | prev.getASuccessor() = b | reaches_end(prev))
)
}
private predicate exits_early(BasicBlock b) {
exists(FunctionObject f |
f.neverReturns() and
f.getACall().getBasicBlock() = b
)
}

View File

@@ -4,7 +4,7 @@
import python
private import LegacyPointsTo
import semmle.python.pointsto.PointsTo
private import semmle.python.types.ImportTime
import IDEContextual
private newtype TDefinition =

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Additional models for remote flow sources for `tornado.websocket.WebSocketHandler` have been added.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Added taint flow model and type model for `urllib.parseurl`.

View File

@@ -0,0 +1,7 @@
---
category: breaking
---
* All modules that depend on the points-to analysis have now been removed from the top level `python.qll` module. To access the points-to functionality, import the new `LegacyPointsTo` module. This also means that some predicates have been removed from various classes, for instance `Function.getFunctionObject()`. To access these predicates, import the `LegacyPointsTo` module and use the `FunctionWithPointsTo` class instead. Most cases follow this pattern, but there are a few exceptions:
* The `getLiteralObject` method on `ImmutableLiteral` subclasses has been replaced with a predicate `getLiteralObject(ImmutableLiteral l)` in the `LegacyPointsTo` module.
* The `getMetrics` method on `Function`, `Class`, and `Module` has been removed. To access metrics, import `LegacyPointsTo` and use the classes `FunctionMetrics`, etc. instead.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Remote flow sources for the `python-socketio` package have been modeled.

View File

@@ -14,27 +14,27 @@ import semmle.python.Patterns
import semmle.python.Keywords
import semmle.python.Comprehensions
import semmle.python.Flow
import semmle.python.Metrics
private import semmle.python.Metrics
import semmle.python.Constants
import semmle.python.Scope
import semmle.python.Comment
import semmle.python.GuardedControlFlow
import semmle.python.types.ImportTime
import semmle.python.types.Object
import semmle.python.types.ClassObject
import semmle.python.types.FunctionObject
import semmle.python.types.ModuleObject
import semmle.python.types.Version
import semmle.python.types.Descriptors
private import semmle.python.types.ImportTime
private import semmle.python.types.Object
private import semmle.python.types.ClassObject
private import semmle.python.types.FunctionObject
private import semmle.python.types.ModuleObject
private import semmle.python.types.Version
private import semmle.python.types.Descriptors
import semmle.python.SSA
import semmle.python.SelfAttribute
import semmle.python.types.Properties
private import semmle.python.SelfAttribute
private import semmle.python.types.Properties
import semmle.python.xml.XML
import semmle.python.essa.Essa
import semmle.python.pointsto.Base
import semmle.python.pointsto.Context
import semmle.python.pointsto.CallGraph
import semmle.python.objects.ObjectAPI
private import semmle.python.pointsto.Base
private import semmle.python.pointsto.Context
private import semmle.python.pointsto.CallGraph
private import semmle.python.objects.ObjectAPI
import semmle.python.Unit
import site
private import semmle.python.Overlay

View File

@@ -141,18 +141,12 @@ class Class extends Class_, Scope, AstNode {
/** Gets the metaclass expression */
Expr getMetaClass() { result = this.getParent().getMetaClass() }
/** Gets the ClassObject corresponding to this class */
ClassObject getClassObject() { result.getOrigin() = this.getParent() }
/** Gets the nth base of this class definition. */
Expr getBase(int index) { result = this.getParent().getBase(index) }
/** Gets a base of this class definition. */
Expr getABase() { result = this.getParent().getABase() }
/** Gets the metrics for this class */
ClassMetrics getMetrics() { result = this }
/**
* Gets the qualified name for this class.
* Should return the same name as the `__qualname__` attribute on classes in Python 3.

View File

@@ -240,17 +240,12 @@ class Bytes extends StringLiteral {
/* syntax: b"hello" */
Bytes() { not this.isUnicode() }
override Object getLiteralObject() {
py_cobjecttypes(result, theBytesType()) and
py_cobjectnames(result, this.quotedString())
}
/**
* The extractor puts quotes into the name of each string (to prevent "0" clashing with 0).
* The following predicate help us match up a string/byte literals in the source
* which the equivalent object.
*/
private string quotedString() {
string quotedString() {
exists(string b_unquoted | b_unquoted = this.getS() | result = "b'" + b_unquoted + "'")
}
}
@@ -266,8 +261,6 @@ class Ellipsis extends Ellipsis_ {
* Consists of string (both unicode and byte) literals and numeric literals.
*/
abstract class ImmutableLiteral extends Expr {
abstract Object getLiteralObject();
abstract boolean booleanValue();
}
@@ -292,12 +285,6 @@ class IntegerLiteral extends Num {
override string toString() { result = "IntegerLiteral" }
override Object getLiteralObject() {
py_cobjecttypes(result, theIntType()) and py_cobjectnames(result, this.getN())
or
py_cobjecttypes(result, theLongType()) and py_cobjectnames(result, this.getN())
}
override boolean booleanValue() {
this.getValue() = 0 and result = false
or
@@ -317,10 +304,6 @@ class FloatLiteral extends Num {
override string toString() { result = "FloatLiteral" }
override Object getLiteralObject() {
py_cobjecttypes(result, theFloatType()) and py_cobjectnames(result, this.getN())
}
override boolean booleanValue() {
this.getValue() = 0.0 and result = false
or
@@ -343,10 +326,6 @@ class ImaginaryLiteral extends Num {
override string toString() { result = "ImaginaryLiteral" }
override Object getLiteralObject() {
py_cobjecttypes(result, theComplexType()) and py_cobjectnames(result, this.getN())
}
override boolean booleanValue() {
this.getValue() = 0.0 and result = false
or
@@ -365,11 +344,6 @@ class NegativeIntegerLiteral extends ImmutableLiteral, UnaryExpr {
override boolean booleanValue() { result = this.getOperand().(IntegerLiteral).booleanValue() }
override Object getLiteralObject() {
(py_cobjecttypes(result, theIntType()) or py_cobjecttypes(result, theLongType())) and
py_cobjectnames(result, "-" + this.getOperand().(IntegerLiteral).getN())
}
/**
* Gets the (integer) value of this constant. Will not return a result if the value does not fit into
* a 32 bit signed value
@@ -385,11 +359,6 @@ class Unicode extends StringLiteral {
/* syntax: "hello" */
Unicode() { this.isUnicode() }
override Object getLiteralObject() {
py_cobjecttypes(result, theUnicodeType()) and
py_cobjectnames(result, this.quotedString())
}
/**
* Gets the quoted representation fo this string.
*
@@ -593,12 +562,11 @@ class StringLiteral extends Str_, ImmutableLiteral {
this.getText() != "" and result = true
}
override Object getLiteralObject() { none() }
override string toString() { result = "StringLiteral" }
}
private predicate name_consts(Name_ n, string id) {
/** Holds if `n` is a named constant (`True`, `False`, or `None`) with name `id`. */
predicate name_consts(Name_ n, string id) {
exists(Variable v | py_variables(v, n) and id = v.getId() |
id = "True" or id = "False" or id = "None"
)
@@ -627,8 +595,6 @@ class True extends BooleanLiteral {
/* syntax: True */
True() { name_consts(this, "True") }
override Object getLiteralObject() { name_consts(this, "True") and result = theTrueObject() }
override boolean booleanValue() { result = true }
}
@@ -637,8 +603,6 @@ class False extends BooleanLiteral {
/* syntax: False */
False() { name_consts(this, "False") }
override Object getLiteralObject() { name_consts(this, "False") and result = theFalseObject() }
override boolean booleanValue() { result = false }
}
@@ -647,8 +611,6 @@ class None extends NameConstant {
/* syntax: None */
None() { name_consts(this, "None") }
override Object getLiteralObject() { name_consts(this, "None") and result = theNoneObject() }
override boolean booleanValue() { result = false }
}

View File

@@ -190,24 +190,6 @@ class ControlFlowNode extends @py_flow_node {
/** Whether this node is a normal (non-exceptional) exit */
predicate isNormalExit() { py_scope_flow(this, _, 0) or py_scope_flow(this, _, 2) }
/** Whether it is unlikely that this ControlFlowNode can be reached */
predicate unlikelyReachable() {
not start_bb_likely_reachable(this.getBasicBlock())
or
exists(BasicBlock b |
start_bb_likely_reachable(b) and
not end_bb_likely_reachable(b) and
// If there is an unlikely successor edge earlier in the BB
// than this node, then this node must be unreachable.
exists(ControlFlowNode p, int i, int j |
p.(RaisingNode).unlikelySuccessor(_) and
p = b.getNode(i) and
this = b.getNode(j) and
i < j
)
)
}
/** Whether this strictly dominates other. */
pragma[inline]
predicate strictlyDominates(ControlFlowNode other) {
@@ -901,6 +883,58 @@ class StarredNode extends ControlFlowNode {
ControlFlowNode getValue() { toAst(result) = toAst(this).(Starred).getValue() }
}
/** The ControlFlowNode for an 'except' statement. */
class ExceptFlowNode extends ControlFlowNode {
ExceptFlowNode() { this.getNode() instanceof ExceptStmt }
/**
* Gets the type handled by this exception handler.
* `ExceptionType` in `except ExceptionType as e:`
*/
ControlFlowNode getType() {
exists(ExceptStmt ex |
this.getBasicBlock().dominates(result.getBasicBlock()) and
ex = this.getNode() and
result = ex.getType().getAFlowNode()
)
}
/**
* Gets the name assigned to the handled exception, if any.
* `e` in `except ExceptionType as e:`
*/
ControlFlowNode getName() {
exists(ExceptStmt ex |
this.getBasicBlock().dominates(result.getBasicBlock()) and
ex = this.getNode() and
result = ex.getName().getAFlowNode()
)
}
}
/** The ControlFlowNode for an 'except*' statement. */
class ExceptGroupFlowNode extends ControlFlowNode {
ExceptGroupFlowNode() { this.getNode() instanceof ExceptGroupStmt }
/**
* Gets the type handled by this exception handler.
* `ExceptionType` in `except* ExceptionType as e:`
*/
ControlFlowNode getType() {
this.getBasicBlock().dominates(result.getBasicBlock()) and
result = this.getNode().(ExceptGroupStmt).getType().getAFlowNode()
}
/**
* Gets the name assigned to the handled exception, if any.
* `e` in `except* ExceptionType as e:`
*/
ControlFlowNode getName() {
this.getBasicBlock().dominates(result.getBasicBlock()) and
result = this.getNode().(ExceptGroupStmt).getName().getAFlowNode()
}
}
private module Scopes {
private predicate fast_local(NameNode n) {
exists(FastLocalVariable v |
@@ -1004,7 +1038,8 @@ class BasicBlock extends @py_flow_node {
)
}
private ControlFlowNode firstNode() { result = this }
/** Gets the first node in this basic block */
ControlFlowNode firstNode() { result = this }
/** Gets the last node in this basic block */
ControlFlowNode getLastNode() {
@@ -1093,15 +1128,6 @@ class BasicBlock extends @py_flow_node {
)
}
/**
* Whether (as inferred by type inference) it is highly unlikely (or impossible) for control to flow from this to succ.
*/
predicate unlikelySuccessor(BasicBlock succ) {
this.getLastNode().(RaisingNode).unlikelySuccessor(succ.firstNode())
or
not end_bb_likely_reachable(this) and succ = this.getASuccessor()
}
/** Holds if this basic block strictly reaches the other. Is the start of other reachable from the end of this. */
cached
predicate strictlyReaches(BasicBlock other) {
@@ -1112,11 +1138,6 @@ class BasicBlock extends @py_flow_node {
/** Holds if this basic block reaches the other. Is the start of other reachable from the end of this. */
predicate reaches(BasicBlock other) { this = other or this.strictlyReaches(other) }
/**
* Whether (as inferred by type inference) this basic block is likely to be reachable.
*/
predicate likelyReachable() { start_bb_likely_reachable(this) }
/**
* Gets the `ConditionBlock`, if any, that controls this block and
* does not control any other `ConditionBlock`s that control this block.
@@ -1144,26 +1165,6 @@ class BasicBlock extends @py_flow_node {
}
}
private predicate start_bb_likely_reachable(BasicBlock b) {
exists(Scope s | s.getEntryNode() = b.getNode(_))
or
exists(BasicBlock pred |
pred = b.getAPredecessor() and
end_bb_likely_reachable(pred) and
not pred.getLastNode().(RaisingNode).unlikelySuccessor(b)
)
}
private predicate end_bb_likely_reachable(BasicBlock b) {
start_bb_likely_reachable(b) and
not exists(ControlFlowNode p, ControlFlowNode s |
p.(RaisingNode).unlikelySuccessor(s) and
p = b.getNode(_) and
s = b.getNode(_) and
not p = b.getLastNode()
)
}
private class ControlFlowNodeAlias = ControlFlowNode;
final private class FinalBasicBlock = BasicBlock;

View File

@@ -78,6 +78,7 @@ private import semmle.python.frameworks.Sanic
private import semmle.python.frameworks.ServerLess
private import semmle.python.frameworks.Setuptools
private import semmle.python.frameworks.Simplejson
private import semmle.python.frameworks.Socketio
private import semmle.python.frameworks.SqlAlchemy
private import semmle.python.frameworks.Starlette
private import semmle.python.frameworks.Stdlib

View File

@@ -84,12 +84,6 @@ class Function extends Function_, Scope, AstNode {
/** Gets the name used to define this function */
override string getName() { result = Function_.super.getName() }
/** Gets the metrics for this function */
FunctionMetrics getMetrics() { result = this }
/** Gets the FunctionObject corresponding to this function */
FunctionObject getFunctionObject() { result.getOrigin() = this.getDefinition() }
/**
* Whether this function is a procedure, that is, it has no explicit return statement and always returns None.
* Note that generator and async functions are not procedures as they return generators and coroutines respectively.

View File

@@ -29,9 +29,9 @@ class FunctionMetrics extends Function {
*/
int getCyclomaticComplexity() {
exists(int e, int n |
n = count(BasicBlock b | b = this.getABasicBlock() and b.likelyReachable()) and
n = count(BasicBlockWithPointsTo b | b = this.getABasicBlock() and b.likelyReachable()) and
e =
count(BasicBlock b1, BasicBlock b2 |
count(BasicBlockWithPointsTo b1, BasicBlockWithPointsTo b2 |
b1 = this.getABasicBlock() and
b1.likelyReachable() and
b2 = this.getABasicBlock() and

View File

@@ -86,9 +86,6 @@ class Module extends Module_, Scope, AstNode {
result = this.getName().regexpReplaceAll("\\.[^.]*$", "")
}
/** Gets the metrics for this module */
ModuleMetrics getMetrics() { result = this }
string getAnImportedModuleName() {
exists(Import i | i.getEnclosingModule() = this | result = i.getAnImportedModuleName())
or

View File

@@ -61,14 +61,6 @@ class SsaVariable extends @py_ssa_var {
)
}
/** Gets an argument of the phi function defining this variable, pruned of unlikely edges. */
SsaVariable getAPrunedPhiInput() {
result = this.getAPhiInput() and
exists(BasicBlock incoming | incoming = this.getPredecessorBlockForPhiArgument(result) |
not incoming.getLastNode().(RaisingNode).unlikelySuccessor(this.getDefinition())
)
}
/** Gets a variable that ultimately defines this variable and is not itself defined by another variable */
SsaVariable getAnUltimateDefinition() {
result = this and not exists(this.getAPhiInput())
@@ -85,17 +77,11 @@ class SsaVariable extends @py_ssa_var {
string getId() { result = this.getVariable().getId() }
/** Gets the incoming edges for a Phi node. */
private BasicBlock getAPredecessorBlockForPhi() {
BasicBlock getAPredecessorBlockForPhi() {
exists(this.getAPhiInput()) and
result.getASuccessor() = this.getDefinition().getBasicBlock()
}
/** Gets the incoming edges for a Phi node, pruned of unlikely edges. */
private BasicBlock getAPrunedPredecessorBlockForPhi() {
result = this.getAPredecessorBlockForPhi() and
not result.unlikelySuccessor(this.getDefinition().getBasicBlock())
}
/** Whether it is possible to reach a use of this variable without passing a definition */
predicate reachableWithoutDefinition() {
not exists(this.getDefinition()) and not py_ssa_phi(this, _)
@@ -115,38 +101,6 @@ class SsaVariable extends @py_ssa_var {
)
}
/** Whether this variable may be undefined */
predicate maybeUndefined() {
not exists(this.getDefinition()) and not py_ssa_phi(this, _) and not this.implicitlyDefined()
or
this.getDefinition().isDelete()
or
exists(SsaVariable var | var = this.getAPrunedPhiInput() | var.maybeUndefined())
or
/*
* For phi-nodes, there must be a corresponding phi-input for each control-flow
* predecessor. Otherwise, the variable will be undefined on that incoming edge.
* WARNING: the same phi-input may cover multiple predecessors, so this check
* cannot be done by counting.
*/
exists(BasicBlock incoming |
reaches_end(incoming) and
incoming = this.getAPrunedPredecessorBlockForPhi() and
not this.getAPhiInput().getDefinition().getBasicBlock().dominates(incoming)
)
}
private predicate implicitlyDefined() {
not exists(this.getDefinition()) and
not py_ssa_phi(this, _) and
exists(GlobalVariable var | this.getVariable() = var |
globallyDefinedName(var.getId())
or
var.getId() = "__path__" and var.getScope().(Module).isPackageInit()
)
}
/**
* Gets the global variable that is accessed if this local is undefined.
* Only applies to local variables in class scopes.
@@ -173,43 +127,6 @@ class SsaVariable extends @py_ssa_var {
}
}
private predicate reaches_end(BasicBlock b) {
not exits_early(b) and
(
/* Entry point */
not exists(BasicBlock prev | prev.getASuccessor() = b)
or
exists(BasicBlock prev | prev.getASuccessor() = b | reaches_end(prev))
)
}
private predicate exits_early(BasicBlock b) {
exists(FunctionObject f |
f.neverReturns() and
f.getACall().getBasicBlock() = b
)
}
private predicate gettext_installed() {
// Good enough (and fast) approximation
exists(Module m | m.getName() = "gettext")
}
private predicate builtin_constant(string name) {
exists(Object::builtin(name))
or
name = "WindowsError"
or
name = "_" and gettext_installed()
}
private predicate auto_name(string name) {
name = "__file__" or name = "__builtins__" or name = "__name__"
}
/** Whether this name is (almost) always defined, ie. it is a builtin or VM defined name */
predicate globallyDefinedName(string name) { builtin_constant(name) or auto_name(name) }
/** An SSA variable that is backed by a global variable */
class GlobalSsaVariable extends EssaVariable {
GlobalSsaVariable() { this.getSourceVariable() instanceof GlobalVariable }

View File

@@ -5,6 +5,7 @@
import python
private import semmle.python.pointsto.Filters
private import LegacyPointsTo
/**
* An attribute access where the left hand side of the attribute expression

View File

@@ -1,9 +1,10 @@
/** This module provides an API for attribute reads and writes. */
private import python
import DataFlowUtil
import DataFlowPublic
private import DataFlowPrivate
private import semmle.python.types.Builtins
private import semmle.python.dataflow.new.internal.Builtins
/**
* A data flow node that reads or writes an attribute of an object.
@@ -134,8 +135,12 @@ private class BuiltInCallNode extends CallNode {
BuiltInCallNode() {
// TODO disallow instances where the name of the built-in may refer to an in-scope variable of that name.
exists(NameNode id | this.getFunction() = id and id.getId() = name and id.isGlobal()) and
name = any(Builtin b).getName()
exists(NameNode id |
name = Builtins::getBuiltinName() and
this.getFunction() = id and
id.getId() = name and
id.isGlobal()
)
}
/** Gets the name of the built-in function that is called at this `CallNode` */

View File

@@ -9,6 +9,7 @@ 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
/**
* Python modules and the way imports are resolved are... complicated. Here's a crash course in how

View File

@@ -1,6 +1,6 @@
import python
import semmle.python.dataflow.TaintTracking
private import semmle.python.objects.ObjectInternal
private import LegacyPointsTo
private import semmle.python.dataflow.Implementation
module TaintTracking {

View File

@@ -1,4 +1,5 @@
import python
private import LegacyPointsTo
import semmle.python.dataflow.TaintTracking
class OpenFile extends TaintKind {

View File

@@ -1,7 +1,6 @@
import python
private import LegacyPointsTo
import semmle.python.dataflow.TaintTracking
private import semmle.python.objects.ObjectInternal
private import semmle.python.pointsto.Filters as Filters
import semmle.python.dataflow.Legacy
@@ -257,7 +256,7 @@ class TaintTrackingImplementation extends string instanceof TaintTracking::Confi
TaintKind kind, string edgeLabel
) {
this.unprunedStep(src, node, context, path, kind, edgeLabel) and
node.getBasicBlock().likelyReachable() and
node.getBasicBlock().(BasicBlockWithPointsTo).likelyReachable() and
not super.isBarrier(node) and
(
not path = TNoAttribute()
@@ -685,7 +684,9 @@ private class EssaTaintTracking extends string instanceof TaintTracking::Configu
TaintTrackingNode src, PhiFunction defn, TaintTrackingContext context, AttributePath path,
TaintKind kind
) {
exists(DataFlow::Node srcnode, BasicBlock pred, EssaVariable predvar, DataFlow::Node phi |
exists(
DataFlow::Node srcnode, BasicBlockWithPointsTo pred, EssaVariable predvar, DataFlow::Node phi
|
src = TTaintTrackingNode_(srcnode, context, path, kind, this) and
defn = phi.asVariable().getDefinition() and
predvar = defn.getInput(pred) and

View File

@@ -9,9 +9,7 @@
*/
import python
private import semmle.python.pointsto.PointsTo
private import semmle.python.pointsto.PointsToContext
private import semmle.python.objects.ObjectInternal
private import LegacyPointsTo
/** A state that should be tracked. */
abstract class TrackableState extends string {

View File

@@ -89,7 +89,6 @@
import python
private import LegacyPointsTo
private import semmle.python.pointsto.Filters as Filters
private import semmle.python.objects.ObjectInternal
private import semmle.python.dataflow.Implementation
import semmle.python.dataflow.Configuration

View File

@@ -1,4 +1,5 @@
import semmle.python.dependencies.Dependencies
private import LegacyPointsTo
/**
* A library describing an abstract mechanism for representing dependency categories.

View File

@@ -1,6 +1,7 @@
import python
import semmle.python.dependencies.Dependencies
import semmle.python.dependencies.DependencyKind
private import LegacyPointsTo
/**
* Combine the source-file and package into a single string:

View File

@@ -1,5 +1,4 @@
import python
/*
* Classification of variables. These should be non-overlapping and complete.
*
@@ -12,6 +11,9 @@ import python
* Escaping globals -- Global variables that have definitions and at least one of those definitions is in another scope.
*/
private import semmle.python.types.ImportTime
private import semmle.python.essa.SsaDefinitions
/** A source language variable, to be converted into a set of SSA variables. */
abstract class SsaSourceVariable extends @py_variable {
SsaSourceVariable() {
@@ -274,6 +276,17 @@ class ModuleVariable extends SsaSourceVariable instanceof GlobalVariable {
override CallNode redefinedAtCallSite() { none() }
}
/** Holds if `f` is an import of the form `from .[...] import ...` and the enclosing scope is an __init__ module */
private predicate import_from_dot_in_init(ImportExprNode f) {
f.getScope() = any(Module m).getInitModule() and
(
f.getNode().getLevel() = 1 and
not exists(f.getNode().getName())
or
f.getNode().getImportedModuleName() = f.getEnclosingModule().getPackage().getName()
)
}
class NonEscapingGlobalVariable extends ModuleVariable {
NonEscapingGlobalVariable() {
this instanceof GlobalVariable and

View File

@@ -6,6 +6,7 @@ import python
private import SsaCompute
import semmle.python.essa.Definitions
private import semmle.python.internal.CachedStages
private import semmle.python.essa.SsaDefinitions
/** An (enhanced) SSA variable derived from `SsaSourceVariable`. */
class EssaVariable extends TEssaDefinition {

View File

@@ -6,6 +6,13 @@
import python
private import semmle.python.internal.CachedStages
/** Hold if `expr` is a test (a branch) and `use` is within that test */
predicate test_contains(ControlFlowNode expr, ControlFlowNode use) {
expr.getNode() instanceof Expr and
expr.isBranch() and
expr.getAChild*() = use
}
cached
module SsaSource {
/** Holds if `v` is used as the receiver in a method call. */

View File

@@ -0,0 +1,119 @@
/**
* Provides definitions and modeling for the `python-socketio` PyPI package.
* See https://python-socketio.readthedocs.io/en/stable/.
*/
private import python
private import semmle.python.dataflow.new.TaintTracking
private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.Concepts
private import semmle.python.ApiGraphs
private import semmle.python.frameworks.internal.PoorMansFunctionResolution
/**
* Provides models for the `python-socketio` PyPI package.
* See https://python-socketio.readthedocs.io/en/stable/.
*/
module SocketIO {
/** Provides models for socketio `Server` and `AsyncServer` classes. */
module Server {
/** Gets an instance of a socketio `Server` or `AsyncServer`. */
API::Node server() {
result = API::moduleImport("socketio").getMember(["Server", "AsyncServer"]).getAnInstance()
}
/** Gets a decorator that indicates a socketio event handler. */
private API::Node serverEventAnnotation() {
result = server().getMember("event")
or
result = server().getMember("on").getReturn()
}
private class EventHandler extends Http::Server::RequestHandler::Range {
EventHandler() {
serverEventAnnotation().getAValueReachableFromSource().asExpr() = this.getADecorator()
or
exists(DataFlow::CallCfgNode c, DataFlow::Node arg |
c = server().getMember("on").getACall()
|
(
arg = c.getArg(1)
or
arg = c.getArgByName("handler")
) and
poorMansFunctionTracker(this) = arg
)
}
override Parameter getARoutedParameter() {
result = this.getAnArg() and
not result = this.getArg(0) // First parameter is `sid`, which is not a remote flow source as it cannot be controlled by the client.
}
override string getFramework() { result = "socketio" }
}
private class CallbackArgument extends DataFlow::Node {
CallbackArgument() {
exists(DataFlow::CallCfgNode c |
c = [server(), Namespace::instance()].getMember(["emit", "send"]).getACall()
|
this = c.getArgByName("callback")
)
}
}
private class CallbackHandler extends Http::Server::RequestHandler::Range {
CallbackHandler() { any(CallbackArgument ca) = poorMansFunctionTracker(this) }
override Parameter getARoutedParameter() { result = this.getAnArg() }
override string getFramework() { result = "socketio" }
}
private class SocketIOCall extends RemoteFlowSource::Range {
SocketIOCall() { this = [server(), Namespace::instance()].getMember("call").getACall() }
override string getSourceType() { result = "socketio call" }
}
}
/** Provides modeling for socketio server Namespace/AsyncNamespace classes. */
module Namespace {
/** Gets a reference to the `socketio.Namespace` or `socketio.AsyncNamespace` classes or any subclass. */
API::Node subclassRef() {
result =
API::moduleImport("socketio").getMember(["Namespace", "AsyncNamespace"]).getASubclass*()
}
/** Gets a reference to an instance of a subclass of `socketio.Namespace` or `socketio.AsyncNamespace`. */
API::Node instance() {
result = subclassRef().getAnInstance()
or
result = subclassRef().getAMember().getSelfParameter()
}
/** A socketio Namespace class. */
class NamespaceClass extends Class {
NamespaceClass() { this.getABase() = subclassRef().asSource().asExpr() }
/** Gets a handler for socketio events. */
Function getAnEventHandler() {
result = this.getAMethod() and
result.getName().matches("on_%")
}
}
private class NamespaceEventHandler extends Http::Server::RequestHandler::Range {
NamespaceEventHandler() { this = any(NamespaceClass nc).getAnEventHandler() }
override Parameter getARoutedParameter() {
result = this.getAnArg() and
not result = this.getArg(0) and
not result = this.getArg(1) // First 2 parameters are `self` and `sid`.
}
override string getFramework() { result = "socketio" }
}
}
}

View File

@@ -142,6 +142,8 @@ extensions:
- ["typing", "Member[cast]", "Argument[1,val:]", "ReturnValue", "value"]
# See https://docs.python.org/3/library/urllib.parse.html#urllib.parse.parse_qs
- ["urllib", "Member[parse].Member[parse_qs]", "Argument[0,qs:]", "ReturnValue", "taint"]
# See https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlparse
- ["urllib", "Member[parse].Member[urlparse]", "Argument[0,urlstring:]", "ReturnValue", "taint"]
# See https://docs.python.org/3/library/urllib.parse.html#urllib.parse.quote
- ["urllib", "Member[parse].Member[quote]", "Argument[0,string:]", "ReturnValue", "taint"]
# See https://docs.python.org/3/library/urllib.parse.html#urllib.parse.quote_plus
@@ -181,7 +183,9 @@ extensions:
- addsTo:
pack: codeql/python-all
extensible: typeModel
data: []
data:
# See https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlparse
- ["urllib.parse.ParseResult~Subclass", 'urllib', 'Member[parse].Member[urlparse]']
- addsTo:
pack: codeql/python-all

View File

@@ -245,6 +245,67 @@ module Stdlib {
}
}
/**
* Provides models for the `urllib.parse.ParseResult` class
*
* See https://docs.python.org/3.9/library/urllib.parse.html#urllib.parse.ParseResult.
*/
module ParseResult {
/** Gets a reference to the `urllib.parse.ParseResult` class. */
API::Node classRef() {
result = API::moduleImport("urllib").getMember("parse").getMember("ParseResult")
or
result = ModelOutput::getATypeNode("urllib.parse.ParseResult~Subclass").getASubclass*()
}
/**
* A source of instances of `urllib.parse.ParseResult`, extend this class to model new instances.
*
* This can include instantiations of the class, return values from function
* calls, or a special parameter that will be set when functions are called by an external
* library.
*
* Use the predicate `ParseResult::instance()` to get references to instances of `urllib.parse.ParseResult`.
*/
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
/** A direct instantiation of `urllib.parse.ParseResult`. */
private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
ClassInstantiation() { this = classRef().getACall() }
}
/** Gets a reference to an instance of `urllib.parse.ParseResult`. */
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
t.start() and
result instanceof InstanceSource
or
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
}
/** Gets a reference to an instance of `urllib.parse.ParseResult`. */
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
/**
* Taint propagation for `urllib.parse.ParseResult`.
*/
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
InstanceTaintSteps() { this = "urllib.parse.ParseResult" }
override DataFlow::Node getInstance() { result = instance() }
override string getAttributeName() {
result in [
"netloc", "path", "params", "query", "fragment", "username", "password", "hostname",
"port"
]
}
override string getMethodName() { none() }
override string getAsyncMethodName() { none() }
}
}
// ---------------------------------------------------------------------------
// logging
// ---------------------------------------------------------------------------

View File

@@ -135,6 +135,8 @@ module Tornado {
API::Node subclassRef() {
result = web().getMember("RequestHandler").getASubclass*()
or
result = WebSocket::WebSocketHandler::subclassRef()
or
result = ModelOutput::getATypeNode("tornado.web.RequestHandler~Subclass").getASubclass*()
}
@@ -428,6 +430,49 @@ module Tornado {
}
}
}
// ---------------------------------------------------------------------------
// tornado.websocket
// ---------------------------------------------------------------------------
/** Gets a reference to the `tornado.websocket` module. */
API::Node websocket() { result = Tornado::tornado().getMember("websocket") }
/** Provides models for the `tornado.websocket` module */
module WebSocket {
/**
* Provides models for the `tornado.websocket.WebSocketHandler` class and subclasses.
*
* See https://www.tornadoweb.org/en/stable/websocket.html#tornado.websocket.WebSocketHandler.
*/
module WebSocketHandler {
/** Gets a reference to the `tornado.websocket.WebSocketHandler` class or any subclass. */
API::Node subclassRef() {
result = websocket().getMember("WebSocketHandler").getASubclass*()
or
result =
ModelOutput::getATypeNode("tornado.websocket.WebSocketHandler~Subclass").getASubclass*()
}
/** A subclass of `tornado.websocket.WebSocketHandler`. */
class WebSocketHandlerClass extends Web::RequestHandler::RequestHandlerClass {
WebSocketHandlerClass() { this.getParent() = subclassRef().asSource().asExpr() }
override Function getARequestHandler() {
result = super.getARequestHandler()
or
result = this.getAMethod() and
result.getName() = "open"
}
/** Gets a function that could handle incoming WebSocket events, if any. */
Function getAWebSocketEventHandler() {
result = this.getAMethod() and
result.getName() =
["on_message", "on_close", "on_ping", "on_pong", "select_subprotocol", "check_origin"]
}
}
}
}
}
// ---------------------------------------------------------------------------
@@ -542,6 +587,27 @@ module Tornado {
override string getFramework() { result = "Tornado" }
}
/** A request handler for WebSocket events. */
private class TornadoWebSocketEventHandler extends Http::Server::RequestHandler::Range {
TornadoWebSocketEventHandler() {
exists(TornadoModule::WebSocket::WebSocketHandler::WebSocketHandlerClass cls |
cls.getAWebSocketEventHandler() = this
)
}
override Parameter getARoutedParameter() {
// The `open` method is handled as a normal request handler in `TornadoRouteSetup` or `TornadoRequestHandlerWithoutKnownRoute`.
// For other event handlers (such as `on_message`), all parameters should be remote flow sources, as they are not affected by routing.
result in [
this.getArg(_), this.getArgByName(_), this.getVararg().(Parameter),
this.getKwarg().(Parameter)
] and
not result = this.getArg(0)
}
override string getFramework() { result = "Tornado" }
}
// ---------------------------------------------------------------------------
// Response modeling
// ---------------------------------------------------------------------------

View File

@@ -1,7 +1,7 @@
/** Utilities for handling the zope libraries */
import python
private import semmle.python.pointsto.PointsTo
private import LegacyPointsTo
/** A method that belongs to a sub-class of `zope.interface.Interface` */
class ZopeInterfaceMethodValue extends PythonFunctionValue {

View File

@@ -5,6 +5,7 @@ private import semmle.python.pointsto.PointsTo
private import semmle.python.pointsto.MRO
private import semmle.python.pointsto.PointsToContext
private import semmle.python.types.Builtins
private import semmle.python.objects.ObjectAPI
/**
* A constant.

View File

@@ -5,6 +5,7 @@ private import semmle.python.pointsto.PointsTo
private import semmle.python.pointsto.PointsToContext
private import semmle.python.pointsto.MRO
private import semmle.python.types.Builtins
private import semmle.python.pointsto.Context
/** A property object. */
class PropertyInternal extends ObjectInternal, TProperty {

View File

@@ -5,6 +5,8 @@ private import semmle.python.pointsto.PointsTo
private import semmle.python.pointsto.MRO
private import semmle.python.pointsto.PointsToContext
private import semmle.python.types.Builtins
private import semmle.python.pointsto.Context
private import semmle.python.pointsto.Base
/** A class representing instances */
abstract class InstanceObject extends ObjectInternal {

View File

@@ -5,6 +5,7 @@ private import semmle.python.pointsto.PointsTo
private import semmle.python.pointsto.MRO
private import semmle.python.pointsto.PointsToContext
private import semmle.python.types.Builtins
private import semmle.python.types.ImportTime
/** A class representing modules */
abstract class ModuleObjectInternal extends ObjectInternal {

View File

@@ -6,9 +6,6 @@
import python
private import LegacyPointsTo
private import TObject
private import semmle.python.objects.ObjectInternal
private import semmle.python.pointsto.PointsTo
private import semmle.python.pointsto.PointsToContext
private import semmle.python.pointsto.MRO
private import semmle.python.types.Builtins

View File

@@ -6,6 +6,7 @@ private import semmle.python.objects.ObjectInternal
private import semmle.python.pointsto.PointsTo
private import semmle.python.pointsto.PointsToContext
private import semmle.python.internal.CachedStages
private import semmle.python.pointsto.Context
/**
* Internal type backing `ObjectInternal` and `Value`

View File

@@ -12,6 +12,8 @@ import python
import semmle.python.essa.SsaDefinitions
private import semmle.python.types.Builtins
private import semmle.python.internal.CachedStages
private import semmle.python.types.Object
private import semmle.python.types.ClassObject
/*
* The following predicates exist in order to provide
@@ -42,24 +44,6 @@ private predicate class_defines_name(Class cls, string name) {
exists(SsaVariable var | name = var.getId() and var.getAUse() = cls.getANormalExit())
}
/** Hold if `expr` is a test (a branch) and `use` is within that test */
predicate test_contains(ControlFlowNode expr, ControlFlowNode use) {
expr.getNode() instanceof Expr and
expr.isBranch() and
expr.getAChild*() = use
}
/** Holds if `f` is an import of the form `from .[...] import ...` and the enclosing scope is an __init__ module */
predicate import_from_dot_in_init(ImportExprNode f) {
f.getScope() = any(Module m).getInitModule() and
(
f.getNode().getLevel() = 1 and
not exists(f.getNode().getName())
or
f.getNode().getImportedModuleName() = f.getEnclosingModule().getPackage().getName()
)
}
/** Gets the pseudo-object representing the value referred to by an undefined variable */
Object undefinedVariable() { py_special_objects(result, "_semmle_undefined_value") }

View File

@@ -11,6 +11,8 @@
import python
private import semmle.python.pointsto.PointsToContext
private import semmle.python.types.FunctionObject
private import semmle.python.pointsto.Context
private newtype TTInvocation =
TInvocation(FunctionObject f, Context c) {

View File

@@ -6,7 +6,13 @@ private import semmle.python.pointsto.PointsToContext
private import semmle.python.pointsto.MRO
private import semmle.python.types.Builtins
private import semmle.python.types.Extensions
private import semmle.python.pointsto.Context
private import semmle.python.internal.CachedStages
private import semmle.python.types.Object
private import semmle.python.types.FunctionObject
private import semmle.python.types.ClassObject
private import semmle.python.pointsto.Base
private import semmle.python.types.ImportTime
/* Use this version for speed */
class CfgOrigin extends @py_object {

View File

@@ -1,6 +1,8 @@
import python
private import semmle.python.pointsto.PointsTo
private import semmle.python.objects.ObjectInternal
private import semmle.python.types.ImportTime
private import semmle.python.types.Version
/*
* A note on 'cost'. Cost doesn't represent the cost to compute,

View File

@@ -50,7 +50,7 @@ module FullServerSideRequestForgeryFlow = TaintTracking::Global<FullServerSideRe
*/
predicate fullyControlledRequest(Http::Client::Request request) {
forall(DataFlow::Node urlPart | urlPart = request.getAUrlPart() |
FullServerSideRequestForgeryFlow::flow(_, urlPart)
FullServerSideRequestForgeryFlow::flowTo(urlPart)
)
}

View File

@@ -1,4 +1,5 @@
import python
private import LegacyPointsTo
class Builtin extends @py_cobject {
Builtin() {

View File

@@ -6,6 +6,7 @@ private import semmle.python.pointsto.PointsTo
private import semmle.python.pointsto.MRO
private import semmle.python.types.Builtins
private import semmle.python.objects.ObjectInternal
private import semmle.python.types.ImportTime
/**
* A class whose instances represents Python classes.
@@ -88,7 +89,7 @@ class ClassObject extends Object {
}
/** Gets the scope associated with this class, if it is not a builtin class */
Class getPyClass() { result.getClassObject() = this }
ClassWithPointsTo getPyClass() { result.getClassObject() = this }
/** Returns an attribute declared on this class (not on a super-class) */
Object declaredAttribute(string name) {

View File

@@ -1,5 +1,8 @@
import python
private import semmle.python.objects.ObjectInternal
private import semmle.python.types.Object
private import semmle.python.types.FunctionObject
private import semmle.python.pointsto.Context
/** A class method object. Either a decorated function or an explicit call to classmethod(f) */
class ClassMethodObject extends Object {

View File

@@ -80,7 +80,7 @@ class RaisingNode extends ControlFlowNode {
or
this.getNode() instanceof Print and result = theIOErrorType()
or
exists(ExceptFlowNode except |
exists(ExceptFlowNodeWithPointsTo except |
except = this.getAnExceptionalSuccessor() and
except.handles_objectapi(result) and
result = this.innateException_objectapi()
@@ -107,7 +107,7 @@ class RaisingNode extends ControlFlowNode {
or
this.getNode() instanceof Print and result = ClassValue::ioError()
or
exists(ExceptFlowNode except |
exists(ExceptFlowNodeWithPointsTo except |
except = this.getAnExceptionalSuccessor() and
except.handles(result) and
result = this.innateException()
@@ -200,8 +200,8 @@ class RaisingNode extends ControlFlowNode {
succ = this.getAnExceptionalSuccessor() and
(
/* An 'except' that handles raised and there is no more previous handler */
succ.(ExceptFlowNode).handles_objectapi(raised) and
not exists(ExceptFlowNode other, StmtList s, int i, int j |
succ.(ExceptFlowNodeWithPointsTo).handles_objectapi(raised) and
not exists(ExceptFlowNodeWithPointsTo other, StmtList s, int i, int j |
not other = succ and
other.handles_objectapi(raised) and
s.getItem(i) = succ.getNode() and
@@ -211,7 +211,7 @@ class RaisingNode extends ControlFlowNode {
)
or
/* Any successor that is not an 'except', provided that 'raised' is not handled by a different successor. */
not this.getAnExceptionalSuccessor().(ExceptFlowNode).handles_objectapi(raised) and
not this.getAnExceptionalSuccessor().(ExceptFlowNodeWithPointsTo).handles_objectapi(raised) and
not succ instanceof ExceptFlowNode
)
}
@@ -223,8 +223,8 @@ class RaisingNode extends ControlFlowNode {
succ = this.getAnExceptionalSuccessor() and
(
/* An 'except' that handles raised and there is no more previous handler */
succ.(ExceptFlowNode).handles(raised) and
not exists(ExceptFlowNode other, StmtList s, int i, int j |
succ.(ExceptFlowNodeWithPointsTo).handles(raised) and
not exists(ExceptFlowNodeWithPointsTo other, StmtList s, int i, int j |
not other = succ and
other.handles(raised) and
s.getItem(i) = succ.getNode() and
@@ -234,7 +234,7 @@ class RaisingNode extends ControlFlowNode {
)
or
/* Any successor that is not an 'except', provided that 'raised' is not handled by a different successor. */
not this.getAnExceptionalSuccessor().(ExceptFlowNode).handles(raised) and
not this.getAnExceptionalSuccessor().(ExceptFlowNodeWithPointsTo).handles(raised) and
not succ instanceof ExceptFlowNode
)
}
@@ -248,7 +248,7 @@ class RaisingNode extends ControlFlowNode {
raised.isLegalExceptionType() and
raised = this.getARaisedType_objectapi() and
this.isExceptionalExit(s) and
not this.getAnExceptionalSuccessor().(ExceptFlowNode).handles_objectapi(raised)
not this.getAnExceptionalSuccessor().(ExceptFlowNodeWithPointsTo).handles_objectapi(raised)
}
/**
@@ -260,7 +260,7 @@ class RaisingNode extends ControlFlowNode {
raised.isLegalExceptionType() and
raised = this.getARaisedType() and
this.isExceptionalExit(s) and
not this.getAnExceptionalSuccessor().(ExceptFlowNode).handles(raised)
not this.getAnExceptionalSuccessor().(ExceptFlowNodeWithPointsTo).handles(raised)
}
}
@@ -368,36 +368,10 @@ predicate scope_raises_unknown(Scope s) {
)
}
/** The ControlFlowNode for an 'except' statement. */
class ExceptFlowNode extends ControlFlowNode {
ExceptFlowNode() { this.getNode() instanceof ExceptStmt }
/**
* Gets the type handled by this exception handler.
* `ExceptionType` in `except ExceptionType as e:`
*/
ControlFlowNodeWithPointsTo getType() {
exists(ExceptStmt ex |
this.getBasicBlock().dominates(result.getBasicBlock()) and
ex = this.getNode() and
result = ex.getType().getAFlowNode()
)
}
/**
* Gets the name assigned to the handled exception, if any.
* `e` in `except ExceptionType as e:`
*/
ControlFlowNode getName() {
exists(ExceptStmt ex |
this.getBasicBlock().dominates(result.getBasicBlock()) and
ex = this.getNode() and
result = ex.getName().getAFlowNode()
)
}
/** An extension of `ExceptFlowNode` that provides points-to related methods. */
class ExceptFlowNodeWithPointsTo extends ExceptFlowNode {
private predicate handledObject_objectapi(Object obj, ClassObject cls, ControlFlowNode origin) {
this.getType().refersTo(obj, cls, origin)
this.getType().(ControlFlowNodeWithPointsTo).refersTo(obj, cls, origin)
or
exists(Object tup | this.handledObject_objectapi(tup, theTupleType(), _) |
element_from_tuple_objectapi(tup).refersTo(obj, cls, origin)
@@ -407,7 +381,7 @@ class ExceptFlowNode extends ControlFlowNode {
private predicate handledObject(Value val, ClassValue cls, ControlFlowNode origin) {
val.getClass() = cls and
(
this.getType().pointsTo(val, origin)
this.getType().(ControlFlowNodeWithPointsTo).pointsTo(val, origin)
or
exists(TupleValue tup | this.handledObject(tup, ClassValue::tuple(), _) |
val = tup.getItem(_) and origin = val.getOrigin()
@@ -452,29 +426,6 @@ class ExceptFlowNode extends ControlFlowNode {
}
}
/** The ControlFlowNode for an 'except*' statement. */
class ExceptGroupFlowNode extends ControlFlowNode {
ExceptGroupFlowNode() { this.getNode() instanceof ExceptGroupStmt }
/**
* Gets the type handled by this exception handler.
* `ExceptionType` in `except* ExceptionType as e:`
*/
ControlFlowNode getType() {
this.getBasicBlock().dominates(result.getBasicBlock()) and
result = this.getNode().(ExceptGroupStmt).getType().getAFlowNode()
}
/**
* Gets the name assigned to the handled exception, if any.
* `e` in `except* ExceptionType as e:`
*/
ControlFlowNode getName() {
this.getBasicBlock().dominates(result.getBasicBlock()) and
result = this.getNode().(ExceptGroupStmt).getName().getAFlowNode()
}
}
private ControlFlowNodeWithPointsTo element_from_tuple_objectapi(Object tuple) {
exists(Tuple t | t = tuple.getOrigin() and result = t.getAnElt().getAFlowNode())
}

View File

@@ -15,7 +15,8 @@ private import semmle.python.pointsto.PointsTo
private import semmle.python.pointsto.PointsToContext
private import semmle.python.objects.TObject
/* Make ObjectInternal visible to save extra imports in user code */
import semmle.python.objects.ObjectInternal
private import semmle.python.objects.ObjectInternal
private import semmle.python.pointsto.Context
abstract class PointsToExtension extends @py_flow_node {
string toString() { result = "PointsToExtension with missing toString" }

View File

@@ -1,10 +1,7 @@
import python
private import LegacyPointsTo
import semmle.python.types.Exceptions
private import semmle.python.pointsto.PointsTo
private import semmle.python.objects.Callables
private import semmle.python.libraries.Zope
private import semmle.python.objects.ObjectInternal
private import semmle.python.types.Builtins
/** A function object, whether written in Python or builtin */

View File

@@ -1,4 +1,5 @@
import python
private import semmle.python.types.ModuleObject
private predicate is_normal_module(ModuleObject m) {
m instanceof BuiltinModuleObject

View File

@@ -2,6 +2,9 @@ import python
private import semmle.python.pointsto.PointsTo
private import semmle.python.objects.ObjectInternal
private import semmle.python.types.ModuleKind
private import semmle.python.types.Object
private import semmle.python.types.ClassObject
private import semmle.python.objects.ObjectAPI
abstract class ModuleObject extends Object {
ModuleValue theModule() {

View File

@@ -1,6 +1,5 @@
import python
private import LegacyPointsTo
private import semmle.python.objects.ObjectInternal
private import semmle.python.types.Builtins
private import semmle.python.internal.CachedStages

View File

@@ -1,8 +1,5 @@
import python
private import LegacyPointsTo
private import semmle.python.types.Object
private import semmle.python.types.ClassObject
private import semmle.python.types.FunctionObject
predicate string_attribute_all(ControlFlowNodeWithPointsTo n, string attr) {
(n.getNode() instanceof Unicode or n.getNode() instanceof Bytes) and

View File

@@ -1,5 +1,5 @@
import python
private import semmle.python.pointsto.PointsTo
private import LegacyPointsTo
/** A helper class for UndefinedClassAttribute.ql and MaybeUndefinedClassAttribute.ql */
class CheckClass extends ClassObject {

View File

@@ -12,6 +12,7 @@
*/
import python
private import LegacyPointsTo
predicate does_nothing(PyFunctionObject f) {
not exists(Stmt s | s.getScope() = f.getFunction() |

View File

@@ -13,6 +13,7 @@
import python
import Equality
private import LegacyPointsTo
predicate class_stores_to_attribute(ClassValue cls, SelfAttributeStore store, string name) {
exists(FunctionValue f |

View File

@@ -12,6 +12,7 @@
*/
import python
private import LegacyPointsTo
ClassObject left_base(ClassObject type, ClassObject base) {
exists(int i | i > 0 and type.getBaseType(i) = base and result = type.getBaseType(i - 1))

View File

@@ -12,6 +12,7 @@
import python
import ClassAttributes
private import LegacyPointsTo
predicate guarded_by_other_attribute(SelfAttributeRead a, CheckClass c) {
c.sometimesDefines(a.getName()) and

View File

@@ -12,6 +12,7 @@
*/
import python
private import LegacyPointsTo
predicate mutates_descriptor(ClassObject cls, SelfAttributeStore s) {
cls.isDescriptorType() and

View File

@@ -12,6 +12,7 @@
*/
import python
private import LegacyPointsTo
class InitCallStmt extends ExprStmt {
InitCallStmt() {

View File

@@ -11,6 +11,7 @@
*/
import python
private import LegacyPointsTo
from PropertyObject prop, ClassObject cls
where cls.declaredAttribute(_) = prop and not cls.failedInference() and not cls.isNewStyle()

View File

@@ -14,6 +14,7 @@
*/
import python
private import LegacyPointsTo
from ClassValue c
where not c.isBuiltin() and not c.isContextManager() and exists(c.declaredAttribute("__del__"))

View File

@@ -12,6 +12,7 @@
*/
import python
private import LegacyPointsTo
from ClassObject c
where not c.isNewStyle() and c.declaresAttribute("__slots__") and not c.failedInference()

View File

@@ -11,6 +11,7 @@
*/
import python
private import LegacyPointsTo
predicate uses_of_super_in_old_style_class(Call s) {
exists(Function f, ClassObject c |

View File

@@ -12,6 +12,7 @@
import python
import ClassAttributes
private import LegacyPointsTo
predicate undefined_class_attribute(SelfAttributeRead a, CheckClass c, int line, string name) {
name = a.getName() and

View File

@@ -13,6 +13,7 @@
*/
import python
private import LegacyPointsTo
predicate fewer_than_two_public_methods(Class cls, int methods) {
(methods = 0 or methods = 1) and

View File

@@ -16,6 +16,7 @@
import python
import Expressions.CallArgs
private import LegacyPointsTo
from Call call, ClassValue cls, string name, FunctionValue init
where

View File

@@ -15,6 +15,7 @@
import python
import Expressions.CallArgs
private import LegacyPointsTo
from Call call, ClassValue cls, string too, string should, int limit, FunctionValue init
where

View File

@@ -12,8 +12,9 @@
*/
import python
private import LegacyPointsTo
from ExceptFlowNode ex, Value t, ClassValue c, ControlFlowNode origin, string what
from ExceptFlowNodeWithPointsTo ex, Value t, ClassValue c, ControlFlowNode origin, string what
where
ex.handledException(t, c, origin) and
(

View File

@@ -14,6 +14,7 @@
import python
import Raising
import Exceptions.NotImplemented
private import LegacyPointsTo
from Raise r, ClassValue t
where

View File

@@ -13,7 +13,6 @@
import python
private import LegacyPointsTo
import semmle.python.pointsto.PointsTo
predicate rhs_in_expr(ControlFlowNode rhs, Compare cmp) {
exists(Cmpop op, int i | cmp.getOp(i) = op and cmp.getComparator(i) = rhs.getNode() |

View File

@@ -2,7 +2,6 @@
import python
private import LegacyPointsTo
private import semmle.python.objects.ObjectInternal
/** 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) {

View File

@@ -13,6 +13,7 @@
import python
import IsComparisons
private import LegacyPointsTo
from Compare comp, Cmpop op, ClassValue c
where

View File

@@ -1,6 +1,7 @@
/** Helper functions for queries that test redundant comparisons. */
import python
private import LegacyPointsTo
/** A comparison where the left and right hand sides appear to be identical. */
class RedundantComparison extends Compare {

View File

@@ -16,6 +16,7 @@
import python
import Expressions.CallArgs
private import LegacyPointsTo
from Call call, FunctionObject func, string name
where

View File

@@ -15,7 +15,6 @@
import python
import LegacyPointsTo
import semmle.python.objects.ObjectInternal
import semmle.python.strings
predicate string_format(BinaryExpr operation, StringLiteral str, Value args, AstNode origin) {

View File

@@ -13,6 +13,7 @@
*/
import python
private import LegacyPointsTo
import CallArgs
from Call call, FunctionValue func, string too, string should, int limit

View File

@@ -12,6 +12,7 @@
*/
import python
private import LegacyPointsTo
predicate explicitly_returns_non_none(Function func) {
exists(Return return |
@@ -21,7 +22,7 @@ predicate explicitly_returns_non_none(Function func) {
}
predicate has_implicit_return(Function func) {
exists(ControlFlowNode fallthru |
exists(ControlFlowNodeWithPointsTo fallthru |
fallthru = func.getFallthroughNode() and not fallthru.unlikelyReachable()
)
or

View File

@@ -10,6 +10,7 @@
*/
import python
private import LegacyPointsTo
predicate slice_method_name(string name) {
name = "__getslice__" or name = "__setslice__" or name = "__delslice__"

View File

@@ -14,6 +14,7 @@
import python
import Expressions.CallArgs
private import LegacyPointsTo
from Call call, FunctionValue func, FunctionValue overridden, string problem
where

View File

@@ -14,6 +14,7 @@
import python
import Expressions.CallArgs
private import LegacyPointsTo
from Call call, FunctionValue func, FunctionValue overriding, string problem
where

View File

@@ -12,6 +12,7 @@
*/
import python
private import LegacyPointsTo
from ClassValue iterable, FunctionValue iter, ClassValue iterator
where

View File

@@ -12,11 +12,12 @@
*/
import python
private import LegacyPointsTo
from FunctionValue method
where
exists(ClassValue c |
c.declaredAttribute("__del__") = method and
method.getScope().getMetrics().getCyclomaticComplexity() > 3
method.getScope().(FunctionMetrics).getCyclomaticComplexity() > 3
)
select method, "Overly complex '__del__' method."

View File

@@ -14,7 +14,7 @@
*/
import python
import semmle.python.objects.Callables
private import LegacyPointsTo
predicate meaningful_return_value(Expr val) {
val instanceof Name

View File

@@ -1,5 +1,6 @@
import python
private import LegacyPointsTo
private import semmle.python.types.ImportTime
predicate is_import_time(Stmt s) { not s.getScope+() instanceof Function }

View File

@@ -13,6 +13,7 @@
import python
import Cyclic
private import LegacyPointsTo
from ModuleValue m1, ModuleValue m2, Stmt imp
where

View File

@@ -12,6 +12,7 @@
*/
import python
private import LegacyPointsTo
predicate modules_imports_itself(ImportingStmt i, ModuleValue m) {
i.getEnclosingModule() = m.getScope() and

View File

@@ -14,6 +14,7 @@
import python
import Cyclic
private import LegacyPointsTo
// This is a potentially crashing bug if
// 1. the imports in the whole cycle are lexically outside a def (and so executed at import time)

View File

@@ -13,6 +13,8 @@
*/
import python
private import LegacyPointsTo
private import semmle.python.types.ImportTime
predicate import_star(ImportStar imp, ModuleValue exporter) {
exporter.importedAs(imp.getImportedModuleName())

View File

@@ -10,6 +10,7 @@
*/
import python
private import LegacyPointsTo
from Function f
select f, f.getMetrics().getNumberOfLinesOfCode() as n order by n desc
from FunctionMetrics f
select f, f.getNumberOfLinesOfCode() as n order by n desc

View File

@@ -11,6 +11,7 @@
*/
import python
private import LegacyPointsTo
from ClassMetrics cls
select cls, cls.getAfferentCoupling() as n order by n desc

View File

@@ -11,6 +11,7 @@
*/
import python
private import LegacyPointsTo
from ClassMetrics cls
select cls, cls.getEfferentCoupling() as n order by n desc

View File

@@ -12,8 +12,10 @@
*/
import python
private import LegacyPointsTo
from Module m, ModuleMetrics mm
where mm = m.getMetrics() and mm.getNumberOfLines() > 0
select m, 100.0 * (mm.getNumberOfLinesOfComments().(float) / mm.getNumberOfLines().(float)) as ratio
from ModuleMetrics mm
where mm.getNumberOfLines() > 0
select mm,
100.0 * (mm.getNumberOfLinesOfComments().(float) / mm.getNumberOfLines().(float)) as ratio
order by ratio desc

Some files were not shown because too many files have changed in this diff Show More