mirror of
https://github.com/github/codeql.git
synced 2026-04-30 19:26:02 +02:00
Merge branch 'main' into orm
This commit is contained in:
@@ -1,3 +1,13 @@
|
||||
## 0.0.11
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Added new SSRF sinks for `httpx`, `pycurl`, `urllib`, `urllib2`, `urllib3`, and `libtaxii`. This improvement was [submitted by @haby0](https://github.com/github/codeql/pull/8275).
|
||||
* The regular expression parser now groups sequences of normal characters. This reduces the number of instances of `RegExpNormalChar`.
|
||||
* Fixed taint propagation for attribute assignment. In the assignment `x.foo = tainted` we no longer treat the entire object `x` as tainted, just because the attribute `foo` contains tainted data. This leads to slightly fewer false positives.
|
||||
* Improved analysis of attributes for data-flow and taint tracking queries, so `getattr`/`setattr` are supported, and a write to an attribute properly stops flow for the old value in that attribute.
|
||||
* Added post-update nodes (`DataFlow::PostUpdateNode`) for arguments in calls that can't be resolved.
|
||||
|
||||
## 0.0.10
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Improved analysis of attributes for data-flow and taint tracking queries, so `getattr`/`setattr` are supported, and a write to an attribute properly stops flow for the old value in that attribute.
|
||||
* Added post-update nodes (`DataFlow::PostUpdateNode`) for arguments in calls that can't be resolved.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* All deprecated predicates/classes/modules that have been deprecated for over a year have been deleted.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
category: deprecated
|
||||
---
|
||||
* Many classes/predicates/modules that had upper-case acronyms have been renamed to follow our style-guide.
|
||||
The old name still exists as a deprecated alias.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
category: deprecated
|
||||
---
|
||||
* Some modules that started with a lowercase letter have been renamed to follow our style-guide.
|
||||
The old name still exists as a deprecated alias.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Fixed taint propagation for attribute assignment. In the assignment `x.foo = tainted` we no longer treat the entire object `x` as tainted, just because the attribute `foo` contains tainted data. This leads to slightly fewer false positives.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The regular expression parser now groups sequences of normal characters. This reduces the number of instances of `RegExpNormalChar`.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: breaking
|
||||
---
|
||||
* The flow state variants of `isBarrier` and `isAdditionalFlowStep` are no longer exposed in the taint tracking library. The `isSanitizer` and `isAdditionalTaintStep` predicates should be used instead.
|
||||
9
python/ql/lib/change-notes/released/0.0.11.md
Normal file
9
python/ql/lib/change-notes/released/0.0.11.md
Normal file
@@ -0,0 +1,9 @@
|
||||
## 0.0.11
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Added new SSRF sinks for `httpx`, `pycurl`, `urllib`, `urllib2`, `urllib3`, and `libtaxii`. This improvement was [submitted by @haby0](https://github.com/github/codeql/pull/8275).
|
||||
* The regular expression parser now groups sequences of normal characters. This reduces the number of instances of `RegExpNormalChar`.
|
||||
* Fixed taint propagation for attribute assignment. In the assignment `x.foo = tainted` we no longer treat the entire object `x` as tainted, just because the attribute `foo` contains tainted data. This leads to slightly fewer false positives.
|
||||
* Improved analysis of attributes for data-flow and taint tracking queries, so `getattr`/`setattr` are supported, and a write to an attribute properly stops flow for the old value in that attribute.
|
||||
* Added post-update nodes (`DataFlow::PostUpdateNode`) for arguments in calls that can't be resolved.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.0.10
|
||||
lastReleaseVersion: 0.0.11
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/python-all
|
||||
version: 0.0.11-dev
|
||||
version: 0.0.12-dev
|
||||
groups: python
|
||||
dbscheme: semmlecode.python.dbscheme
|
||||
extractor: python
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import python
|
||||
|
||||
/** Syntactic node (Class, Function, Module, Expr, Stmt or Comprehension) corresponding to a flow node */
|
||||
/** A syntactic node (Class, Function, Module, Expr, Stmt or Comprehension) corresponding to a flow node */
|
||||
abstract class AstNode extends AstNode_ {
|
||||
/*
|
||||
* Special comment for documentation generation.
|
||||
@@ -61,31 +61,31 @@ abstract class AstNode extends AstNode_ {
|
||||
}
|
||||
|
||||
/* Parents */
|
||||
/** Internal implementation class */
|
||||
/** The parent of a `Function`. Internal implementation class */
|
||||
class FunctionParent extends FunctionParent_ { }
|
||||
|
||||
/** Internal implementation class */
|
||||
/** The parent of an `Arguments` node. Internal implementation class */
|
||||
class ArgumentsParent extends ArgumentsParent_ { }
|
||||
|
||||
/** Internal implementation class */
|
||||
/** The parent of an `ExprList`. Internal implementation class */
|
||||
class ExprListParent extends ExprListParent_ { }
|
||||
|
||||
/** Internal implementation class */
|
||||
/** The parent of an `ExprContext`. Internal implementation class */
|
||||
class ExprContextParent extends ExprContextParent_ { }
|
||||
|
||||
/** Internal implementation class */
|
||||
/** The parent of a `StmtList`. Internal implementation class */
|
||||
class StmtListParent extends StmtListParent_ { }
|
||||
|
||||
/** Internal implementation class */
|
||||
/** The parent of a `StrList`. Internal implementation class */
|
||||
class StrListParent extends StrListParent_ { }
|
||||
|
||||
/** Internal implementation class */
|
||||
/** The parent of an `Expr`. Internal implementation class */
|
||||
class ExprParent extends ExprParent_ { }
|
||||
|
||||
/** Internal implementation class */
|
||||
/** The parent of a `PatternList`. Internal implementation class */
|
||||
class PatternListParent extends PatternListParent_ { }
|
||||
|
||||
/** Internal implementation class */
|
||||
/** The parent of a `Pattern`. Internal implementation class */
|
||||
class PatternParent extends PatternParent_ { }
|
||||
|
||||
class DictItem extends DictItem_, AstNode {
|
||||
@@ -120,7 +120,7 @@ class Comprehension extends Comprehension_, AstNode {
|
||||
class BytesOrStr extends BytesOrStr_ { }
|
||||
|
||||
/**
|
||||
* Part of a string literal formed by implicit concatenation.
|
||||
* A part of a string literal formed by implicit concatenation.
|
||||
* For example the string literal "abc" expressed in the source as `"a" "b" "c"`
|
||||
* would be composed of three `StringPart`s.
|
||||
*/
|
||||
|
||||
@@ -85,12 +85,6 @@ class ClassDef extends Assign {
|
||||
|
||||
/** The scope of a class. This is the scope of all the statements within the class definition */
|
||||
class Class extends Class_, Scope, AstNode {
|
||||
/**
|
||||
* Use getADecorator() instead of getDefinition().getADecorator()
|
||||
* Use getMetaClass() instead of getDefinition().getMetaClass()
|
||||
*/
|
||||
deprecated ClassExpr getDefinition() { result = this.getParent() }
|
||||
|
||||
/** Gets a defined init method of this class */
|
||||
Function getInitMethod() { result.getScope() = this and result.isInitMethod() }
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ class CommentBlock extends @py_comment {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = "Comment block" }
|
||||
|
||||
/** The length of this comment block (in comments) */
|
||||
/** Gets the length of this comment block (in comments) */
|
||||
int length() { result = max(int i | comment_block_part(this, _, i)) }
|
||||
|
||||
/**
|
||||
|
||||
@@ -76,22 +76,22 @@ class CompareOp extends int {
|
||||
}
|
||||
}
|
||||
|
||||
/** The `CompareOp` for "equals". */
|
||||
/** Gets the `CompareOp` for "equals". */
|
||||
CompareOp eq() { result = 1 }
|
||||
|
||||
/** The `CompareOp` for "not equals". */
|
||||
/** Gets the `CompareOp` for "not equals". */
|
||||
CompareOp ne() { result = 2 }
|
||||
|
||||
/** The `CompareOp` for "less than". */
|
||||
/** Gets the `CompareOp` for "less than". */
|
||||
CompareOp lt() { result = 3 }
|
||||
|
||||
/** The `CompareOp` for "less than or equal to". */
|
||||
/** Gets the `CompareOp` for "less than or equal to". */
|
||||
CompareOp le() { result = 4 }
|
||||
|
||||
/** The `CompareOp` for "greater than". */
|
||||
/** Gets the `CompareOp` for "greater than". */
|
||||
CompareOp gt() { result = 5 }
|
||||
|
||||
/** The `CompareOp` for "greater than or equal to". */
|
||||
/** Gets the `CompareOp` for "greater than or equal to". */
|
||||
CompareOp ge() { result = 6 }
|
||||
|
||||
/* Workaround precision limits in floating point numbers */
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import python
|
||||
|
||||
/** Base class for list, set and dictionary comprehensions, and generator expressions. */
|
||||
/** The base class for list, set and dictionary comprehensions, and generator expressions. */
|
||||
abstract class Comp extends Expr {
|
||||
abstract Function getFunction();
|
||||
|
||||
|
||||
@@ -334,6 +334,7 @@ module CodeExecution {
|
||||
|
||||
/**
|
||||
* A data-flow node that constructs an SQL statement.
|
||||
*
|
||||
* Often, it is worthy of an alert if an SQL statement is constructed such that
|
||||
* executing it would be a security risk.
|
||||
*
|
||||
@@ -355,11 +356,14 @@ class SqlConstruction extends DataFlow::Node {
|
||||
module SqlConstruction {
|
||||
/**
|
||||
* A data-flow node that constructs an SQL statement.
|
||||
*
|
||||
* Often, it is worthy of an alert if an SQL statement is constructed such that
|
||||
* executing it would be a security risk.
|
||||
*
|
||||
* If it is important that the SQL statement is indeed executed, then use `SQLExecution`.
|
||||
*
|
||||
* Extend this class to model new APIs. If you want to refine existing API models,
|
||||
* extend `SqlExecution` instead.
|
||||
* extend `SqlConstruction` instead.
|
||||
*/
|
||||
abstract class Range extends DataFlow::Node {
|
||||
/** Gets the argument that specifies the SQL statements to be constructed. */
|
||||
@@ -449,6 +453,105 @@ module RegexExecution {
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides classes for modeling XML-related APIs. */
|
||||
module XML {
|
||||
/**
|
||||
* A data-flow node that constructs an XPath expression.
|
||||
*
|
||||
* Often, it is worthy of an alert if an XPath expression is constructed such that
|
||||
* executing it would be a security risk.
|
||||
*
|
||||
* If it is important that the XPath expression is indeed executed, then use `XPathExecution`.
|
||||
*
|
||||
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||
* extend `XPathConstruction::Range` instead.
|
||||
*/
|
||||
class XPathConstruction extends DataFlow::Node {
|
||||
XPathConstruction::Range range;
|
||||
|
||||
XPathConstruction() { this = range }
|
||||
|
||||
/** Gets the argument that specifies the XPath expressions to be constructed. */
|
||||
DataFlow::Node getXPath() { result = range.getXPath() }
|
||||
|
||||
/**
|
||||
* Gets the name of this XPath expression construction, typically the name of an executing method.
|
||||
* This is used for nice alert messages and should include the module if possible.
|
||||
*/
|
||||
string getName() { result = range.getName() }
|
||||
}
|
||||
|
||||
/** Provides a class for modeling new XPath construction APIs. */
|
||||
module XPathConstruction {
|
||||
/**
|
||||
* A data-flow node that constructs an XPath expression.
|
||||
*
|
||||
* Often, it is worthy of an alert if an XPath expression is constructed such that
|
||||
* executing it would be a security risk.
|
||||
*
|
||||
* Extend this class to model new APIs. If you want to refine existing API models,
|
||||
* extend `XPathConstruction` instead.
|
||||
*/
|
||||
abstract class Range extends DataFlow::Node {
|
||||
/** Gets the argument that specifies the XPath expressions to be constructed. */
|
||||
abstract DataFlow::Node getXPath();
|
||||
|
||||
/**
|
||||
* Gets the name of this XPath expression construction, typically the name of an executing method.
|
||||
* This is used for nice alert messages and should include the module if possible.
|
||||
*/
|
||||
abstract string getName();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A data-flow node that executes a xpath expression.
|
||||
*
|
||||
* If the context of interest is such that merely constructing an XPath expression
|
||||
* would be valuabe to report, then consider using `XPathConstruction`.
|
||||
*
|
||||
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||
* extend `XPathExecution::Range` instead.
|
||||
*/
|
||||
class XPathExecution extends DataFlow::Node {
|
||||
XPathExecution::Range range;
|
||||
|
||||
XPathExecution() { this = range }
|
||||
|
||||
/** Gets the data flow node for the XPath expression being executed by this node. */
|
||||
DataFlow::Node getXPath() { result = range.getXPath() }
|
||||
|
||||
/**
|
||||
* Gets the name of this XPath expression execution, typically the name of an executing method.
|
||||
* This is used for nice alert messages and should include the module if possible.
|
||||
*/
|
||||
string getName() { result = range.getName() }
|
||||
}
|
||||
|
||||
/** Provides classes for modeling new regular-expression execution APIs. */
|
||||
module XPathExecution {
|
||||
/**
|
||||
* A data-flow node that executes a XPath expression.
|
||||
*
|
||||
* If the context of interest is such that merely constructing an XPath expression
|
||||
* would be valuabe to report, then consider using `XPathConstruction`.
|
||||
*
|
||||
* Extend this class to model new APIs. If you want to refine existing API models,
|
||||
* extend `XPathExecution` instead.
|
||||
*/
|
||||
abstract class Range extends DataFlow::Node {
|
||||
/** Gets the data flow node for the XPath expression being executed by this node. */
|
||||
abstract DataFlow::Node getXPath();
|
||||
|
||||
/**
|
||||
* Gets the name of this xpath expression execution, typically the name of an executing method.
|
||||
* This is used for nice alert messages and should include the module if possible.
|
||||
*/
|
||||
abstract string getName();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides classes for modeling LDAP-related APIs. */
|
||||
module LDAP {
|
||||
/**
|
||||
|
||||
@@ -30,9 +30,6 @@ class Expr extends Expr_, AstNode {
|
||||
/** Whether this expression is a constant */
|
||||
predicate isConstant() { not this.isVariable() }
|
||||
|
||||
/** Use isParenthesized instead. */
|
||||
deprecated override predicate isParenthesised() { this.isParenthesized() }
|
||||
|
||||
/** Whether the parenthesized property of this expression is true. */
|
||||
predicate isParenthesized() { Expr_.super.isParenthesised() }
|
||||
|
||||
@@ -49,9 +46,6 @@ class Expr extends Expr_, AstNode {
|
||||
/** Gets an immediate (non-nested) sub-expression of this expression */
|
||||
Expr getASubExpression() { none() }
|
||||
|
||||
/** Use StrConst.getText() instead */
|
||||
deprecated string strValue() { none() }
|
||||
|
||||
override AstNode getAChildNode() { result = this.getASubExpression() }
|
||||
|
||||
/**
|
||||
@@ -315,7 +309,7 @@ class Ellipsis extends Ellipsis_ {
|
||||
}
|
||||
|
||||
/**
|
||||
* Immutable literal expressions (except tuples).
|
||||
* An immutable literal expression (except tuples).
|
||||
* Consists of string (both unicode and byte) literals and numeric literals.
|
||||
*/
|
||||
abstract class ImmutableLiteral extends Expr {
|
||||
@@ -446,6 +440,8 @@ class Unicode extends StrConst {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the quoted representation fo this string.
|
||||
*
|
||||
* 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.
|
||||
@@ -620,8 +616,6 @@ class StrConst extends Str_, ImmutableLiteral {
|
||||
)
|
||||
}
|
||||
|
||||
deprecated override string strValue() { result = this.getS() }
|
||||
|
||||
override Expr getASubExpression() { none() }
|
||||
|
||||
override AstNode getAChildNode() { result = this.getAnImplicitlyConcatenatedPart() }
|
||||
@@ -685,7 +679,7 @@ class False extends BooleanLiteral {
|
||||
override boolean booleanValue() { result = false }
|
||||
}
|
||||
|
||||
/** `None` */
|
||||
/** The `None` constant. */
|
||||
class None extends NameConstant {
|
||||
/* syntax: None */
|
||||
None() { name_consts(this, "None") }
|
||||
@@ -728,20 +722,20 @@ class Guard extends Guard_ {
|
||||
/** A context in which an expression used */
|
||||
class ExprContext extends ExprContext_ { }
|
||||
|
||||
/** Load context, the context of var in len(var) */
|
||||
/** The load context, the context of var in len(var) */
|
||||
class Load extends Load_ { }
|
||||
|
||||
/** Store context, the context of var in var = 0 */
|
||||
/** The store context, the context of var in var = 0 */
|
||||
class Store extends Store_ { }
|
||||
|
||||
/** Delete context, the context of var in del var */
|
||||
/** The delete context, the context of var in del var */
|
||||
class Del extends Del_ { }
|
||||
|
||||
/** This is an artifact of the Python grammar which includes an AugLoad context, even though it is never used. */
|
||||
library class AugLoad extends AugLoad_ { }
|
||||
/** The context of an augmented load. This is an artifact of the Python grammar which includes an AugLoad context, even though it is never used. */
|
||||
class AugLoad extends AugLoad_ { }
|
||||
|
||||
/** Augmented store context, the context of var in var += 1 */
|
||||
/** The augmented store context, the context of var in var += 1 */
|
||||
class AugStore extends AugStore_ { }
|
||||
|
||||
/** Parameter context, the context of var in def f(var): pass */
|
||||
/** The parameter context, the context of var in def f(var): pass */
|
||||
class Param extends Param_ { }
|
||||
|
||||
@@ -2,12 +2,6 @@ import python
|
||||
|
||||
/** A file */
|
||||
class File extends Container, @file {
|
||||
/** DEPRECATED: Use `getAbsolutePath` instead. */
|
||||
deprecated override string getName() { result = this.getAbsolutePath() }
|
||||
|
||||
/** DEPRECATED: Use `getAbsolutePath` instead. */
|
||||
deprecated string getFullName() { result = this.getAbsolutePath() }
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
@@ -115,9 +109,6 @@ private predicate occupied_line(File f, int n) {
|
||||
|
||||
/** A folder (directory) */
|
||||
class Folder extends Container, @folder {
|
||||
/** DEPRECATED: Use `getAbsolutePath` instead. */
|
||||
deprecated override string getName() { result = this.getAbsolutePath() }
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
@@ -156,9 +147,6 @@ class Folder extends Container, @folder {
|
||||
abstract class Container extends @container {
|
||||
Container getParent() { containerparent(result, this) }
|
||||
|
||||
/** Gets a child of this container */
|
||||
deprecated Container getChild() { containerparent(this, result) }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of the path of this container.
|
||||
*
|
||||
@@ -166,8 +154,11 @@ abstract class Container extends @container {
|
||||
*/
|
||||
string toString() { result = this.getAbsolutePath() }
|
||||
|
||||
/** Gets the name of this container */
|
||||
abstract string getName();
|
||||
/**
|
||||
* Gets the name of this container.
|
||||
* DEPRECATED: Use `getAbsolutePath` instead.
|
||||
*/
|
||||
deprecated string getName() { result = this.getAbsolutePath() }
|
||||
|
||||
/**
|
||||
* Gets the relative path of this file or folder from the root folder of the
|
||||
@@ -339,7 +330,7 @@ abstract class Container extends @container {
|
||||
* paths. The list of paths is composed of the paths passed to the extractor and
|
||||
* `sys.path`.
|
||||
*/
|
||||
predicate isImportRoot(int n) { this.getName() = import_path_element(n) }
|
||||
predicate isImportRoot(int n) { this.getAbsolutePath() = import_path_element(n) }
|
||||
|
||||
/** Holds if this folder is the root folder for the standard library. */
|
||||
predicate isStdLibRoot(int major, int minor) {
|
||||
|
||||
@@ -82,30 +82,12 @@ class ControlFlowNode extends @py_flow_node {
|
||||
toAst(this) instanceof NameConstant
|
||||
}
|
||||
|
||||
/** Use NameNode.isLoad() instead */
|
||||
deprecated predicate isUse() { toAst(this) instanceof Name and this.isLoad() }
|
||||
|
||||
/** Use NameNode.isStore() */
|
||||
deprecated predicate isDefinition() { toAst(this) instanceof Name and this.isStore() }
|
||||
|
||||
/** Whether this flow node corresponds to an attribute expression */
|
||||
predicate isAttribute() { toAst(this) instanceof Attribute }
|
||||
|
||||
/** Use AttrNode.isLoad() instead */
|
||||
deprecated predicate isAttributeLoad() { toAst(this) instanceof Attribute and this.isLoad() }
|
||||
|
||||
/** Use AttrNode.isStore() instead */
|
||||
deprecated predicate isAttributeStore() { toAst(this) instanceof Attribute and this.isStore() }
|
||||
|
||||
/** Whether this flow node corresponds to an subscript expression */
|
||||
predicate isSubscript() { toAst(this) instanceof Subscript }
|
||||
|
||||
/** Use SubscriptNode.isLoad() instead */
|
||||
deprecated predicate isSubscriptLoad() { toAst(this) instanceof Subscript and this.isLoad() }
|
||||
|
||||
/** Use SubscriptNode.isStore() instead */
|
||||
deprecated predicate isSubscriptStore() { toAst(this) instanceof Subscript and this.isStore() }
|
||||
|
||||
/** Whether this flow node corresponds to an import member */
|
||||
predicate isImportMember() { toAst(this) instanceof ImportMember }
|
||||
|
||||
@@ -155,7 +137,7 @@ class ControlFlowNode extends @py_flow_node {
|
||||
/** Whether this flow node is the first in its scope */
|
||||
predicate isEntryNode() { py_scope_flow(this, _, -1) }
|
||||
|
||||
/** The value that this ControlFlowNode points-to. */
|
||||
/** Gets the value that this ControlFlowNode points-to. */
|
||||
predicate pointsTo(Value value) { this.pointsTo(_, value, _) }
|
||||
|
||||
/** Gets the value that this ControlFlowNode points-to. */
|
||||
@@ -164,10 +146,10 @@ class ControlFlowNode extends @py_flow_node {
|
||||
/** Gets a value that this ControlFlowNode may points-to. */
|
||||
Value inferredValue() { this.pointsTo(_, result, _) }
|
||||
|
||||
/** The value and origin that this ControlFlowNode points-to. */
|
||||
/** Gets the value and origin that this ControlFlowNode points-to. */
|
||||
predicate pointsTo(Value value, ControlFlowNode origin) { this.pointsTo(_, value, origin) }
|
||||
|
||||
/** The value and origin that this ControlFlowNode points-to, given the context. */
|
||||
/** Gets the value and origin that this ControlFlowNode points-to, given the context. */
|
||||
predicate pointsTo(Context context, Value value, ControlFlowNode origin) {
|
||||
PointsTo::pointsTo(this, context, value, origin)
|
||||
}
|
||||
@@ -317,7 +299,7 @@ class ControlFlowNode extends @py_flow_node {
|
||||
exists(BasicBlock b, int i, int j | this = b.getNode(i) and other = b.getNode(j) and i < j)
|
||||
}
|
||||
|
||||
/* Holds if this CFG node is a branch */
|
||||
/** Holds if this CFG node is a branch */
|
||||
predicate isBranch() { py_true_successors(this, _) or py_false_successors(this, _) }
|
||||
|
||||
ControlFlowNode getAChild() { result = this.getExprChild(this.getBasicBlock()) }
|
||||
@@ -439,12 +421,6 @@ class AttrNode extends ControlFlowNode {
|
||||
)
|
||||
}
|
||||
|
||||
/** Use getObject() instead */
|
||||
deprecated ControlFlowNode getValue() { result = this.getObject() }
|
||||
|
||||
/** Use getObject(name) instead */
|
||||
deprecated ControlFlowNode getValue(string name) { result = this.getObject(name) }
|
||||
|
||||
/**
|
||||
* Gets the flow node corresponding to the object of the attribute expression corresponding to this flow node,
|
||||
* with the matching name
|
||||
@@ -507,18 +483,6 @@ class ImportStarNode extends ControlFlowNode {
|
||||
class SubscriptNode extends ControlFlowNode {
|
||||
SubscriptNode() { toAst(this) instanceof Subscript }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getObject()` instead.
|
||||
* This will be formally deprecated before the end 2018 and removed in 2019.
|
||||
*/
|
||||
deprecated ControlFlowNode getValue() {
|
||||
exists(Subscript s |
|
||||
this.getNode() = s and
|
||||
s.getObject() = result.getNode() and
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
)
|
||||
}
|
||||
|
||||
/** flow node corresponding to the value of the sequence in a subscript operation */
|
||||
ControlFlowNode getObject() {
|
||||
exists(Subscript s |
|
||||
@@ -950,10 +914,6 @@ class NameNode extends ControlFlowNode {
|
||||
/** A control flow node corresponding to a named constant, one of `None`, `True` or `False`. */
|
||||
class NameConstantNode extends NameNode {
|
||||
NameConstantNode() { exists(NameConstant n | py_flow_bb_node(this, n, _, _)) }
|
||||
|
||||
deprecated override predicate defines(Variable v) { none() }
|
||||
|
||||
deprecated override predicate deletes(Variable v) { none() }
|
||||
/*
|
||||
* We ought to override uses as well, but that has
|
||||
* a serious performance impact.
|
||||
@@ -1009,12 +969,6 @@ private module Scopes {
|
||||
scope = n.getEnclosingModule()
|
||||
}
|
||||
|
||||
private predicate maybe_defined(SsaVariable var) {
|
||||
exists(var.getDefinition()) and not py_ssa_phi(var, _) and not var.getDefinition().isDelete()
|
||||
or
|
||||
exists(SsaVariable input | input = var.getAPhiInput() | maybe_defined(input))
|
||||
}
|
||||
|
||||
private predicate maybe_undefined(SsaVariable var) {
|
||||
not exists(var.getDefinition()) and not py_ssa_phi(var, _)
|
||||
or
|
||||
|
||||
@@ -19,17 +19,22 @@ private import semmle.python.frameworks.FastApi
|
||||
private import semmle.python.frameworks.Flask
|
||||
private import semmle.python.frameworks.FlaskAdmin
|
||||
private import semmle.python.frameworks.FlaskSqlAlchemy
|
||||
private import semmle.python.frameworks.Httpx
|
||||
private import semmle.python.frameworks.Idna
|
||||
private import semmle.python.frameworks.Invoke
|
||||
private import semmle.python.frameworks.Jmespath
|
||||
private import semmle.python.frameworks.Ldap
|
||||
private import semmle.python.frameworks.Ldap3
|
||||
private import semmle.python.frameworks.Libtaxii
|
||||
private import semmle.python.frameworks.Libxml2
|
||||
private import semmle.python.frameworks.Lxml
|
||||
private import semmle.python.frameworks.MarkupSafe
|
||||
private import semmle.python.frameworks.Multidict
|
||||
private import semmle.python.frameworks.Mysql
|
||||
private import semmle.python.frameworks.MySQLdb
|
||||
private import semmle.python.frameworks.Peewee
|
||||
private import semmle.python.frameworks.Psycopg2
|
||||
private import semmle.python.frameworks.Pycurl
|
||||
private import semmle.python.frameworks.Pydantic
|
||||
private import semmle.python.frameworks.PyMySQL
|
||||
private import semmle.python.frameworks.Requests
|
||||
@@ -44,5 +49,6 @@ private import semmle.python.frameworks.Toml
|
||||
private import semmle.python.frameworks.Tornado
|
||||
private import semmle.python.frameworks.Twisted
|
||||
private import semmle.python.frameworks.Ujson
|
||||
private import semmle.python.frameworks.Urllib3
|
||||
private import semmle.python.frameworks.Yaml
|
||||
private import semmle.python.frameworks.Yarl
|
||||
|
||||
@@ -5,11 +5,11 @@ import python
|
||||
* It is the syntactic entity that is compiled to a code object.
|
||||
*/
|
||||
class Function extends Function_, Scope, AstNode {
|
||||
/** The expression defining this function */
|
||||
/** Gets the expression defining this function */
|
||||
CallableExpr getDefinition() { result = this.getParent() }
|
||||
|
||||
/**
|
||||
* The scope in which this function occurs, will be a class for a method,
|
||||
* Gets the scope in which this function occurs. This will be a class for a method,
|
||||
* another function for nested functions, generator expressions or comprehensions,
|
||||
* or a module for a plain function.
|
||||
*/
|
||||
@@ -183,8 +183,8 @@ class FunctionDef extends Assign {
|
||||
override Stmt getLastStatement() { result = this.getDefinedFunction().getLastStatement() }
|
||||
}
|
||||
|
||||
/** A function that uses 'fast' locals, stored in the frame not in a dictionary. */
|
||||
class FastLocalsFunction extends Function {
|
||||
/** A function that uses 'fast' locals, stored in the frame not in a dictionary. */
|
||||
FastLocalsFunction() {
|
||||
not exists(ImportStar i | i.getScope() = this) and
|
||||
not exists(Exec e | e.getScope() = this)
|
||||
|
||||
@@ -35,6 +35,8 @@ class ImportExpr extends ImportExpr_ {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the level of this import.
|
||||
*
|
||||
* The language specifies level as -1 if relative imports are to be tried first, 0 for absolute imports,
|
||||
* and level > 0 for explicit relative imports.
|
||||
*/
|
||||
@@ -165,13 +167,6 @@ class Import extends Import_ {
|
||||
result = this.getAName().getValue().(ImportMember).getModule()
|
||||
}
|
||||
|
||||
/**
|
||||
* Use getAnImportedModuleName(),
|
||||
* possibly combined with ModuleObject.importedAs()
|
||||
* Gets a module imported by this import statement
|
||||
*/
|
||||
deprecated Module getAModule() { result.getName() = this.getAnImportedModuleName() }
|
||||
|
||||
/** Whether this a `from ... import ...` statement */
|
||||
predicate isFromImport() { this.getAName().getValue() instanceof ImportMember }
|
||||
|
||||
@@ -216,13 +211,6 @@ class ImportStar extends ImportStar_ {
|
||||
|
||||
override string toString() { result = "from " + this.getModuleExpr().getName() + " import *" }
|
||||
|
||||
/**
|
||||
* Use getAnImportedModuleName(),
|
||||
* possibly combined with ModuleObject.importedAs()
|
||||
* Gets the module imported by this import * statement
|
||||
*/
|
||||
deprecated Module getTheModule() { result.getName() = this.getImportedModuleName() }
|
||||
|
||||
override Expr getASubExpression() { result = this.getModule() }
|
||||
|
||||
override Stmt getASubStatement() { none() }
|
||||
|
||||
@@ -18,7 +18,7 @@ class FunctionMetrics extends Function {
|
||||
int getNumberOfLinesOfDocStrings() { py_docstringlines(this, result) }
|
||||
|
||||
/**
|
||||
* Cyclomatic complexity:
|
||||
* Gets the cyclomatic complexity of the function:
|
||||
* The number of linearly independent paths through the source code.
|
||||
* Computed as E - N + 2P,
|
||||
* where
|
||||
@@ -27,9 +27,9 @@ class FunctionMetrics extends Function {
|
||||
* P = the number of connected components, which for a single function is 1.
|
||||
*/
|
||||
int getCyclomaticComplexity() {
|
||||
exists(int E, int N |
|
||||
N = count(BasicBlock b | b = this.getABasicBlock() and b.likelyReachable()) and
|
||||
E =
|
||||
exists(int e, int n |
|
||||
n = count(BasicBlock b | b = this.getABasicBlock() and b.likelyReachable()) and
|
||||
e =
|
||||
count(BasicBlock b1, BasicBlock b2 |
|
||||
b1 = this.getABasicBlock() and
|
||||
b1.likelyReachable() and
|
||||
@@ -39,7 +39,7 @@ class FunctionMetrics extends Function {
|
||||
not b1.unlikelySuccessor(b2)
|
||||
)
|
||||
|
|
||||
result = E - N + 2
|
||||
result = e - n + 2
|
||||
)
|
||||
}
|
||||
|
||||
@@ -130,13 +130,13 @@ class ClassMetrics extends Class {
|
||||
}
|
||||
|
||||
/**
|
||||
* The afferent coupling of a class is the number of classes that
|
||||
* Gets the afferent coupling of a class -- the number of classes that
|
||||
* directly depend on it.
|
||||
*/
|
||||
int getAfferentCoupling() { result = count(ClassMetrics t | t.dependsOn(this)) }
|
||||
|
||||
/**
|
||||
* The efferent coupling of a class is the number of classes that
|
||||
* Gets the efferent coupling of a class -- the number of classes that
|
||||
* it directly depends on.
|
||||
*/
|
||||
int getEfferentCoupling() { result = count(ClassMetrics t | this.dependsOn(t)) }
|
||||
@@ -273,13 +273,13 @@ class ModuleMetrics extends Module {
|
||||
int getNumberOfLinesOfDocStrings() { py_docstringlines(this, result) }
|
||||
|
||||
/**
|
||||
* The afferent coupling of a class is the number of classes that
|
||||
* Gets the afferent coupling of a class -- the number of classes that
|
||||
* directly depend on it.
|
||||
*/
|
||||
int getAfferentCoupling() { result = count(ModuleMetrics t | t.dependsOn(this)) }
|
||||
|
||||
/**
|
||||
* The efferent coupling of a class is the number of classes that
|
||||
* Gets the efferent coupling of a class -- the number of classes that
|
||||
* it directly depends on.
|
||||
*/
|
||||
int getEfferentCoupling() { result = count(ModuleMetrics t | this.dependsOn(t)) }
|
||||
|
||||
@@ -22,12 +22,13 @@ class Module extends Module_, Scope, AstNode {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the enclosing scope of this module (always none).
|
||||
*
|
||||
* This method will be deprecated in the next release. Please use `getEnclosingScope()` instead.
|
||||
* The enclosing scope of this module (always none)
|
||||
*/
|
||||
override Scope getScope() { none() }
|
||||
|
||||
/** The enclosing scope of this module (always none) */
|
||||
/** Gets the enclosing scope of this module (always none) */
|
||||
override Scope getEnclosingScope() { none() }
|
||||
|
||||
/** Gets the statements forming the body of this module */
|
||||
@@ -98,12 +99,6 @@ class Module extends Module_, Scope, AstNode {
|
||||
/** Gets the metrics for this module */
|
||||
ModuleMetrics getMetrics() { result = this }
|
||||
|
||||
/**
|
||||
* Use ModuleObject.getAnImportedModule() instead.
|
||||
* Gets a module imported by this module
|
||||
*/
|
||||
deprecated Module getAnImportedModule() { result.getName() = this.getAnImportedModuleName() }
|
||||
|
||||
string getAnImportedModuleName() {
|
||||
exists(Import i | i.getEnclosingModule() = this | result = i.getAnImportedModuleName())
|
||||
or
|
||||
@@ -196,7 +191,7 @@ private predicate isPotentialSourcePackage(Folder f) {
|
||||
private predicate isPotentialPackage(Folder f) {
|
||||
exists(f.getFile("__init__.py"))
|
||||
or
|
||||
py_flags_versioned("options.respect_init", "False", _) and major_version() = 2
|
||||
py_flags_versioned("options.respect_init", "False", _) and major_version() = 2 and exists(f)
|
||||
}
|
||||
|
||||
private string moduleNameFromBase(Container file) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import python
|
||||
|
||||
/** Base class for operators */
|
||||
/** The base class for operators */
|
||||
class Operator extends Operator_ {
|
||||
/** Gets the name of the special method used to implement this operator */
|
||||
string getSpecialMethodName() { none() }
|
||||
@@ -131,7 +131,7 @@ class Compare extends Compare_ {
|
||||
}
|
||||
}
|
||||
|
||||
/** List of comparison operators in a comparison */
|
||||
/** A list of comparison operators in a comparison */
|
||||
class CmpopList extends CmpopList_ { }
|
||||
|
||||
/** A comparison operator */
|
||||
|
||||
@@ -10,6 +10,7 @@ class Pattern extends Pattern_, AstNode {
|
||||
override Scope getScope() { result = this.getCase().getScope() }
|
||||
|
||||
/** Gets the case statement containing this pattern */
|
||||
pragma[nomagic]
|
||||
Case getCase() { result.contains(this) }
|
||||
|
||||
override string toString() { result = "Pattern" }
|
||||
|
||||
@@ -9,11 +9,12 @@ class Scope extends Scope_ {
|
||||
Module getEnclosingModule() { result = this.getEnclosingScope().getEnclosingModule() }
|
||||
|
||||
/**
|
||||
* Gets the scope enclosing this scope (modules have no enclosing scope).
|
||||
*
|
||||
* This method will be deprecated in the next release. Please use `getEnclosingScope()` instead.
|
||||
* The reason for this is to avoid confusion around use of `x.getScope+()` where `x` might be an
|
||||
* `AstNode` or a `Variable`. Forcing the users to write `x.getScope().getEnclosingScope*()` ensures that
|
||||
* the apparent semantics and the actual semantics coincide.
|
||||
* [ Gets the scope enclosing this scope (modules have no enclosing scope) ]
|
||||
*/
|
||||
Scope getScope() { none() }
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ private predicate self_attribute(Attribute attr, Class cls) {
|
||||
)
|
||||
}
|
||||
|
||||
/** Helper class for UndefinedClassAttribute.ql & MaybeUndefinedClassAttribute.ql */
|
||||
/** A helper class for UndefinedClassAttribute.ql & MaybeUndefinedClassAttribute.ql */
|
||||
class SelfAttributeRead extends SelfAttribute {
|
||||
SelfAttributeRead() {
|
||||
this.getCtx() instanceof Load and
|
||||
|
||||
@@ -112,6 +112,6 @@ class SpecialMethodCallNode extends PotentialSpecialMethodCallNode {
|
||||
)
|
||||
}
|
||||
|
||||
/** The method that is called. */
|
||||
/** Gets the method that is called. */
|
||||
Value getResolvedSpecialMethod() { result = resolvedSpecialMethod }
|
||||
}
|
||||
|
||||
@@ -323,7 +323,7 @@ class Raise extends Raise_ {
|
||||
override Expr getASubExpression() { py_exprs(result, _, this, _) }
|
||||
|
||||
/**
|
||||
* The expression immediately following the `raise`, this is the
|
||||
* Gets the expression immediately following the `raise`. This is the
|
||||
* exception raised, but not accounting for tuples in Python 2.
|
||||
*/
|
||||
Expr getException() {
|
||||
@@ -332,7 +332,7 @@ class Raise extends Raise_ {
|
||||
result = this.getExc()
|
||||
}
|
||||
|
||||
/** The exception raised, accounting for tuples in Python 2. */
|
||||
/** Gets the exception raised, accounting for tuples in Python 2. */
|
||||
Expr getRaised() {
|
||||
exists(Expr raw | raw = this.getException() |
|
||||
if not major_version() = 2 or not exists(raw.(Tuple).getAnElt())
|
||||
|
||||
@@ -227,7 +227,7 @@ private module SensitiveDataModeling {
|
||||
}
|
||||
|
||||
/**
|
||||
* Any kind of variable assignment (also including with/for) where the name indicates
|
||||
* A variable assignment (also including with/for) where the name indicates
|
||||
* it contains sensitive data.
|
||||
*
|
||||
* Note: We _could_ make any access to a variable with a sensitive name a source of
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
private import python
|
||||
private import internal.TypeTracker as Internal
|
||||
|
||||
/** Any string that may appear as the name of an attribute or access path. */
|
||||
/** A string that may appear as the name of an attribute or access path. */
|
||||
class AttributeName = Internal::ContentName;
|
||||
|
||||
/** Either an attribute name, or the empty string (representing no attribute). */
|
||||
/** An attribute name, or the empty string (representing no attribute). */
|
||||
class OptionalAttributeName = Internal::OptionalContentName;
|
||||
|
||||
/**
|
||||
* Summary of the steps needed to track a value to a given dataflow node.
|
||||
* The summary of the steps needed to track a value to a given dataflow node.
|
||||
*
|
||||
* This can be used to track objects that implement a certain API in order to
|
||||
* recognize calls to that API. Note that type-tracking does not by itself provide a
|
||||
|
||||
@@ -49,8 +49,11 @@ predicate isArgumentNode(ArgumentNode arg, DataFlowCall c, ArgumentPosition pos)
|
||||
//--------
|
||||
predicate isExpressionNode(ControlFlowNode node) { node.getNode() instanceof Expr }
|
||||
|
||||
/** DEPRECATED: Alias for `SyntheticPreUpdateNode` */
|
||||
deprecated module syntheticPreUpdateNode = SyntheticPreUpdateNode;
|
||||
|
||||
/** A module collecting the different reasons for synthesising a pre-update node. */
|
||||
module syntheticPreUpdateNode {
|
||||
module SyntheticPreUpdateNode {
|
||||
class SyntheticPreUpdateNode extends Node, TSyntheticPreUpdateNode {
|
||||
NeedsSyntheticPreUpdateNode post;
|
||||
|
||||
@@ -73,7 +76,7 @@ module syntheticPreUpdateNode {
|
||||
override Node getPreUpdateNode() { result.(SyntheticPreUpdateNode).getPostUpdateNode() = this }
|
||||
|
||||
/**
|
||||
* A label for this kind of node. This will figure in the textual representation of the synthesized pre-update node.
|
||||
* Gets the label for this kind of node. This will figure in the textual representation of the synthesized pre-update node.
|
||||
*
|
||||
* There is currently only one reason for needing a pre-update node, so we always use that as the label.
|
||||
*/
|
||||
@@ -88,10 +91,13 @@ module syntheticPreUpdateNode {
|
||||
CfgNode objectCreationNode() { result.getNode().(CallNode) = any(ClassCall c).getNode() }
|
||||
}
|
||||
|
||||
import syntheticPreUpdateNode
|
||||
import SyntheticPreUpdateNode
|
||||
|
||||
/** DEPRECATED: Alias for `SyntheticPostUpdateNode` */
|
||||
deprecated module syntheticPostUpdateNode = SyntheticPostUpdateNode;
|
||||
|
||||
/** A module collecting the different reasons for synthesising a post-update node. */
|
||||
module syntheticPostUpdateNode {
|
||||
module SyntheticPostUpdateNode {
|
||||
/** A post-update node is synthesized for all nodes which satisfy `NeedsSyntheticPostUpdateNode`. */
|
||||
class SyntheticPostUpdateNode extends PostUpdateNode, TSyntheticPostUpdateNode {
|
||||
NeedsSyntheticPostUpdateNode pre;
|
||||
@@ -118,7 +124,7 @@ module syntheticPostUpdateNode {
|
||||
}
|
||||
|
||||
/**
|
||||
* A label for this kind of node. This will figure in the textual representation of the synthesized post-update node.
|
||||
* Gets the label for this kind of node. This will figure in the textual representation of the synthesized post-update node.
|
||||
* We favour being an arguments as the reason for the post-update node in case multiple reasons apply.
|
||||
*/
|
||||
string label() {
|
||||
@@ -132,6 +138,8 @@ module syntheticPostUpdateNode {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the pre-update node for this node.
|
||||
*
|
||||
* An argument might have its value changed as a result of a call.
|
||||
* Certain arguments, such as implicit self arguments are already post-update nodes
|
||||
* and should not have an extra node synthesised.
|
||||
@@ -153,7 +161,7 @@ module syntheticPostUpdateNode {
|
||||
)
|
||||
}
|
||||
|
||||
/** An object might have its value changed after a store. */
|
||||
/** Gets the pre-update node associated with a store. This is used for when an object might have its value changed after a store. */
|
||||
CfgNode storePreUpdateNode() {
|
||||
exists(Attribute a |
|
||||
result.getNode() = a.getObject().getAFlowNode() and
|
||||
@@ -162,7 +170,7 @@ module syntheticPostUpdateNode {
|
||||
}
|
||||
|
||||
/**
|
||||
* A node marking the state change of an object after a read.
|
||||
* Gets a node marking the state change of an object after a read.
|
||||
*
|
||||
* A reverse read happens when the result of a read is modified, e.g. in
|
||||
* ```python
|
||||
@@ -185,7 +193,7 @@ module syntheticPostUpdateNode {
|
||||
}
|
||||
}
|
||||
|
||||
import syntheticPostUpdateNode
|
||||
import SyntheticPostUpdateNode
|
||||
|
||||
class DataFlowExpr = Expr;
|
||||
|
||||
@@ -621,7 +629,7 @@ newtype TDataFlowCallable =
|
||||
TLambda(Function lambda) { lambda.isLambda() } or
|
||||
TModule(Module m)
|
||||
|
||||
/** Represents a callable. */
|
||||
/** A callable. */
|
||||
abstract class DataFlowCallable extends TDataFlowCallable {
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
@@ -722,7 +730,7 @@ newtype TDataFlowCall =
|
||||
TClassCall(CallNode call) { call = any(ClassValue c | not c.isAbsent()).getACall() } or
|
||||
TSpecialCall(SpecialMethodCallNode special)
|
||||
|
||||
/** Represents a call. */
|
||||
/** A call. */
|
||||
abstract class DataFlowCall extends TDataFlowCall {
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
@@ -747,7 +755,7 @@ abstract class DataFlowCall extends TDataFlowCall {
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a call to a function/lambda.
|
||||
* A call to a function/lambda.
|
||||
* This excludes calls to bound methods, classes, and special methods.
|
||||
* Bound method calls and class calls insert an argument for the explicit
|
||||
* `self` parameter, and special method calls have special argument passing.
|
||||
@@ -834,7 +842,7 @@ class ClassCall extends DataFlowCall, TClassCall {
|
||||
override DataFlowCallable getEnclosingCallable() { result.getScope() = call.getScope() }
|
||||
}
|
||||
|
||||
/** Represents a call to a special method. */
|
||||
/** A call to a special method. */
|
||||
class SpecialCall extends DataFlowCall, TSpecialCall {
|
||||
SpecialMethodCallNode special;
|
||||
|
||||
@@ -1706,6 +1714,8 @@ import IterableUnpacking
|
||||
*/
|
||||
module MatchUnpacking {
|
||||
/**
|
||||
* Holds when there is flow from the subject `nodeFrom` to the (top-level) pattern `nodeTo` of a `match` statement.
|
||||
*
|
||||
* The subject of a match flows to each top-level pattern
|
||||
* (a pattern directly under a `case` statement).
|
||||
*
|
||||
|
||||
@@ -34,7 +34,8 @@ private module Cached {
|
||||
CallStep() or
|
||||
ReturnStep() or
|
||||
StoreStep(ContentName content) or
|
||||
LoadStep(ContentName content)
|
||||
LoadStep(ContentName content) or
|
||||
JumpStep()
|
||||
|
||||
/** Gets the summary resulting from appending `step` to type-tracking summary `tt`. */
|
||||
cached
|
||||
@@ -49,6 +50,9 @@ private module Cached {
|
||||
step = LoadStep(content) and result = MkTypeTracker(hasCall, "")
|
||||
or
|
||||
exists(string p | step = StoreStep(p) and content = "" and result = MkTypeTracker(hasCall, p))
|
||||
or
|
||||
step = JumpStep() and
|
||||
result = MkTypeTracker(false, content)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -67,6 +71,9 @@ private module Cached {
|
||||
)
|
||||
or
|
||||
step = StoreStep(content) and result = MkTypeBackTracker(hasReturn, "")
|
||||
or
|
||||
step = JumpStep() and
|
||||
result = MkTypeBackTracker(false, content)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -110,12 +117,17 @@ class StepSummary extends TStepSummary {
|
||||
exists(string content | this = StoreStep(content) | result = "store " + content)
|
||||
or
|
||||
exists(string content | this = LoadStep(content) | result = "load " + content)
|
||||
or
|
||||
this instanceof JumpStep and result = "jump"
|
||||
}
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate smallstepNoCall(Node nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) {
|
||||
jumpStep(nodeFrom, nodeTo) and
|
||||
summary = JumpStep()
|
||||
or
|
||||
levelStep(nodeFrom, nodeTo) and
|
||||
summary = LevelStep()
|
||||
or
|
||||
exists(string content |
|
||||
|
||||
@@ -14,6 +14,9 @@ predicate simpleLocalFlowStep = DataFlowPrivate::simpleLocalFlowStep/2;
|
||||
|
||||
predicate jumpStep = DataFlowPrivate::jumpStepSharedWithTypeTracker/2;
|
||||
|
||||
/** Holds if there is a level step from `pred` to `succ`. */
|
||||
predicate levelStep(Node pred, Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Gets the name of a possible piece of content. For Python, this is currently only attribute names,
|
||||
* using the name of the attribute for the corresponding content.
|
||||
|
||||
@@ -64,13 +64,30 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
override predicate isSource(DataFlow::Node source) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink.
|
||||
* Holds if `source` is a relevant taint source with the given initial
|
||||
* `state`.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSink(DataFlow::Node sink) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink accepting `state`.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) { none() }
|
||||
|
||||
/** Holds if the node `node` is a taint sanitizer. */
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
@@ -79,6 +96,16 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
defaultTaintSanitizer(node)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the node `node` is a taint sanitizer when the flow state is
|
||||
* `state`.
|
||||
*/
|
||||
predicate isSanitizer(DataFlow::Node node, DataFlow::FlowState state) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) {
|
||||
this.isSanitizer(node, state)
|
||||
}
|
||||
|
||||
/** Holds if taint propagation into `node` is prohibited. */
|
||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||
|
||||
@@ -107,6 +134,25 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
defaultAdditionalTaintStep(node1, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the additional taint propagation step from `node1` to `node2`
|
||||
* must be taken into account in the analysis. This step is only applicable
|
||||
* in `state1` and updates the flow state to `state2`.
|
||||
*/
|
||||
predicate isAdditionalTaintStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
final override predicate isAdditionalFlowStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
this.isAdditionalTaintStep(node1, state1, node2, state2)
|
||||
}
|
||||
|
||||
override predicate allowImplicitRead(DataFlow::Node node, DataFlow::Content c) {
|
||||
(this.isSink(node) or this.isAdditionalTaintStep(node, _)) and
|
||||
defaultImplicitTaintRead(node, c)
|
||||
|
||||
@@ -64,13 +64,30 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
override predicate isSource(DataFlow::Node source) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink.
|
||||
* Holds if `source` is a relevant taint source with the given initial
|
||||
* `state`.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSink(DataFlow::Node sink) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink accepting `state`.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) { none() }
|
||||
|
||||
/** Holds if the node `node` is a taint sanitizer. */
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
@@ -79,6 +96,16 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
defaultTaintSanitizer(node)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the node `node` is a taint sanitizer when the flow state is
|
||||
* `state`.
|
||||
*/
|
||||
predicate isSanitizer(DataFlow::Node node, DataFlow::FlowState state) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) {
|
||||
this.isSanitizer(node, state)
|
||||
}
|
||||
|
||||
/** Holds if taint propagation into `node` is prohibited. */
|
||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||
|
||||
@@ -107,6 +134,25 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
defaultAdditionalTaintStep(node1, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the additional taint propagation step from `node1` to `node2`
|
||||
* must be taken into account in the analysis. This step is only applicable
|
||||
* in `state1` and updates the flow state to `state2`.
|
||||
*/
|
||||
predicate isAdditionalTaintStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
final override predicate isAdditionalFlowStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
this.isAdditionalTaintStep(node1, state1, node2, state2)
|
||||
}
|
||||
|
||||
override predicate allowImplicitRead(DataFlow::Node node, DataFlow::Content c) {
|
||||
(this.isSink(node) or this.isAdditionalTaintStep(node, _)) and
|
||||
defaultImplicitTaintRead(node, c)
|
||||
|
||||
@@ -64,13 +64,30 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
override predicate isSource(DataFlow::Node source) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink.
|
||||
* Holds if `source` is a relevant taint source with the given initial
|
||||
* `state`.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSink(DataFlow::Node sink) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink accepting `state`.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) { none() }
|
||||
|
||||
/** Holds if the node `node` is a taint sanitizer. */
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
@@ -79,6 +96,16 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
defaultTaintSanitizer(node)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the node `node` is a taint sanitizer when the flow state is
|
||||
* `state`.
|
||||
*/
|
||||
predicate isSanitizer(DataFlow::Node node, DataFlow::FlowState state) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) {
|
||||
this.isSanitizer(node, state)
|
||||
}
|
||||
|
||||
/** Holds if taint propagation into `node` is prohibited. */
|
||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||
|
||||
@@ -107,6 +134,25 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
defaultAdditionalTaintStep(node1, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the additional taint propagation step from `node1` to `node2`
|
||||
* must be taken into account in the analysis. This step is only applicable
|
||||
* in `state1` and updates the flow state to `state2`.
|
||||
*/
|
||||
predicate isAdditionalTaintStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
final override predicate isAdditionalFlowStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
this.isAdditionalTaintStep(node1, state1, node2, state2)
|
||||
}
|
||||
|
||||
override predicate allowImplicitRead(DataFlow::Node node, DataFlow::Content c) {
|
||||
(this.isSink(node) or this.isAdditionalTaintStep(node, _)) and
|
||||
defaultImplicitTaintRead(node, c)
|
||||
|
||||
@@ -64,13 +64,30 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
override predicate isSource(DataFlow::Node source) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink.
|
||||
* Holds if `source` is a relevant taint source with the given initial
|
||||
* `state`.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSink(DataFlow::Node sink) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink accepting `state`.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) { none() }
|
||||
|
||||
/** Holds if the node `node` is a taint sanitizer. */
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
@@ -79,6 +96,16 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
defaultTaintSanitizer(node)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the node `node` is a taint sanitizer when the flow state is
|
||||
* `state`.
|
||||
*/
|
||||
predicate isSanitizer(DataFlow::Node node, DataFlow::FlowState state) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) {
|
||||
this.isSanitizer(node, state)
|
||||
}
|
||||
|
||||
/** Holds if taint propagation into `node` is prohibited. */
|
||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||
|
||||
@@ -107,6 +134,25 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
defaultAdditionalTaintStep(node1, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the additional taint propagation step from `node1` to `node2`
|
||||
* must be taken into account in the analysis. This step is only applicable
|
||||
* in `state1` and updates the flow state to `state2`.
|
||||
*/
|
||||
predicate isAdditionalTaintStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
final override predicate isAdditionalFlowStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
this.isAdditionalTaintStep(node1, state1, node2, state2)
|
||||
}
|
||||
|
||||
override predicate allowImplicitRead(DataFlow::Node node, DataFlow::Content c) {
|
||||
(this.isSink(node) or this.isAdditionalTaintStep(node, _)) and
|
||||
defaultImplicitTaintRead(node, c)
|
||||
|
||||
@@ -119,16 +119,6 @@ module TaintTracking {
|
||||
this.(TaintTrackingImplementation).hasFlowPath(src, sink)
|
||||
}
|
||||
|
||||
/* Old query API */
|
||||
/* deprecated */
|
||||
deprecated predicate hasFlow(Source src, Sink sink) {
|
||||
exists(PathSource psrc, PathSink psink |
|
||||
this.hasFlowPath(psrc, psink) and
|
||||
src = psrc.getNode().asCfgNode() and
|
||||
sink = psink.getNode().asCfgNode()
|
||||
)
|
||||
}
|
||||
|
||||
/* New query API */
|
||||
predicate hasSimpleFlow(DataFlow::Node src, DataFlow::Node sink) {
|
||||
exists(PathSource psrc, PathSink psink |
|
||||
|
||||
@@ -79,7 +79,7 @@ abstract class AttributePath extends TAttributePath {
|
||||
predicate noAttribute() { this = TNoAttribute() }
|
||||
}
|
||||
|
||||
/** AttributePath for no attribute. */
|
||||
/** The `AttributePath` for no attribute. */
|
||||
class NoAttribute extends TNoAttribute, AttributePath {
|
||||
override string toString() { result = "no attribute" }
|
||||
|
||||
@@ -88,7 +88,7 @@ class NoAttribute extends TNoAttribute, AttributePath {
|
||||
override AttributePath fromAttribute(string name) { none() }
|
||||
}
|
||||
|
||||
/** AttributePath for an attribute. */
|
||||
/** The `AttributePath` for an attribute. */
|
||||
class NamedAttributePath extends TAttribute, AttributePath {
|
||||
override string toString() {
|
||||
exists(string attr |
|
||||
@@ -124,8 +124,8 @@ newtype TTaintTrackingNode =
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing the (node, context, path, kind) tuple.
|
||||
* Used for context-sensitive path-aware taint-tracking.
|
||||
* A class representing the (node, context, path, kind) tuple.
|
||||
* Used for context-sensitive path-aware taint-tracking.
|
||||
*/
|
||||
class TaintTrackingNode extends TTaintTrackingNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
@@ -900,22 +900,6 @@ private class EssaTaintTracking extends string {
|
||||
or
|
||||
result = this.testEvaluates(defn, not_operand(test), use, src).booleanNot()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `test` is the test in a branch and `use` is that test
|
||||
* with all the `not` prefixes removed.
|
||||
*/
|
||||
private predicate boolean_filter(ControlFlowNode test, ControlFlowNode use) {
|
||||
any(PyEdgeRefinement ref).getTest() = test and
|
||||
(
|
||||
use = test
|
||||
or
|
||||
exists(ControlFlowNode notuse |
|
||||
this.boolean_filter(test, notuse) and
|
||||
use = not_operand(notuse)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate testEvaluatesMaybe(ControlFlowNode test, ControlFlowNode use) {
|
||||
@@ -991,7 +975,7 @@ int iterable_unpacking_descent(SequenceNode left_parent, ControlFlowNode left_de
|
||||
}
|
||||
|
||||
module Implementation {
|
||||
/* A call that returns a copy (or similar) of the argument */
|
||||
/** Holds if `tonode` is a call that returns a copy (or similar) of the argument `fromnode` */
|
||||
predicate copyCall(ControlFlowNode fromnode, CallNode tonode) {
|
||||
tonode.getFunction().(AttrNode).getObject("copy") = fromnode
|
||||
or
|
||||
|
||||
@@ -2,24 +2,7 @@ import semmle.python.dataflow.TaintTracking
|
||||
private import semmle.python.objects.ObjectInternal
|
||||
import semmle.python.dataflow.Implementation
|
||||
|
||||
/* For backwards compatibility -- Use `TaintTrackingContext` instead. */
|
||||
deprecated class CallContext extends TaintTrackingContext {
|
||||
TaintTrackingContext getCallee(CallNode call) { result.getCaller(call) = this }
|
||||
|
||||
predicate appliesToScope(Scope s) {
|
||||
exists(PythonFunctionObjectInternal func, TaintKind param, AttributePath path, int n |
|
||||
this = TParamContext(param, path, n) and
|
||||
exists(TaintTrackingImplementation impl |
|
||||
impl.callWithTaintedArgument(_, _, _, func, n, path, param) and
|
||||
s = func.getScope()
|
||||
)
|
||||
)
|
||||
or
|
||||
this.isTop()
|
||||
}
|
||||
}
|
||||
|
||||
/* Backwards compatibility with config-less taint-tracking */
|
||||
/** A configuration that provides backwards compatibility with config-less taint-tracking */
|
||||
private class LegacyConfiguration extends TaintTracking::Configuration {
|
||||
LegacyConfiguration() {
|
||||
/* A name that won't be accidentally chosen by users */
|
||||
|
||||
@@ -138,9 +138,6 @@ abstract class TaintKind extends string {
|
||||
exists(TaintedNode n | n.getTaintKind() = this and n.getCfgNode() = expr)
|
||||
}
|
||||
|
||||
/** DEPRECATED -- Use getType() instead */
|
||||
deprecated ClassObject getClass() { none() }
|
||||
|
||||
/**
|
||||
* Gets the class of this kind of taint.
|
||||
* For example, if this were a kind of string taint
|
||||
@@ -180,7 +177,7 @@ abstract class TaintKind extends string {
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of `TaintKind`, so the two types can be used interchangeably.
|
||||
* An Alias of `TaintKind`, so the two types can be used interchangeably.
|
||||
*/
|
||||
class FlowLabel = TaintKind;
|
||||
|
||||
@@ -561,7 +558,7 @@ module DataFlowExtension {
|
||||
ControlFlowNode getACalleeSuccessorNode(CallNode call) { none() }
|
||||
}
|
||||
|
||||
/** Data flow variable that modifies the basic data-flow. */
|
||||
/** A data flow variable that modifies the basic data-flow. */
|
||||
class DataFlowVariable extends EssaVariable {
|
||||
/**
|
||||
* Gets a successor node for data-flow.
|
||||
@@ -608,49 +605,11 @@ private import semmle.python.pointsto.PointsTo
|
||||
*/
|
||||
module DataFlow {
|
||||
/**
|
||||
* Generic taint kind, source and sink classes for convenience and
|
||||
* The generic taint kind, source and sink classes for convenience and
|
||||
* compatibility with other language libraries
|
||||
*/
|
||||
class Extension = DataFlowExtension::DataFlowNode;
|
||||
|
||||
abstract deprecated class Configuration extends string {
|
||||
bindingset[this]
|
||||
Configuration() { this = this }
|
||||
|
||||
abstract predicate isSource(ControlFlowNode source);
|
||||
|
||||
abstract predicate isSink(ControlFlowNode sink);
|
||||
|
||||
private predicate hasFlowPath(TaintedNode source, TaintedNode sink) {
|
||||
source.getConfiguration() = this and
|
||||
this.isSource(source.getCfgNode()) and
|
||||
this.isSink(sink.getCfgNode()) and
|
||||
source.flowsTo(sink)
|
||||
}
|
||||
|
||||
predicate hasFlow(ControlFlowNode source, ControlFlowNode sink) {
|
||||
exists(TaintedNode psource, TaintedNode psink |
|
||||
psource.getCfgNode() = source and
|
||||
psink.getCfgNode() = sink and
|
||||
this.isSource(source) and
|
||||
this.isSink(sink) and
|
||||
this.hasFlowPath(psource, psink)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
deprecated private class ConfigurationAdapter extends TaintTracking::Configuration instanceof Configuration {
|
||||
override predicate isSource(DataFlow::Node node, TaintKind kind) {
|
||||
Configuration.super.isSource(node.asCfgNode()) and
|
||||
kind instanceof DataFlowType
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node node, TaintKind kind) {
|
||||
Configuration.super.isSink(node.asCfgNode()) and
|
||||
kind instanceof DataFlowType
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TDataFlowNode =
|
||||
TEssaNode(EssaVariable var) or
|
||||
TCfgNode(ControlFlowNode node)
|
||||
@@ -670,9 +629,6 @@ module DataFlow {
|
||||
abstract Location getLocation();
|
||||
|
||||
AstNode asAstNode() { result = this.asCfgNode().getNode() }
|
||||
|
||||
/** For backwards compatibility -- Use asAstNode() instead */
|
||||
deprecated AstNode getNode() { result = this.asAstNode() }
|
||||
}
|
||||
|
||||
class CfgNode extends Node, TCfgNode {
|
||||
@@ -709,9 +665,10 @@ module DataFlow {
|
||||
}
|
||||
|
||||
deprecated private class DataFlowType extends TaintKind {
|
||||
// this only exists to avoid an empty recursion error in the type checker
|
||||
DataFlowType() {
|
||||
this = "Data flow" and
|
||||
exists(DataFlow::Configuration c)
|
||||
1 = 2
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -158,13 +158,13 @@ private predicate defn_of_instance_attribute(Assign asgn, Class c, string name)
|
||||
)
|
||||
}
|
||||
|
||||
/* Whether asgn defines an attribute of a class */
|
||||
/** Holds if asgn defines an attribute of a class */
|
||||
private predicate defn_of_class_attribute(Assign asgn, Class c, string name) {
|
||||
asgn.getScope() = c and
|
||||
asgn.getATarget().(Name).getId() = name
|
||||
}
|
||||
|
||||
/* Holds if `value` is a value assigned to the `name`d attribute of module `m`. */
|
||||
/** Holds if `value` is a value assigned to the `name`d attribute of module `m`. */
|
||||
private predicate defn_of_module_attribute(ControlFlowNode value, Module m, string name) {
|
||||
exists(DefinitionNode def |
|
||||
def.getScope() = m and
|
||||
|
||||
@@ -134,7 +134,7 @@ private newtype TEssaDefinition =
|
||||
TPhiFunction(SsaSourceVariable v, BasicBlock b) { EssaDefinitions::phiNode(v, b) }
|
||||
|
||||
/**
|
||||
* Definition of an extended-SSA (ESSA) variable.
|
||||
* A definition of an extended-SSA (ESSA) variable.
|
||||
* There is exactly one definition for each variable,
|
||||
* and exactly one variable for each definition.
|
||||
*/
|
||||
@@ -171,6 +171,9 @@ abstract class EssaDefinition extends TEssaDefinition {
|
||||
EssaVariable getVariable() { result.getDefinition() = this }
|
||||
|
||||
abstract BasicBlock getBasicBlock();
|
||||
|
||||
/** Gets the name of the primary QL class for this element. */
|
||||
string getAPrimaryQlClass() { result = "EssaDefinition" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -216,13 +219,15 @@ class EssaEdgeRefinement extends EssaDefinition, TEssaEdgeDefinition {
|
||||
}
|
||||
|
||||
override string getRepresentation() {
|
||||
result = this.getAQlClass() + "(" + this.getInput().getRepresentation() + ")"
|
||||
result = this.getAPrimaryQlClass() + "(" + this.getInput().getRepresentation() + ")"
|
||||
}
|
||||
|
||||
/** Gets the scope of the variable defined by this definition. */
|
||||
override Scope getScope() { result = this.getPredecessor().getScope() }
|
||||
|
||||
override BasicBlock getBasicBlock() { result = this.getSuccessor() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "EssaEdgeRefinement" }
|
||||
}
|
||||
|
||||
/** A Phi-function as specified in classic SSA form. */
|
||||
@@ -366,6 +371,8 @@ class PhiFunction extends EssaDefinition, TPhiFunction {
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "PhiFunction" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -396,7 +403,7 @@ class EssaNodeDefinition extends EssaDefinition, TEssaNodeDefinition {
|
||||
|
||||
override Location getLocation() { result = this.getDefiningNode().getLocation() }
|
||||
|
||||
override string getRepresentation() { result = this.getAQlClass() }
|
||||
override string getRepresentation() { result = this.getAPrimaryQlClass() }
|
||||
|
||||
override Scope getScope() {
|
||||
exists(BasicBlock defb |
|
||||
@@ -414,6 +421,8 @@ class EssaNodeDefinition extends EssaDefinition, TEssaNodeDefinition {
|
||||
}
|
||||
|
||||
override BasicBlock getBasicBlock() { result = this.getDefiningNode().getBasicBlock() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "EssaNodeDefinition" }
|
||||
}
|
||||
|
||||
/** A definition of an ESSA variable that takes another ESSA variable as an input. */
|
||||
@@ -448,10 +457,10 @@ class EssaNodeRefinement extends EssaDefinition, TEssaNodeRefinement {
|
||||
override Location getLocation() { result = this.getDefiningNode().getLocation() }
|
||||
|
||||
override string getRepresentation() {
|
||||
result = this.getAQlClass() + "(" + this.getInput().getRepresentation() + ")"
|
||||
result = this.getAPrimaryQlClass() + "(" + this.getInput().getRepresentation() + ")"
|
||||
or
|
||||
not exists(this.getInput()) and
|
||||
result = this.getAQlClass() + "(" + this.getSourceVariable().getName() + "??)"
|
||||
result = this.getAPrimaryQlClass() + "(" + this.getSourceVariable().getName() + "??)"
|
||||
}
|
||||
|
||||
override Scope getScope() {
|
||||
@@ -470,6 +479,8 @@ class EssaNodeRefinement extends EssaDefinition, TEssaNodeRefinement {
|
||||
}
|
||||
|
||||
override BasicBlock getBasicBlock() { result = this.getDefiningNode().getBasicBlock() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "EssaNodeRefinement" }
|
||||
}
|
||||
|
||||
pragma[noopt]
|
||||
@@ -483,12 +494,6 @@ private EssaVariable potential_input(EssaNodeRefinement ref) {
|
||||
)
|
||||
}
|
||||
|
||||
/* For backwards compatibility */
|
||||
deprecated class PyNodeDefinition = EssaNodeDefinition;
|
||||
|
||||
/* For backwards compatibility */
|
||||
deprecated class PyNodeRefinement = EssaNodeRefinement;
|
||||
|
||||
/** An assignment to a variable `v = val` */
|
||||
class AssignmentDefinition extends EssaNodeDefinition {
|
||||
AssignmentDefinition() {
|
||||
@@ -500,9 +505,11 @@ class AssignmentDefinition extends EssaNodeDefinition {
|
||||
}
|
||||
|
||||
override string getRepresentation() { result = this.getValue().getNode().toString() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "AssignmentDefinition" }
|
||||
}
|
||||
|
||||
/** Capture of a raised exception `except ExceptionType ex:` */
|
||||
/** A capture of a raised exception `except ExceptionType ex:` */
|
||||
class ExceptionCapture extends EssaNodeDefinition {
|
||||
ExceptionCapture() {
|
||||
SsaSource::exception_capture(this.getSourceVariable(), this.getDefiningNode())
|
||||
@@ -516,6 +523,8 @@ class ExceptionCapture extends EssaNodeDefinition {
|
||||
}
|
||||
|
||||
override string getRepresentation() { result = "except " + this.getSourceVariable().getName() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ExceptionCapture" }
|
||||
}
|
||||
|
||||
/** An assignment to a variable as part of a multiple assignment `..., v, ... = val` */
|
||||
@@ -536,6 +545,8 @@ class MultiAssignmentDefinition extends EssaNodeDefinition {
|
||||
SsaSource::multi_assignment_definition(this.getSourceVariable(), this.getDefiningNode(), index,
|
||||
lhs)
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "MultiAssignmentDefinition" }
|
||||
}
|
||||
|
||||
/** A definition of a variable in a `with` statement */
|
||||
@@ -543,6 +554,8 @@ class WithDefinition extends EssaNodeDefinition {
|
||||
WithDefinition() { SsaSource::with_definition(this.getSourceVariable(), this.getDefiningNode()) }
|
||||
|
||||
override string getRepresentation() { result = "with" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "WithDefinition" }
|
||||
}
|
||||
|
||||
/** A definition of a variable via a capture pattern */
|
||||
@@ -552,6 +565,8 @@ class PatternCaptureDefinition extends EssaNodeDefinition {
|
||||
}
|
||||
|
||||
override string getRepresentation() { result = "pattern capture" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "PatternCaptureDefinition" }
|
||||
}
|
||||
|
||||
/** A definition of a variable via a pattern alias */
|
||||
@@ -561,6 +576,8 @@ class PatternAliasDefinition extends EssaNodeDefinition {
|
||||
}
|
||||
|
||||
override string getRepresentation() { result = "pattern alias" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "PatternAliasDefinition" }
|
||||
}
|
||||
|
||||
/** A definition of a variable by declaring it as a parameter */
|
||||
@@ -594,6 +611,8 @@ class ParameterDefinition extends EssaNodeDefinition {
|
||||
|
||||
/** Gets the `Parameter` this `ParameterDefinition` represents. */
|
||||
Parameter getParameter() { result = this.getDefiningNode().getNode() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ParameterDefinition" }
|
||||
}
|
||||
|
||||
/** A deletion of a variable `del v` */
|
||||
@@ -601,10 +620,12 @@ class DeletionDefinition extends EssaNodeDefinition {
|
||||
DeletionDefinition() {
|
||||
SsaSource::deletion_definition(this.getSourceVariable(), this.getDefiningNode())
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "DeletionDefinition" }
|
||||
}
|
||||
|
||||
/**
|
||||
* Definition of variable at the entry of a scope. Usually this represents the transfer of
|
||||
* A definition of variable at the entry of a scope. Usually this represents the transfer of
|
||||
* a global or non-local variable from one scope to another.
|
||||
*/
|
||||
class ScopeEntryDefinition extends EssaNodeDefinition {
|
||||
@@ -614,16 +635,20 @@ class ScopeEntryDefinition extends EssaNodeDefinition {
|
||||
}
|
||||
|
||||
override Scope getScope() { result.getEntryNode() = this.getDefiningNode() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ScopeEntryDefinition" }
|
||||
}
|
||||
|
||||
/** Possible redefinition of variable via `from ... import *` */
|
||||
/** A possible redefinition of variable via `from ... import *` */
|
||||
class ImportStarRefinement extends EssaNodeRefinement {
|
||||
ImportStarRefinement() {
|
||||
SsaSource::import_star_refinement(this.getSourceVariable(), _, this.getDefiningNode())
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ImportStarRefinement" }
|
||||
}
|
||||
|
||||
/** Assignment of an attribute `obj.attr = val` */
|
||||
/** An assignment of an attribute `obj.attr = val` */
|
||||
class AttributeAssignment extends EssaNodeRefinement {
|
||||
AttributeAssignment() {
|
||||
SsaSource::attribute_assignment_refinement(this.getSourceVariable(), _, this.getDefiningNode())
|
||||
@@ -635,12 +660,16 @@ class AttributeAssignment extends EssaNodeRefinement {
|
||||
|
||||
override string getRepresentation() {
|
||||
result =
|
||||
this.getAQlClass() + " '" + this.getName() + "'(" + this.getInput().getRepresentation() + ")"
|
||||
this.getAPrimaryQlClass() + " '" + this.getName() + "'(" + this.getInput().getRepresentation()
|
||||
+ ")"
|
||||
or
|
||||
not exists(this.getInput()) and
|
||||
result =
|
||||
this.getAQlClass() + " '" + this.getName() + "'(" + this.getSourceVariable().getName() + "??)"
|
||||
this.getAPrimaryQlClass() + " '" + this.getName() + "'(" + this.getSourceVariable().getName() +
|
||||
"??)"
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "AttributeAssignment" }
|
||||
}
|
||||
|
||||
/** A use of a variable as an argument, `foo(v)`, which might modify the object referred to. */
|
||||
@@ -654,15 +683,19 @@ class ArgumentRefinement extends EssaNodeRefinement {
|
||||
ControlFlowNode getArgument() { result = argument }
|
||||
|
||||
CallNode getCall() { result = this.getDefiningNode() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ArgumentRefinement" }
|
||||
}
|
||||
|
||||
/** Deletion of an attribute `del obj.attr`. */
|
||||
/** A deletion of an attribute `del obj.attr`. */
|
||||
class EssaAttributeDeletion extends EssaNodeRefinement {
|
||||
EssaAttributeDeletion() {
|
||||
SsaSource::attribute_deletion_refinement(this.getSourceVariable(), _, this.getDefiningNode())
|
||||
}
|
||||
|
||||
string getName() { result = this.getDefiningNode().(AttrNode).getName() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "EssaAttributeDeletion" }
|
||||
}
|
||||
|
||||
/** A pi-node (guard) with only one successor. */
|
||||
@@ -690,10 +723,12 @@ class SingleSuccessorGuard extends EssaNodeRefinement {
|
||||
test = this.getDefiningNode() and
|
||||
SsaSource::test_refinement(this.getSourceVariable(), use, test)
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "SingleSuccessorGuard" }
|
||||
}
|
||||
|
||||
/**
|
||||
* Implicit definition of the names of sub-modules in a package.
|
||||
* An implicit definition of the names of sub-modules in a package.
|
||||
* Although the interpreter does not pre-define these names, merely populating them
|
||||
* as they are imported, this is a good approximation for static analysis.
|
||||
*/
|
||||
@@ -701,11 +736,13 @@ class ImplicitSubModuleDefinition extends EssaNodeDefinition {
|
||||
ImplicitSubModuleDefinition() {
|
||||
SsaSource::init_module_submodule_defn(this.getSourceVariable(), this.getDefiningNode())
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ImplicitSubModuleDefinition" }
|
||||
}
|
||||
|
||||
/** An implicit (possible) definition of an escaping variable at a call-site */
|
||||
class CallsiteRefinement extends EssaNodeRefinement {
|
||||
override string toString() { result = "CallsiteRefinement" }
|
||||
override string toString() { result = "CallSiteRefinement" }
|
||||
|
||||
CallsiteRefinement() {
|
||||
exists(SsaSourceVariable var, ControlFlowNode defn |
|
||||
@@ -718,6 +755,8 @@ class CallsiteRefinement extends EssaNodeRefinement {
|
||||
}
|
||||
|
||||
CallNode getCall() { this.getDefiningNode() = result }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "CallsiteRefinement" }
|
||||
}
|
||||
|
||||
/** An implicit (possible) modification of the object referred at a method call */
|
||||
@@ -728,14 +767,18 @@ class MethodCallsiteRefinement extends EssaNodeRefinement {
|
||||
}
|
||||
|
||||
CallNode getCall() { this.getDefiningNode() = result }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "MethodCallsiteRefinement" }
|
||||
}
|
||||
|
||||
/** An implicit (possible) modification of `self` at a method call */
|
||||
class SelfCallsiteRefinement extends MethodCallsiteRefinement {
|
||||
SelfCallsiteRefinement() { this.getSourceVariable().(Variable).isSelf() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "SelfCallsiteRefinement" }
|
||||
}
|
||||
|
||||
/** Python specific sub-class of generic EssaEdgeRefinement */
|
||||
/** A Python specific sub-class of generic EssaEdgeRefinement */
|
||||
class PyEdgeRefinement extends EssaEdgeRefinement {
|
||||
override string getRepresentation() {
|
||||
/*
|
||||
@@ -750,4 +793,6 @@ class PyEdgeRefinement extends EssaEdgeRefinement {
|
||||
}
|
||||
|
||||
ControlFlowNode getTest() { result = this.getPredecessor().getLastNode() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "PyEdgeRefinement" }
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ private module SsaComputeImpl {
|
||||
Liveness::liveAtEntry(v, succ)
|
||||
}
|
||||
|
||||
/** A phi node for `v` at the beginning of basic block `b`. */
|
||||
/** Holds if there is a phi node for `v` at the beginning of basic block `b`. */
|
||||
cached
|
||||
predicate phiNode(SsaSourceVariable v, BasicBlock b) {
|
||||
(
|
||||
@@ -175,8 +175,8 @@ private module SsaComputeImpl {
|
||||
}
|
||||
|
||||
/**
|
||||
* A ranking of the indices `i` at which there is an SSA definition or use of
|
||||
* `v` in the basic block `b`.
|
||||
* Holds if the `rankix`th definition or use of the SSA variable `v` in the basic block `b` occurs
|
||||
* at index `i`.
|
||||
*
|
||||
* Basic block indices are translated to rank indices in order to skip
|
||||
* irrelevant indices at which there is no definition or use when traversing
|
||||
@@ -187,14 +187,14 @@ private module SsaComputeImpl {
|
||||
i = rank[rankix](int j | variableDef(v, _, b, j) or variableUse(v, _, b, j))
|
||||
}
|
||||
|
||||
/** A definition of a variable occurring at the specified rank index in basic block `b`. */
|
||||
/** Holds if there is a definition of a variable occurring at the specified rank index in basic block `b`. */
|
||||
cached
|
||||
predicate defRank(SsaSourceVariable v, BasicBlock b, int rankix, int i) {
|
||||
variableDef(v, _, b, i) and
|
||||
defUseRank(v, b, rankix, i)
|
||||
}
|
||||
|
||||
/** A variable access `use` of `v` in `b` at index `i`. */
|
||||
/** Holds if there is a variable access `use` of `v` in `b` at index `i`. */
|
||||
cached
|
||||
predicate variableUse(SsaSourceVariable v, ControlFlowNode use, BasicBlock b, int i) {
|
||||
(v.getAUse() = use or v.hasRefinement(use, _)) and
|
||||
@@ -205,7 +205,7 @@ private module SsaComputeImpl {
|
||||
}
|
||||
|
||||
/**
|
||||
* A definition of an SSA variable occurring at the specified position.
|
||||
* Holds if there is a definition of an SSA variable occurring at the specified position.
|
||||
* This is either a phi node, a `VariableUpdate`, or a parameter.
|
||||
*/
|
||||
cached
|
||||
@@ -227,7 +227,7 @@ private module SsaComputeImpl {
|
||||
* dominance.
|
||||
*/
|
||||
|
||||
/** The maximum rank index for the given variable and basic block. */
|
||||
/** Gets the maximum rank index for the given variable and basic block. */
|
||||
cached
|
||||
int lastRank(SsaSourceVariable v, BasicBlock b) {
|
||||
result = max(int rankix | defUseRank(v, b, rankix, _))
|
||||
@@ -253,7 +253,7 @@ private module SsaComputeImpl {
|
||||
i = piIndex()
|
||||
}
|
||||
|
||||
/** The SSA definition reaches the rank index `rankix` in its own basic block `b`. */
|
||||
/** Holds if the SSA definition reaches the rank index `rankix` in its own basic block `b`. */
|
||||
cached
|
||||
predicate ssaDefReachesRank(SsaSourceVariable v, BasicBlock b, int i, int rankix) {
|
||||
ssaDefRank(v, b, rankix, i)
|
||||
@@ -264,7 +264,7 @@ private module SsaComputeImpl {
|
||||
}
|
||||
|
||||
/**
|
||||
* The SSA definition of `v` at `def` reaches `use` in the same basic block
|
||||
* Holds if the SSA definition of `v` at `def` reaches `use` in the same basic block
|
||||
* without crossing another SSA definition of `v`.
|
||||
*/
|
||||
cached
|
||||
@@ -303,7 +303,7 @@ private module SsaComputeImpl {
|
||||
}
|
||||
|
||||
/**
|
||||
* The SSA definition of `v` at `def` reaches the end of a basic block `b`, at
|
||||
* Holds if the SSA definition of `v` at `def` reaches the end of a basic block `b`, at
|
||||
* which point it is still live, without crossing another SSA definition of `v`.
|
||||
*/
|
||||
cached
|
||||
@@ -320,7 +320,7 @@ private module SsaComputeImpl {
|
||||
}
|
||||
|
||||
/**
|
||||
* The SSA definition of `v` at `(defbb, defindex)` reaches `use` without crossing another
|
||||
* Holds if the SSA definition of `v` at `(defbb, defindex)` reaches `use` without crossing another
|
||||
* SSA definition of `v`.
|
||||
*/
|
||||
cached
|
||||
@@ -360,7 +360,7 @@ private module SsaComputeImpl {
|
||||
i = rank[rankix](int j | variableDefine(v, _, b, j) or variableSourceUse(v, _, b, j))
|
||||
}
|
||||
|
||||
/** A variable access `use` of `v` in `b` at index `i`. */
|
||||
/** Holds if there is a variable access `use` of `v` in `b` at index `i`. */
|
||||
cached
|
||||
predicate variableSourceUse(SsaSourceVariable v, ControlFlowNode use, BasicBlock b, int i) {
|
||||
v.getASourceUse() = use and
|
||||
|
||||
@@ -14,7 +14,7 @@ abstract class GeneratedFile extends File {
|
||||
* There is no formal reason for the above, it just seems to work well in practice.
|
||||
*/
|
||||
|
||||
library class GenericGeneratedFile extends GeneratedFile {
|
||||
class GenericGeneratedFile extends GeneratedFile {
|
||||
GenericGeneratedFile() {
|
||||
not this instanceof SpecificGeneratedFile and
|
||||
(
|
||||
@@ -103,7 +103,7 @@ private predicate auto_generated(File f) {
|
||||
/**
|
||||
* A file generated by a template engine
|
||||
*/
|
||||
abstract library class SpecificGeneratedFile extends GeneratedFile {
|
||||
abstract class SpecificGeneratedFile extends GeneratedFile {
|
||||
/*
|
||||
* Currently cover Spitfire, Pyxl and Mako.
|
||||
* Django templates are not compiled to Python.
|
||||
@@ -112,7 +112,7 @@ abstract library class SpecificGeneratedFile extends GeneratedFile {
|
||||
|
||||
}
|
||||
|
||||
/** File generated by the spitfire templating engine */
|
||||
/** A file generated by the spitfire templating engine */
|
||||
class SpitfireGeneratedFile extends SpecificGeneratedFile {
|
||||
SpitfireGeneratedFile() {
|
||||
exists(Module m | m.getFile() = this and not m instanceof SpitfireTemplate |
|
||||
@@ -127,14 +127,14 @@ class SpitfireGeneratedFile extends SpecificGeneratedFile {
|
||||
override string getTool() { result = "spitfire" }
|
||||
}
|
||||
|
||||
/** File generated by the pyxl templating engine */
|
||||
/** A file generated by the pyxl templating engine */
|
||||
class PyxlGeneratedFile extends SpecificGeneratedFile {
|
||||
PyxlGeneratedFile() { this.getSpecifiedEncoding() = "pyxl" }
|
||||
|
||||
override string getTool() { result = "pyxl" }
|
||||
}
|
||||
|
||||
/** File generated by the mako templating engine */
|
||||
/** A file generated by the mako templating engine */
|
||||
class MakoGeneratedFile extends SpecificGeneratedFile {
|
||||
MakoGeneratedFile() {
|
||||
exists(Module m | m.getFile() = this |
|
||||
@@ -166,7 +166,7 @@ string from_mako_import(Module m) {
|
||||
)
|
||||
}
|
||||
|
||||
/** File generated by Google's protobuf tool. */
|
||||
/** A file generated by Google's protobuf tool. */
|
||||
class ProtobufGeneratedFile extends SpecificGeneratedFile {
|
||||
ProtobufGeneratedFile() {
|
||||
this.getAbsolutePath().regexpMatch(".*_pb2?.py") and
|
||||
|
||||
@@ -15,7 +15,7 @@ class UnitTestClass extends TestScope {
|
||||
|
||||
abstract class Test extends TestScope { }
|
||||
|
||||
/** Class of test function that uses the `unittest` framework */
|
||||
/** A test function that uses the `unittest` framework */
|
||||
class UnitTestFunction extends Test {
|
||||
UnitTestFunction() {
|
||||
this.getScope+() instanceof UnitTestClass and
|
||||
@@ -37,7 +37,7 @@ class NoseTestFunction extends Test {
|
||||
}
|
||||
}
|
||||
|
||||
/** Class of functions that are clearly tests, but don't belong to a specific framework */
|
||||
/** A function that is clearly a test, but doesn't belong to a specific framework */
|
||||
class UnknownTestFunction extends Test {
|
||||
UnknownTestFunction() {
|
||||
this.(Function).getName().matches("test%") and
|
||||
|
||||
@@ -639,3 +639,53 @@ module AiohttpWebModel {
|
||||
override DataFlow::Node getValueArg() { result = value }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides models for the web server part (`aiohttp.client`) of the `aiohttp` PyPI package.
|
||||
* See https://docs.aiohttp.org/en/stable/client.html
|
||||
*/
|
||||
private module AiohttpClientModel {
|
||||
/**
|
||||
* Provides models for the `aiohttp.ClientSession` class
|
||||
*
|
||||
* See https://docs.aiohttp.org/en/stable/client_reference.html#aiohttp.ClientSession.
|
||||
*/
|
||||
module ClientSession {
|
||||
/** Gets a reference to the `aiohttp.ClientSession` class. */
|
||||
private API::Node classRef() {
|
||||
result = API::moduleImport("aiohttp").getMember("ClientSession")
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `aiohttp.ClientSession`. */
|
||||
private API::Node instance() { result = classRef().getReturn() }
|
||||
|
||||
/** A method call on a ClientSession that sends off a request */
|
||||
private class OutgoingRequestCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode {
|
||||
string methodName;
|
||||
|
||||
OutgoingRequestCall() {
|
||||
methodName in [HTTP::httpVerbLower(), "request"] and
|
||||
this = instance().getMember(methodName).getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAUrlPart() {
|
||||
result = this.getArgByName("url")
|
||||
or
|
||||
not methodName = "request" and
|
||||
result = this.getArg(0)
|
||||
or
|
||||
methodName = "request" and
|
||||
result = this.getArg(1)
|
||||
}
|
||||
|
||||
override string getFramework() { result = "aiohttp.ClientSession" }
|
||||
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
// TODO: Look into disabling certificate validation
|
||||
none()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ private module Aiomysql {
|
||||
private import semmle.python.internal.Awaited
|
||||
|
||||
/**
|
||||
* A `ConectionPool` is created when the result of `aiomysql.create_pool()` is awaited.
|
||||
* Gets a `ConnectionPool` that is created when the result of `aiomysql.create_pool()` is awaited.
|
||||
* See https://aiomysql.readthedocs.io/en/stable/pool.html
|
||||
*/
|
||||
API::Node connectionPool() {
|
||||
@@ -23,7 +23,7 @@ private module Aiomysql {
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Connection` is created when
|
||||
* Gets a `Connection` that is created when
|
||||
* - the result of `aiomysql.connect()` is awaited.
|
||||
* - the result of calling `aquire` on a `ConnectionPool` is awaited.
|
||||
* See https://aiomysql.readthedocs.io/en/stable/connection.html#connection
|
||||
@@ -35,7 +35,7 @@ private module Aiomysql {
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Cursor` is created when
|
||||
* Gets a `Cursor` that is created when
|
||||
* - the result of calling `cursor` on a `ConnectionPool` is awaited.
|
||||
* - the result of calling `cursor` on a `Connection` is awaited.
|
||||
* See https://aiomysql.readthedocs.io/en/stable/cursors.html
|
||||
@@ -47,7 +47,7 @@ private module Aiomysql {
|
||||
}
|
||||
|
||||
/**
|
||||
* Calling `execute` on a `Cursor` constructs a query.
|
||||
* A query. Calling `execute` on a `Cursor` constructs a query.
|
||||
* See https://aiomysql.readthedocs.io/en/stable/cursors.html#Cursor.execute
|
||||
*/
|
||||
class CursorExecuteCall extends SqlConstruction::Range, DataFlow::CallCfgNode {
|
||||
@@ -73,7 +73,7 @@ private module Aiomysql {
|
||||
}
|
||||
|
||||
/**
|
||||
* Awaiting the result of calling `execute` executes the query.
|
||||
* An awaited query. Awaiting the result of calling `execute` executes the query.
|
||||
* See https://aiomysql.readthedocs.io/en/stable/cursors.html#Cursor.execute
|
||||
*/
|
||||
class AwaitedCursorExecuteCall extends SqlExecution::Range {
|
||||
@@ -85,7 +85,7 @@ private module Aiomysql {
|
||||
}
|
||||
|
||||
/**
|
||||
* An `Engine` is created when the result of calling `aiomysql.sa.create_engine` is awaited.
|
||||
* Gets an `Engine` that is created when the result of calling `aiomysql.sa.create_engine` is awaited.
|
||||
* See https://aiomysql.readthedocs.io/en/stable/sa.html#engine
|
||||
*/
|
||||
API::Node engine() {
|
||||
@@ -98,13 +98,13 @@ private module Aiomysql {
|
||||
}
|
||||
|
||||
/**
|
||||
* A `SAConnection` is created when the result of calling `aquire` on an `Engine` is awaited.
|
||||
* Gets an `SAConnection` that is created when the result of calling `aquire` on an `Engine` is awaited.
|
||||
* See https://aiomysql.readthedocs.io/en/stable/sa.html#connection
|
||||
*/
|
||||
API::Node saConnection() { result = engine().getMember("acquire").getReturn().getAwaited() }
|
||||
|
||||
/**
|
||||
* Calling `execute` on a `SAConnection` constructs a query.
|
||||
* A query. Calling `execute` on a `SAConnection` constructs a query.
|
||||
* See https://aiomysql.readthedocs.io/en/stable/sa.html#aiomysql.sa.SAConnection.execute
|
||||
*/
|
||||
class SAConnectionExecuteCall extends SqlConstruction::Range, DataFlow::CallCfgNode {
|
||||
@@ -132,7 +132,7 @@ private module Aiomysql {
|
||||
}
|
||||
|
||||
/**
|
||||
* Awaiting the result of calling `execute` executes the query.
|
||||
* An awaited query. Awaiting the result of calling `execute` executes the query.
|
||||
* See https://aiomysql.readthedocs.io/en/stable/sa.html#aiomysql.sa.SAConnection.execute
|
||||
*/
|
||||
class AwaitedSAConnectionExecuteCall extends SqlExecution::Range {
|
||||
|
||||
@@ -15,7 +15,7 @@ private module Aiopg {
|
||||
private import semmle.python.internal.Awaited
|
||||
|
||||
/**
|
||||
* A `ConectionPool` is created when the result of `aiopg.create_pool()` is awaited.
|
||||
* Gets a `ConnectionPool` that is created when the result of `aiopg.create_pool()` is awaited.
|
||||
* See https://aiopg.readthedocs.io/en/stable/core.html#pool
|
||||
*/
|
||||
API::Node connectionPool() {
|
||||
@@ -23,7 +23,7 @@ private module Aiopg {
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Connection` is created when
|
||||
* Gets a `Connection` that is created when
|
||||
* - the result of `aiopg.connect()` is awaited.
|
||||
* - the result of calling `aquire` on a `ConnectionPool` is awaited.
|
||||
* See https://aiopg.readthedocs.io/en/stable/core.html#connection
|
||||
@@ -35,7 +35,7 @@ private module Aiopg {
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Cursor` is created when
|
||||
* Gets a `Cursor` that is created when
|
||||
* - the result of calling `cursor` on a `ConnectionPool` is awaited.
|
||||
* - the result of calling `cursor` on a `Connection` is awaited.
|
||||
* See https://aiopg.readthedocs.io/en/stable/core.html#cursor
|
||||
@@ -47,7 +47,7 @@ private module Aiopg {
|
||||
}
|
||||
|
||||
/**
|
||||
* Calling `execute` on a `Cursor` constructs a query.
|
||||
* A query. Calling `execute` on a `Cursor` constructs a query.
|
||||
* See https://aiopg.readthedocs.io/en/stable/core.html#aiopg.Cursor.execute
|
||||
*/
|
||||
class CursorExecuteCall extends SqlConstruction::Range, DataFlow::CallCfgNode {
|
||||
@@ -73,7 +73,7 @@ private module Aiopg {
|
||||
}
|
||||
|
||||
/**
|
||||
* Awaiting the result of calling `execute` executes the query.
|
||||
* An awaited query. Awaiting the result of calling `execute` executes the query.
|
||||
* See https://aiopg.readthedocs.io/en/stable/core.html#aiopg.Cursor.execute
|
||||
*/
|
||||
class AwaitedCursorExecuteCall extends SqlExecution::Range {
|
||||
@@ -85,7 +85,7 @@ private module Aiopg {
|
||||
}
|
||||
|
||||
/**
|
||||
* An `Engine` is created when the result of calling `aiopg.sa.create_engine` is awaited.
|
||||
* Gets an `Engine` that is created when the result of calling `aiopg.sa.create_engine` is awaited.
|
||||
* See https://aiopg.readthedocs.io/en/stable/sa.html#engine
|
||||
*/
|
||||
API::Node engine() {
|
||||
@@ -94,13 +94,13 @@ private module Aiopg {
|
||||
}
|
||||
|
||||
/**
|
||||
* A `SAConnection` is created when the result of calling `aquire` on an `Engine` is awaited.
|
||||
* Gets an `SAConnection` that is created when the result of calling `aquire` on an `Engine` is awaited.
|
||||
* See https://aiopg.readthedocs.io/en/stable/sa.html#connection
|
||||
*/
|
||||
API::Node saConnection() { result = engine().getMember("acquire").getReturn().getAwaited() }
|
||||
|
||||
/**
|
||||
* Calling `execute` on a `SAConnection` constructs a query.
|
||||
* A query. Calling `execute` on a `SAConnection` constructs a query.
|
||||
* See https://aiopg.readthedocs.io/en/stable/sa.html#aiopg.sa.SAConnection.execute
|
||||
*/
|
||||
class SAConnectionExecuteCall extends SqlConstruction::Range, DataFlow::CallCfgNode {
|
||||
@@ -128,7 +128,7 @@ private module Aiopg {
|
||||
}
|
||||
|
||||
/**
|
||||
* Awaiting the result of calling `execute` executes the query.
|
||||
* An awaited query. Awaiting the result of calling `execute` executes the query.
|
||||
* See https://aiopg.readthedocs.io/en/stable/sa.html#aiopg.sa.SAConnection.execute
|
||||
*/
|
||||
class AwaitedSAConnectionExecuteCall extends SqlExecution::Range {
|
||||
|
||||
@@ -12,13 +12,13 @@ private import semmle.python.ApiGraphs
|
||||
private module Asyncpg {
|
||||
private import semmle.python.internal.Awaited
|
||||
|
||||
/** A `ConectionPool` is created when the result of `asyncpg.create_pool()` is awaited. */
|
||||
/** Gets a `ConnectionPool` that is created when the result of `asyncpg.create_pool()` is awaited. */
|
||||
API::Node connectionPool() {
|
||||
result = API::moduleImport("asyncpg").getMember("create_pool").getReturn().getAwaited()
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Connection` is created when
|
||||
* Gets a `Connection` that is created when
|
||||
* - the result of `asyncpg.connect()` is awaited.
|
||||
* - the result of calling `aquire` on a `ConnectionPool` is awaited.
|
||||
*/
|
||||
@@ -46,7 +46,7 @@ private module Asyncpg {
|
||||
}
|
||||
}
|
||||
|
||||
/** `Connection`s and `ConnectionPool`s provide some methods that access the file system. */
|
||||
/** A model of `Connection` and `ConnectionPool`, which provide some methods that access the file system. */
|
||||
class FileAccessOnConnection extends FileSystemAccess::Range, DataFlow::MethodCallNode {
|
||||
string methodName;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ private import semmle.python.frameworks.PEP249
|
||||
*/
|
||||
module ClickhouseDriver {
|
||||
/**
|
||||
* `clickhouse_driver` implements PEP249,
|
||||
* A model of `clickhouse-driver`, which implements PEP249,
|
||||
* providing ways to execute SQL statements against a database.
|
||||
*/
|
||||
class ClickHouseDriverPEP249 extends PEP249::PEP249ModuleApiNode {
|
||||
|
||||
@@ -526,8 +526,11 @@ module PrivateDjango {
|
||||
/** Gets a reference to the `django` module. */
|
||||
API::Node django() { result = API::moduleImport("django") }
|
||||
|
||||
/** DEPRECATED: Alias for `DjangoImpl` */
|
||||
deprecated module django = DjangoImpl;
|
||||
|
||||
/** Provides models for the `django` module. */
|
||||
module django {
|
||||
module DjangoImpl {
|
||||
// -------------------------------------------------------------------------
|
||||
// django.db
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -541,8 +544,11 @@ module PrivateDjango {
|
||||
DjangoDb() { this = API::moduleImport("django").getMember("db") }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for `DB` */
|
||||
deprecated module db = DB;
|
||||
|
||||
/** Provides models for the `django.db` module. */
|
||||
module db {
|
||||
module DB {
|
||||
/** Gets a reference to the `django.db.connection` object. */
|
||||
API::Node connection() { result = db().getMember("connection") }
|
||||
|
||||
@@ -557,8 +563,11 @@ module PrivateDjango {
|
||||
/** Gets a reference to the `django.db.models` module. */
|
||||
API::Node models() { result = db().getMember("models") }
|
||||
|
||||
/** DEPRECATED: Alias for `Models` */
|
||||
deprecated module models = Models;
|
||||
|
||||
/** Provides models for the `django.db.models` module. */
|
||||
module models {
|
||||
module Models {
|
||||
/**
|
||||
* Provides models for the `django.db.models.Model` class and subclasses.
|
||||
*
|
||||
@@ -770,10 +779,13 @@ module PrivateDjango {
|
||||
/** Gets a reference to the `django.db.models.expressions` module. */
|
||||
API::Node expressions() { result = models().getMember("expressions") }
|
||||
|
||||
/** DEPRECATED: Alias for `Expressions` */
|
||||
deprecated module expressions = Expressions;
|
||||
|
||||
/** Provides models for the `django.db.models.expressions` module. */
|
||||
module expressions {
|
||||
/** Provides models for the `django.db.models.expressions.RawSQL` class. */
|
||||
module RawSQL {
|
||||
module Expressions {
|
||||
/** Provides models for the `django.db.models.expressions.RawSql` class. */
|
||||
module RawSql {
|
||||
/**
|
||||
* Gets an reference to the `django.db.models.expressions.RawSQL` class.
|
||||
*/
|
||||
@@ -806,6 +818,9 @@ module PrivateDjango {
|
||||
instance(DataFlow::TypeTracker::end(), sql).flowsTo(result)
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for RawSql */
|
||||
deprecated module RawSQL = RawSql;
|
||||
}
|
||||
|
||||
/** This internal module provides data-flow modeling of Django ORM. */
|
||||
@@ -981,8 +996,8 @@ module PrivateDjango {
|
||||
DataFlow::Node sql;
|
||||
|
||||
ObjectsAnnotate() {
|
||||
this = django::db::models::querySetReturningMethod(_, "annotate").getACall() and
|
||||
django::db::models::expressions::RawSQL::instance(sql) in [
|
||||
this = DjangoImpl::DB::Models::querySetReturningMethod(_, "annotate").getACall() and
|
||||
DjangoImpl::DB::Models::Expressions::RawSql::instance(sql) in [
|
||||
this.getArg(_), this.getArgByName(_)
|
||||
]
|
||||
}
|
||||
@@ -999,8 +1014,8 @@ module PrivateDjango {
|
||||
DataFlow::Node sql;
|
||||
|
||||
ObjectsAlias() {
|
||||
this = django::db::models::querySetReturningMethod(_, "alias").getACall() and
|
||||
django::db::models::expressions::RawSQL::instance(sql) in [
|
||||
this = DjangoImpl::DB::Models::querySetReturningMethod(_, "alias").getACall() and
|
||||
DjangoImpl::DB::Models::Expressions::RawSql::instance(sql) in [
|
||||
this.getArg(_), this.getArgByName(_)
|
||||
]
|
||||
}
|
||||
@@ -1016,7 +1031,7 @@ module PrivateDjango {
|
||||
* - https://docs.djangoproject.com/en/3.1/ref/models/querysets/#raw
|
||||
*/
|
||||
private class ObjectsRaw extends SqlExecution::Range, DataFlow::CallCfgNode {
|
||||
ObjectsRaw() { this = django::db::models::querySetReturningMethod(_, "raw").getACall() }
|
||||
ObjectsRaw() { this = DjangoImpl::DB::Models::querySetReturningMethod(_, "raw").getACall() }
|
||||
|
||||
override DataFlow::Node getSql() { result = this.getArg(0) }
|
||||
}
|
||||
@@ -1027,7 +1042,9 @@ module PrivateDjango {
|
||||
* See https://docs.djangoproject.com/en/3.1/ref/models/querysets/#extra
|
||||
*/
|
||||
private class ObjectsExtra extends SqlExecution::Range, DataFlow::CallCfgNode {
|
||||
ObjectsExtra() { this = django::db::models::querySetReturningMethod(_, "extra").getACall() }
|
||||
ObjectsExtra() {
|
||||
this = DjangoImpl::DB::Models::querySetReturningMethod(_, "extra").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getSql() {
|
||||
result in [
|
||||
@@ -1042,8 +1059,11 @@ module PrivateDjango {
|
||||
/** Gets a reference to the `django.urls` module. */
|
||||
API::Node urls() { result = django().getMember("urls") }
|
||||
|
||||
/** DEPRECATED: Alias for `Urls` */
|
||||
deprecated module urls = Urls;
|
||||
|
||||
/** Provides models for the `django.urls` module */
|
||||
module urls {
|
||||
module Urls {
|
||||
/**
|
||||
* Gets a reference to the `django.urls.path` function.
|
||||
* See https://docs.djangoproject.com/en/3.0/ref/urls/#path
|
||||
@@ -1063,10 +1083,16 @@ module PrivateDjango {
|
||||
/** Gets a reference to the `django.conf` module. */
|
||||
API::Node conf() { result = django().getMember("conf") }
|
||||
|
||||
/** DEPRECATED: Alias for `Conf` */
|
||||
deprecated module conf = Conf;
|
||||
|
||||
/** Provides models for the `django.conf` module */
|
||||
module conf {
|
||||
module Conf {
|
||||
/** DEPRECATED: Alias for `ConfUrls` */
|
||||
deprecated module conf_urls = ConfUrls;
|
||||
|
||||
/** Provides models for the `django.conf.urls` module */
|
||||
module conf_urls {
|
||||
module ConfUrls {
|
||||
// -------------------------------------------------------------------------
|
||||
// django.conf.urls
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -1089,16 +1115,22 @@ module PrivateDjango {
|
||||
/** Gets a reference to the `django.http` module. */
|
||||
API::Node http() { result = django().getMember("http") }
|
||||
|
||||
/** DEPRECATED: Alias for `Http` */
|
||||
deprecated module http = Http;
|
||||
|
||||
/** Provides models for the `django.http` module */
|
||||
module http {
|
||||
module Http {
|
||||
// ---------------------------------------------------------------------------
|
||||
// django.http.request
|
||||
// ---------------------------------------------------------------------------
|
||||
/** Gets a reference to the `django.http.request` module. */
|
||||
API::Node request() { result = http().getMember("request") }
|
||||
|
||||
/** DEPRECATED: Alias for `Request` */
|
||||
deprecated module request = Request;
|
||||
|
||||
/** Provides models for the `django.http.request` module. */
|
||||
module request {
|
||||
module Request {
|
||||
/**
|
||||
* Provides models for the `django.http.request.HttpRequest` class
|
||||
*
|
||||
@@ -1179,7 +1211,7 @@ module PrivateDjango {
|
||||
// special handling of the `build_absolute_uri` method, see
|
||||
// https://docs.djangoproject.com/en/3.0/ref/request-response/#django.http.HttpRequest.build_absolute_uri
|
||||
exists(DataFlow::AttrRead attr, DataFlow::CallCfgNode call, DataFlow::Node instance |
|
||||
instance = django::http::request::HttpRequest::instance() and
|
||||
instance = DjangoImpl::Http::Request::HttpRequest::instance() and
|
||||
attr.getObject() = instance
|
||||
|
|
||||
attr.getAttributeName() = "build_absolute_uri" and
|
||||
@@ -1256,8 +1288,11 @@ module PrivateDjango {
|
||||
/** Gets a reference to the `django.http.response` module. */
|
||||
API::Node response() { result = http().getMember("response") }
|
||||
|
||||
/** DEPRECATED: Alias for `Response` */
|
||||
deprecated module response = Response;
|
||||
|
||||
/** Provides models for the `django.http.response` module */
|
||||
module response {
|
||||
module Response {
|
||||
/**
|
||||
* Provides models for the `django.http.response.HttpResponse` class
|
||||
*
|
||||
@@ -1991,17 +2026,17 @@ module PrivateDjango {
|
||||
|
||||
/** Gets a reference to the `django.http.response.HttpResponse.write` function. */
|
||||
private DataFlow::TypeTrackingNode write(
|
||||
django::http::response::HttpResponse::InstanceSource instance, DataFlow::TypeTracker t
|
||||
DjangoImpl::Http::Response::HttpResponse::InstanceSource instance, DataFlow::TypeTracker t
|
||||
) {
|
||||
t.startInAttr("write") and
|
||||
instance = django::http::response::HttpResponse::instance() and
|
||||
instance = DjangoImpl::Http::Response::HttpResponse::instance() and
|
||||
result = instance
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = write(instance, t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to the `django.http.response.HttpResponse.write` function. */
|
||||
DataFlow::Node write(django::http::response::HttpResponse::InstanceSource instance) {
|
||||
DataFlow::Node write(DjangoImpl::Http::Response::HttpResponse::InstanceSource instance) {
|
||||
write(instance, DataFlow::TypeTracker::end()).flowsTo(result)
|
||||
}
|
||||
|
||||
@@ -2011,7 +2046,7 @@ module PrivateDjango {
|
||||
* See https://docs.djangoproject.com/en/3.1/ref/request-response/#django.http.HttpResponse.write
|
||||
*/
|
||||
class HttpResponseWriteCall extends HTTP::Server::HttpResponse::Range, DataFlow::CallCfgNode {
|
||||
django::http::response::HttpResponse::InstanceSource instance;
|
||||
DjangoImpl::Http::Response::HttpResponse::InstanceSource instance;
|
||||
|
||||
HttpResponseWriteCall() { this.getFunction() = write(instance) }
|
||||
|
||||
@@ -2032,7 +2067,7 @@ module PrivateDjango {
|
||||
class DjangoResponseSetCookieCall extends HTTP::Server::CookieWrite::Range,
|
||||
DataFlow::MethodCallNode {
|
||||
DjangoResponseSetCookieCall() {
|
||||
this.calls(django::http::response::HttpResponse::instance(), "set_cookie")
|
||||
this.calls(DjangoImpl::Http::Response::HttpResponse::instance(), "set_cookie")
|
||||
}
|
||||
|
||||
override DataFlow::Node getHeaderArg() { none() }
|
||||
@@ -2052,7 +2087,7 @@ module PrivateDjango {
|
||||
class DjangoResponseDeleteCookieCall extends HTTP::Server::CookieWrite::Range,
|
||||
DataFlow::MethodCallNode {
|
||||
DjangoResponseDeleteCookieCall() {
|
||||
this.calls(django::http::response::HttpResponse::instance(), "delete_cookie")
|
||||
this.calls(DjangoImpl::Http::Response::HttpResponse::instance(), "delete_cookie")
|
||||
}
|
||||
|
||||
override DataFlow::Node getHeaderArg() { none() }
|
||||
@@ -2079,7 +2114,7 @@ module PrivateDjango {
|
||||
this.asCfgNode() = subscript
|
||||
|
|
||||
cookieLookup.getAttributeName() = "cookies" and
|
||||
cookieLookup.getObject() = django::http::response::HttpResponse::instance() and
|
||||
cookieLookup.getObject() = DjangoImpl::Http::Response::HttpResponse::instance() and
|
||||
exists(DataFlow::Node subscriptObj |
|
||||
subscriptObj.asCfgNode() = subscript.getObject()
|
||||
|
|
||||
@@ -2105,8 +2140,11 @@ module PrivateDjango {
|
||||
/** Gets a reference to the `django.shortcuts` module. */
|
||||
API::Node shortcuts() { result = django().getMember("shortcuts") }
|
||||
|
||||
/** DEPRECATED: Alias for `Shortcuts` */
|
||||
deprecated module shortcuts = Shortcuts;
|
||||
|
||||
/** Provides models for the `django.shortcuts` module */
|
||||
module shortcuts {
|
||||
module Shortcuts {
|
||||
/**
|
||||
* Gets a reference to the `django.shortcuts.redirect` function
|
||||
*
|
||||
@@ -2184,7 +2222,8 @@ module PrivateDjango {
|
||||
// routing modeling
|
||||
// ---------------------------------------------------------------------------
|
||||
/**
|
||||
* In order to recognize a class as being a django view class, based on the `as_view`
|
||||
* A class that may be a django view class. In order to recognize a class as being a django view class,
|
||||
* based on the `as_view`
|
||||
* call, we need to be able to track such calls on _any_ class. This is provided by
|
||||
* the member predicates of this QL class.
|
||||
*
|
||||
@@ -2195,7 +2234,7 @@ module PrivateDjango {
|
||||
/** Gets a reference to this class. */
|
||||
private DataFlow::TypeTrackingNode getARef(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result.asExpr().(ClassExpr) = this.getParent()
|
||||
result.asExpr() = this.getParent()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = this.getARef(t2).track(t2, t))
|
||||
}
|
||||
@@ -2295,7 +2334,7 @@ module PrivateDjango {
|
||||
/** Provides a class for modeling new django route handlers. */
|
||||
module DjangoRouteHandler {
|
||||
/**
|
||||
* Extend this class to model new APIs. If you want to refine existing API models,
|
||||
* A django route handler. Extend this class to model new APIs. If you want to refine existing API models,
|
||||
* extend `DjangoRouteHandler` instead.
|
||||
*/
|
||||
abstract class Range extends Function { }
|
||||
@@ -2381,7 +2420,7 @@ module PrivateDjango {
|
||||
* See https://docs.djangoproject.com/en/3.0/ref/urls/#path
|
||||
*/
|
||||
private class DjangoUrlsPathCall extends DjangoRouteSetup, DataFlow::CallCfgNode {
|
||||
DjangoUrlsPathCall() { this = django::urls::path().getACall() }
|
||||
DjangoUrlsPathCall() { this = DjangoImpl::Urls::path().getACall() }
|
||||
|
||||
override DataFlow::Node getUrlPatternArg() {
|
||||
result in [this.getArg(0), this.getArgByName("route")]
|
||||
@@ -2464,7 +2503,7 @@ module PrivateDjango {
|
||||
*/
|
||||
private class DjangoUrlsRePathCall extends DjangoRegexRouteSetup, DataFlow::CallCfgNode {
|
||||
DjangoUrlsRePathCall() {
|
||||
this = django::urls::re_path().getACall() and
|
||||
this = DjangoImpl::Urls::re_path().getACall() and
|
||||
// `django.conf.urls.url` (which we support directly with
|
||||
// `DjangoConfUrlsUrlCall`), is implemented in Django 2+ as backward compatibility
|
||||
// using `django.urls.re_path`. See
|
||||
@@ -2494,7 +2533,7 @@ module PrivateDjango {
|
||||
* See https://docs.djangoproject.com/en/1.11/ref/urls/#django.conf.urls.url
|
||||
*/
|
||||
private class DjangoConfUrlsUrlCall extends DjangoRegexRouteSetup, DataFlow::CallCfgNode {
|
||||
DjangoConfUrlsUrlCall() { this = django::conf::conf_urls::url().getACall() }
|
||||
DjangoConfUrlsUrlCall() { this = DjangoImpl::Conf::ConfUrls::url().getACall() }
|
||||
|
||||
override DataFlow::Node getUrlPatternArg() {
|
||||
result in [this.getArg(0), this.getArgByName("regex")]
|
||||
@@ -2507,7 +2546,7 @@ module PrivateDjango {
|
||||
// HttpRequest taint modeling
|
||||
// ---------------------------------------------------------------------------
|
||||
/** A parameter that will receive the django `HttpRequest` instance when a request handler is invoked. */
|
||||
private class DjangoRequestHandlerRequestParam extends django::http::request::HttpRequest::InstanceSource,
|
||||
private class DjangoRequestHandlerRequestParam extends DjangoImpl::Http::Request::HttpRequest::InstanceSource,
|
||||
RemoteFlowSource::Range, DataFlow::ParameterNode {
|
||||
DjangoRequestHandlerRequestParam() {
|
||||
this.getParameter() = any(DjangoRouteSetup setup).getARequestHandler().getRequestParam()
|
||||
@@ -2524,7 +2563,7 @@ module PrivateDjango {
|
||||
*
|
||||
* See https://docs.djangoproject.com/en/3.1/topics/class-based-views/generic-display/#dynamic-filtering
|
||||
*/
|
||||
private class DjangoViewClassRequestAttributeRead extends django::http::request::HttpRequest::InstanceSource,
|
||||
private class DjangoViewClassRequestAttributeRead extends DjangoImpl::Http::Request::HttpRequest::InstanceSource,
|
||||
RemoteFlowSource::Range, DataFlow::Node {
|
||||
DjangoViewClassRequestAttributeRead() {
|
||||
exists(DataFlow::AttrRead read | this = read |
|
||||
@@ -2571,7 +2610,7 @@ module PrivateDjango {
|
||||
*/
|
||||
private class DjangoShortcutsRedirectCall extends HTTP::Server::HttpRedirectResponse::Range,
|
||||
DataFlow::CallCfgNode {
|
||||
DjangoShortcutsRedirectCall() { this = django::shortcuts::redirect().getACall() }
|
||||
DjangoShortcutsRedirectCall() { this = DjangoImpl::Shortcuts::redirect().getACall() }
|
||||
|
||||
/**
|
||||
* Gets the data-flow node that specifies the location of this HTTP redirect response.
|
||||
|
||||
@@ -24,7 +24,7 @@ private module FabricV1 {
|
||||
API::Node fabric() { result = API::moduleImport("fabric") }
|
||||
|
||||
/** Provides models for the `fabric` module. */
|
||||
module fabric {
|
||||
module Fabric {
|
||||
// -------------------------------------------------------------------------
|
||||
// fabric.api
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -32,7 +32,7 @@ private module FabricV1 {
|
||||
API::Node api() { result = fabric().getMember("api") }
|
||||
|
||||
/** Provides models for the `fabric.api` module */
|
||||
module api {
|
||||
module Api {
|
||||
/**
|
||||
* A call to either
|
||||
* - `fabric.api.local`
|
||||
@@ -66,7 +66,7 @@ private module FabricV2 {
|
||||
API::Node fabric() { result = API::moduleImport("fabric") }
|
||||
|
||||
/** Provides models for the `fabric` module. */
|
||||
module fabric {
|
||||
module Fabric {
|
||||
// -------------------------------------------------------------------------
|
||||
// fabric.connection
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -74,13 +74,13 @@ private module FabricV2 {
|
||||
API::Node connection() { result = fabric().getMember("connection") }
|
||||
|
||||
/** Provides models for the `fabric.connection` module */
|
||||
module connection {
|
||||
module Connection {
|
||||
/**
|
||||
* Provides models for the `fabric.connection.Connection` class
|
||||
*
|
||||
* See https://docs.fabfile.org/en/2.5/api/connection.html#fabric.connection.Connection.
|
||||
*/
|
||||
module Connection {
|
||||
module ConnectionClass {
|
||||
/** Gets a reference to the `fabric.connection.Connection` class. */
|
||||
API::Node classRef() {
|
||||
result = fabric().getMember("Connection")
|
||||
@@ -155,7 +155,7 @@ private module FabricV2 {
|
||||
private class FabricConnectionRunSudoLocalCall extends SystemCommandExecution::Range,
|
||||
DataFlow::CallCfgNode {
|
||||
FabricConnectionRunSudoLocalCall() {
|
||||
this.getFunction() = fabric::connection::Connection::instanceRunMethods()
|
||||
this.getFunction() = Fabric::Connection::ConnectionClass::instanceRunMethods()
|
||||
}
|
||||
|
||||
override DataFlow::Node getCommand() {
|
||||
@@ -170,16 +170,16 @@ private module FabricV2 {
|
||||
API::Node tasks() { result = fabric().getMember("tasks") }
|
||||
|
||||
/** Provides models for the `fabric.tasks` module */
|
||||
module tasks {
|
||||
module Tasks {
|
||||
/** Gets a reference to the `fabric.tasks.task` decorator. */
|
||||
API::Node task() { result in [tasks().getMember("task"), fabric().getMember("task")] }
|
||||
}
|
||||
|
||||
class FabricTaskFirstParamConnectionInstance extends fabric::connection::Connection::InstanceSource,
|
||||
class FabricTaskFirstParamConnectionInstance extends Fabric::Connection::ConnectionClass::InstanceSource,
|
||||
DataFlow::ParameterNode {
|
||||
FabricTaskFirstParamConnectionInstance() {
|
||||
exists(Function func |
|
||||
func.getADecorator() = fabric::tasks::task().getAUse().asExpr() and
|
||||
func.getADecorator() = Fabric::Tasks::task().getAUse().asExpr() and
|
||||
this.getParameter() = func.getArg(0)
|
||||
)
|
||||
}
|
||||
@@ -192,7 +192,7 @@ private module FabricV2 {
|
||||
API::Node group() { result = fabric().getMember("group") }
|
||||
|
||||
/** Provides models for the `fabric.group` module */
|
||||
module group {
|
||||
module Group {
|
||||
/**
|
||||
* Provides models for the `fabric.group.Group` class and its subclasses.
|
||||
*
|
||||
@@ -204,7 +204,7 @@ private module FabricV2 {
|
||||
* - https://docs.fabfile.org/en/2.5/api/group.html#fabric.group.SerialGroup
|
||||
* - https://docs.fabfile.org/en/2.5/api/group.html#fabric.group.ThreadingGroup
|
||||
*/
|
||||
module Group {
|
||||
module GroupClass {
|
||||
/**
|
||||
* A source of instances of a subclass of `fabric.group, extend this class to model new instances.Group`
|
||||
*
|
||||
@@ -236,7 +236,9 @@ private module FabricV2 {
|
||||
* See https://docs.fabfile.org/en/2.5/api/group.html#fabric.group.Group.run
|
||||
*/
|
||||
private class FabricGroupRunCall extends SystemCommandExecution::Range, DataFlow::CallCfgNode {
|
||||
FabricGroupRunCall() { this = fabric::group::Group::subclassInstanceRunMethod().getACall() }
|
||||
FabricGroupRunCall() {
|
||||
this = Fabric::Group::GroupClass::subclassInstanceRunMethod().getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getCommand() {
|
||||
result = [this.getArg(0), this.getArgByName("command")]
|
||||
@@ -249,7 +251,7 @@ private module FabricV2 {
|
||||
* See https://docs.fabfile.org/en/2.5/api/group.html#fabric.group.SerialGroup.
|
||||
*/
|
||||
module SerialGroup {
|
||||
private class ClassInstantiation extends Group::ModeledSubclass {
|
||||
private class ClassInstantiation extends GroupClass::ModeledSubclass {
|
||||
ClassInstantiation() {
|
||||
this = group().getMember("SerialGroup")
|
||||
or
|
||||
@@ -264,7 +266,7 @@ private module FabricV2 {
|
||||
* See https://docs.fabfile.org/en/2.5/api/group.html#fabric.group.ThreadingGroup.
|
||||
*/
|
||||
module ThreadingGroup {
|
||||
private class ClassInstantiation extends Group::ModeledSubclass {
|
||||
private class ClassInstantiation extends GroupClass::ModeledSubclass {
|
||||
ClassInstantiation() {
|
||||
this = group().getMember("ThreadingGroup")
|
||||
or
|
||||
|
||||
@@ -30,13 +30,16 @@ private module FastApi {
|
||||
*
|
||||
* See https://fastapi.tiangolo.com/tutorial/bigger-applications/.
|
||||
*/
|
||||
module APIRouter {
|
||||
/** Gets a reference to an instance of `fastapi.APIRouter`. */
|
||||
module ApiRouter {
|
||||
/** Gets a reference to an instance of `fastapi.ApiRouter`. */
|
||||
API::Node instance() {
|
||||
result = API::moduleImport("fastapi").getMember("APIRouter").getASubclass*().getReturn()
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for ApiRouter */
|
||||
deprecated module APIRouter = ApiRouter;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// routing modeling
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -54,7 +57,7 @@ private module FastApi {
|
||||
|
|
||||
this = App::instance().getMember(routeAddingMethod).getACall()
|
||||
or
|
||||
this = APIRouter::instance().getMember(routeAddingMethod).getACall()
|
||||
this = ApiRouter::instance().getMember(routeAddingMethod).getACall()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -193,7 +193,7 @@ module Flask {
|
||||
|
||||
FlaskViewClass() {
|
||||
this.getABase() = Views::View::subclassRef().getAUse().asExpr() and
|
||||
api_node.getAnImmediateUse().asExpr().(ClassExpr) = this.getParent()
|
||||
api_node.getAnImmediateUse().asExpr() = this.getParent()
|
||||
}
|
||||
|
||||
/** Gets a function that could handle incoming requests, if any. */
|
||||
@@ -218,7 +218,7 @@ module Flask {
|
||||
class FlaskMethodViewClass extends FlaskViewClass {
|
||||
FlaskMethodViewClass() {
|
||||
this.getABase() = Views::MethodView::subclassRef().getAUse().asExpr() and
|
||||
api_node.getAnImmediateUse().asExpr().(ClassExpr) = this.getParent()
|
||||
api_node.getAnImmediateUse().asExpr() = this.getParent()
|
||||
}
|
||||
|
||||
override Function getARequestHandler() {
|
||||
@@ -299,7 +299,7 @@ module Flask {
|
||||
override Function getARequestHandler() {
|
||||
exists(DataFlow::LocalSourceNode func_src |
|
||||
func_src.flowsTo(this.getViewArg()) and
|
||||
func_src.asExpr().(CallableExpr) = result.getDefinition()
|
||||
func_src.asExpr() = result.getDefinition()
|
||||
)
|
||||
or
|
||||
exists(FlaskViewClass vc |
|
||||
|
||||
88
python/ql/lib/semmle/python/frameworks/Httpx.qll
Normal file
88
python/ql/lib/semmle/python/frameworks/Httpx.qll
Normal file
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `httpx` PyPI package.
|
||||
*
|
||||
* See
|
||||
* - https://pypi.org/project/httpx/
|
||||
* - https://www.python-httpx.org/
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
/**
|
||||
* Provides models for the `httpx` PyPI package.
|
||||
*
|
||||
* See
|
||||
* - https://pypi.org/project/httpx/
|
||||
* - https://www.python-httpx.org/
|
||||
*/
|
||||
private module HttpxModel {
|
||||
private class RequestCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode {
|
||||
string methodName;
|
||||
|
||||
RequestCall() {
|
||||
methodName in [HTTP::httpVerbLower(), "request", "stream"] and
|
||||
this = API::moduleImport("httpx").getMember(methodName).getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAUrlPart() {
|
||||
result = this.getArgByName("url")
|
||||
or
|
||||
if methodName in ["request", "stream"]
|
||||
then result = this.getArg(1)
|
||||
else result = this.getArg(0)
|
||||
}
|
||||
|
||||
override string getFramework() { result = "httpx" }
|
||||
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
// TODO: Look into disabling certificate validation
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides models for the `httpx.[Async]Client` class
|
||||
*
|
||||
* See https://www.python-httpx.org/async/
|
||||
*/
|
||||
module Client {
|
||||
/** Get a reference to the `httpx.Client` or `httpx.AsyncClient` class. */
|
||||
private API::Node classRef() {
|
||||
result = API::moduleImport("httpx").getMember(["Client", "AsyncClient"])
|
||||
}
|
||||
|
||||
/** Get a reference to an `httpx.Client` or `httpx.AsyncClient` instance. */
|
||||
private API::Node instance() { result = classRef().getReturn() }
|
||||
|
||||
/** A method call on a Client that sends off a request */
|
||||
private class OutgoingRequestCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode {
|
||||
string methodName;
|
||||
|
||||
OutgoingRequestCall() {
|
||||
methodName in [HTTP::httpVerbLower(), "request", "stream"] and
|
||||
this = instance().getMember(methodName).getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAUrlPart() {
|
||||
result = this.getArgByName("url")
|
||||
or
|
||||
if methodName in ["request", "stream"]
|
||||
then result = this.getArg(1)
|
||||
else result = this.getArg(0)
|
||||
}
|
||||
|
||||
override string getFramework() { result = "httpx.[Async]Client" }
|
||||
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
// TODO: Look into disabling certificate validation
|
||||
none()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,14 +20,11 @@ private module Invoke {
|
||||
API::Node invoke() { result = API::moduleImport("invoke") }
|
||||
|
||||
/** Provides models for the `invoke` module. */
|
||||
module invoke {
|
||||
/** Gets a reference to the `invoke.context` module. */
|
||||
API::Node context() { result = invoke().getMember("context") }
|
||||
|
||||
module InvokeModule {
|
||||
/** Provides models for the `invoke.context` module */
|
||||
module context {
|
||||
module Context {
|
||||
/** Provides models for the `invoke.context.Context` class */
|
||||
module Context {
|
||||
module ContextClass {
|
||||
/** Gets a reference to the `invoke.context.Context` class. */
|
||||
API::Node classRef() {
|
||||
result = API::moduleImport("invoke").getMember("context").getMember("Context")
|
||||
@@ -39,7 +36,7 @@ private module Invoke {
|
||||
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
(
|
||||
result = invoke::context::Context::classRef().getACall()
|
||||
result = InvokeModule::Context::ContextClass::classRef().getACall()
|
||||
or
|
||||
exists(Function func |
|
||||
func.getADecorator() = invoke().getMember("task").getAUse().asExpr() and
|
||||
@@ -56,7 +53,7 @@ private module Invoke {
|
||||
/** Gets a reference to the `run` or `sudo` methods on a `invoke.context.Context` instance. */
|
||||
private DataFlow::TypeTrackingNode instanceRunMethods(DataFlow::TypeTracker t) {
|
||||
t.startInAttr(["run", "sudo"]) and
|
||||
result = invoke::context::Context::instance()
|
||||
result = InvokeModule::Context::ContextClass::instance()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = instanceRunMethods(t2).track(t2, t))
|
||||
}
|
||||
@@ -77,7 +74,7 @@ private module Invoke {
|
||||
private class InvokeRunCommandCall extends SystemCommandExecution::Range, DataFlow::CallCfgNode {
|
||||
InvokeRunCommandCall() {
|
||||
this = invoke().getMember(["run", "sudo"]).getACall() or
|
||||
this.getFunction() = invoke::context::Context::instanceRunMethods()
|
||||
this.getFunction() = InvokeModule::Context::ContextClass::instanceRunMethods()
|
||||
}
|
||||
|
||||
override DataFlow::Node getCommand() {
|
||||
|
||||
42
python/ql/lib/semmle/python/frameworks/Libtaxii.qll
Normal file
42
python/ql/lib/semmle/python/frameworks/Libtaxii.qll
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `libtaxii` PyPI package.
|
||||
*
|
||||
* See
|
||||
* - https://pypi.org/project/libtaxii/
|
||||
* - https://github.com/TAXIIProject/libtaxii
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
/**
|
||||
* Provides models for the `libtaxii` PyPI package.
|
||||
*
|
||||
* See
|
||||
* - https://pypi.org/project/libtaxii/
|
||||
* - https://github.com/TAXIIProject/libtaxii
|
||||
*/
|
||||
private module Libtaxii {
|
||||
/**
|
||||
* A call to `libtaxii.common.parse`.
|
||||
* When the `allow_url` parameter value is set to `True`, there is an SSRF vulnerability..
|
||||
*/
|
||||
private class ParseCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode {
|
||||
ParseCall() {
|
||||
this = API::moduleImport("libtaxii").getMember("common").getMember("parse").getACall() and
|
||||
this.getArgByName("allow_url").getALocalSource().asExpr() = any(True t)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAUrlPart() { result in [this.getArg(0), this.getArgByName("s")] }
|
||||
|
||||
override string getFramework() { result = "libtaxii.common.parse" }
|
||||
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
// TODO: Look into disabling certificate validation
|
||||
none()
|
||||
}
|
||||
}
|
||||
}
|
||||
45
python/ql/lib/semmle/python/frameworks/Libxml2.qll
Normal file
45
python/ql/lib/semmle/python/frameworks/Libxml2.qll
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `libxml2` PyPI package.
|
||||
*
|
||||
* See
|
||||
* - https://pypi.org/project/libxml2-python3/
|
||||
* - http://xmlsoft.org/python.html
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `libxml2` PyPI package
|
||||
*
|
||||
* See
|
||||
* - https://pypi.org/project/libxml2-python3/
|
||||
* - http://xmlsoft.org/python.html
|
||||
*/
|
||||
private module Libxml2 {
|
||||
/**
|
||||
* A call to the `xpathEval` method of a parsed document.
|
||||
*
|
||||
* import libxml2
|
||||
* tree = libxml2.parseFile("file.xml")
|
||||
* r = tree.xpathEval('`sink`')
|
||||
*
|
||||
* See http://xmlsoft.org/python.html
|
||||
*/
|
||||
class XpathEvalCall extends XML::XPathExecution::Range, DataFlow::CallCfgNode {
|
||||
XpathEvalCall() {
|
||||
this =
|
||||
API::moduleImport("libxml2")
|
||||
.getMember("parseFile")
|
||||
.getReturn()
|
||||
.getMember("xpathEval")
|
||||
.getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getXPath() { result = this.getArg(0) }
|
||||
|
||||
override string getName() { result = "libxml2" }
|
||||
}
|
||||
}
|
||||
88
python/ql/lib/semmle/python/frameworks/Lxml.qll
Normal file
88
python/ql/lib/semmle/python/frameworks/Lxml.qll
Normal file
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `lxml` PyPI package.
|
||||
*
|
||||
* See
|
||||
* - https://pypi.org/project/lxml/
|
||||
* - https://lxml.de/tutorial.html
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `lxml` PyPI package
|
||||
*
|
||||
* See
|
||||
* - https://pypi.org/project/lxml/
|
||||
* - https://lxml.de/tutorial.html
|
||||
*/
|
||||
private module Lxml {
|
||||
/**
|
||||
* A class constructor compiling an XPath expression.
|
||||
*
|
||||
* from lxml import etree
|
||||
* find_text = etree.XPath("`sink`")
|
||||
* find_text = etree.ETXPath("`sink`")
|
||||
*
|
||||
* See
|
||||
* - https://lxml.de/apidoc/lxml.etree.html#lxml.etree.XPath
|
||||
* - https://lxml.de/apidoc/lxml.etree.html#lxml.etree.ETXPath
|
||||
*/
|
||||
private class XPathClassCall extends XML::XPathConstruction::Range, DataFlow::CallCfgNode {
|
||||
XPathClassCall() {
|
||||
this = API::moduleImport("lxml").getMember("etree").getMember(["XPath", "ETXPath"]).getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getXPath() { result in [this.getArg(0), this.getArgByName("path")] }
|
||||
|
||||
override string getName() { result = "lxml.etree" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the `xpath` method of a parsed document.
|
||||
*
|
||||
* from lxml import etree
|
||||
* root = etree.fromstring(file(XML_DB).read(), XMLParser())
|
||||
* find_text = root.xpath("`sink`")
|
||||
*
|
||||
* See https://lxml.de/apidoc/lxml.etree.html#lxml.etree._ElementTree.xpath
|
||||
* as well as
|
||||
* - https://lxml.de/apidoc/lxml.etree.html#lxml.etree.parse
|
||||
* - https://lxml.de/apidoc/lxml.etree.html#lxml.etree.fromstring
|
||||
* - https://lxml.de/apidoc/lxml.etree.html#lxml.etree.fromstringlist
|
||||
* - https://lxml.de/apidoc/lxml.etree.html#lxml.etree.HTML
|
||||
* - https://lxml.de/apidoc/lxml.etree.html#lxml.etree.XML
|
||||
*/
|
||||
class XPathCall extends XML::XPathExecution::Range, DataFlow::CallCfgNode {
|
||||
XPathCall() {
|
||||
this =
|
||||
API::moduleImport("lxml")
|
||||
.getMember("etree")
|
||||
.getMember(["parse", "fromstring", "fromstringlist", "HTML", "XML"])
|
||||
.getReturn()
|
||||
.getMember("xpath")
|
||||
.getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getXPath() { result in [this.getArg(0), this.getArgByName("_path")] }
|
||||
|
||||
override string getName() { result = "lxml.etree" }
|
||||
}
|
||||
|
||||
class XPathEvaluatorCall extends XML::XPathExecution::Range, DataFlow::CallCfgNode {
|
||||
XPathEvaluatorCall() {
|
||||
this =
|
||||
API::moduleImport("lxml")
|
||||
.getMember("etree")
|
||||
.getMember("XPathEvaluator")
|
||||
.getReturn()
|
||||
.getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getXPath() { result = this.getArg(0) }
|
||||
|
||||
override string getName() { result = "lxml.etree" }
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,10 @@ private module MySQLdb {
|
||||
// ---------------------------------------------------------------------------
|
||||
// MySQLdb
|
||||
// ---------------------------------------------------------------------------
|
||||
/** MySQLdb implements PEP 249, providing ways to execute SQL statements against a database. */
|
||||
/**
|
||||
* A model for MySQLdb as a module that implements PEP 249, providing ways to execute SQL statements
|
||||
* against a database.
|
||||
*/
|
||||
class MySQLdb extends PEP249::PEP249ModuleApiNode {
|
||||
MySQLdb() { this = API::moduleImport("MySQLdb") }
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ private module Mysql {
|
||||
// mysql
|
||||
// ---------------------------------------------------------------------------
|
||||
/** Provides models for the `mysql` module. */
|
||||
module mysql {
|
||||
module MysqlMod {
|
||||
/**
|
||||
* The mysql.connector module
|
||||
* See https://dev.mysql.com/doc/connector-python/en/connector-python-example-connecting.html
|
||||
|
||||
@@ -22,7 +22,10 @@ private module Psycopg2 {
|
||||
// ---------------------------------------------------------------------------
|
||||
// Psycopg
|
||||
// ---------------------------------------------------------------------------
|
||||
/** psycopg2 implements PEP 249, providing ways to execute SQL statements against a database. */
|
||||
/**
|
||||
* A model of psycopg2 as a module that implements PEP 249, providing ways to execute SQL statements
|
||||
* against a database.
|
||||
*/
|
||||
class Psycopg2 extends PEP249::PEP249ModuleApiNode {
|
||||
Psycopg2() { this = API::moduleImport("psycopg2") }
|
||||
}
|
||||
|
||||
@@ -14,8 +14,11 @@ private import semmle.python.frameworks.PEP249
|
||||
* Provides models for the `PyMySQL` PyPI package.
|
||||
* See https://pypi.org/project/PyMySQL/
|
||||
*/
|
||||
private module PyMySQL {
|
||||
/** PyMySQL implements PEP 249, providing ways to execute SQL statements against a database. */
|
||||
private module PyMySql {
|
||||
/**
|
||||
* A model of PyMySQL as a module that implements PEP 249, providing ways to execute SQL statements
|
||||
* against a database.
|
||||
*/
|
||||
class PyMySQLPEP249 extends PEP249::PEP249ModuleApiNode {
|
||||
PyMySQLPEP249() { this = API::moduleImport("pymysql") }
|
||||
}
|
||||
|
||||
59
python/ql/lib/semmle/python/frameworks/Pycurl.qll
Normal file
59
python/ql/lib/semmle/python/frameworks/Pycurl.qll
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `pycurl` PyPI package.
|
||||
*
|
||||
* See
|
||||
* - https://pypi.org/project/pycurl/
|
||||
* - https://pycurl.io/docs/latest/
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
/**
|
||||
* Provides models for the `pycurl` PyPI package.
|
||||
*
|
||||
* See
|
||||
* - https://pypi.org/project/pycurl/
|
||||
* - https://pycurl.io/docs/latest/
|
||||
*/
|
||||
private module Pycurl {
|
||||
/**
|
||||
* Provides models for the `pycurl.Curl` class
|
||||
*
|
||||
* See https://pycurl.io/docs/latest/curl.html.
|
||||
*/
|
||||
module Curl {
|
||||
/** Gets a reference to the `pycurl.Curl` class. */
|
||||
private API::Node classRef() { result = API::moduleImport("pycurl").getMember("Curl") }
|
||||
|
||||
/** Gets a reference to an instance of `pycurl.Curl`. */
|
||||
private API::Node instance() { result = classRef().getReturn() }
|
||||
|
||||
/**
|
||||
* When the first parameter value of the `setopt` function is set to `pycurl.URL`,
|
||||
* the second parameter value is the request resource link.
|
||||
*
|
||||
* See http://pycurl.io/docs/latest/curlobject.html#pycurl.Curl.setopt.
|
||||
*/
|
||||
private class OutgoingRequestCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode {
|
||||
OutgoingRequestCall() {
|
||||
this = instance().getMember("setopt").getACall() and
|
||||
this.getArg(0).asCfgNode().(AttrNode).getName() = "URL"
|
||||
}
|
||||
|
||||
override DataFlow::Node getAUrlPart() {
|
||||
result in [this.getArg(1), this.getArgByName("value")]
|
||||
}
|
||||
|
||||
override string getFramework() { result = "pycurl.Curl" }
|
||||
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
// TODO: Look into disabling certificate validation
|
||||
none()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -220,7 +220,7 @@ private module RestFramework {
|
||||
*
|
||||
* Use the predicate `Request::instance()` to get references to instances of `rest_framework.request.Request`.
|
||||
*/
|
||||
abstract class InstanceSource extends PrivateDjango::django::http::request::HttpRequest::InstanceSource {
|
||||
abstract class InstanceSource extends PrivateDjango::DjangoImpl::Http::Request::HttpRequest::InstanceSource {
|
||||
}
|
||||
|
||||
/** A direct instantiation of `rest_framework.request.Request`. */
|
||||
@@ -295,19 +295,8 @@ private module RestFramework {
|
||||
result = API::moduleImport("rest_framework").getMember("response").getMember("Response")
|
||||
}
|
||||
|
||||
/**
|
||||
* A source of instances of `rest_framework.response.Response`, 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 `Response::instance()` to get references to instances of `rest_framework.response.Response`.
|
||||
*/
|
||||
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
|
||||
|
||||
/** A direct instantiation of `rest_framework.response.Response`. */
|
||||
private class ClassInstantiation extends PrivateDjango::django::http::response::HttpResponse::InstanceSource,
|
||||
private class ClassInstantiation extends PrivateDjango::DjangoImpl::Http::Response::HttpResponse::InstanceSource,
|
||||
DataFlow::CallCfgNode {
|
||||
ClassInstantiation() { this = classRef().getACall() }
|
||||
|
||||
@@ -329,8 +318,8 @@ private module RestFramework {
|
||||
*
|
||||
* See https://www.django-rest-framework.org/api-guide/exceptions/#api-reference
|
||||
*/
|
||||
module APIException {
|
||||
/** A direct instantiation of `rest_framework.exceptions.APIException` or subclass. */
|
||||
module ApiException {
|
||||
/** A direct instantiation of `rest_framework.exceptions.ApiException` or subclass. */
|
||||
private class ClassInstantiation extends HTTP::Server::HttpResponse::Range,
|
||||
DataFlow::CallCfgNode {
|
||||
string className;
|
||||
@@ -366,4 +355,7 @@ private module RestFramework {
|
||||
override string getMimetypeDefault() { none() }
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for ApiException */
|
||||
deprecated module APIException = ApiException;
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ module SqlAlchemy {
|
||||
*
|
||||
* See https://docs.sqlalchemy.org/en/14/core/connections.html#dbapi-connections.
|
||||
*/
|
||||
module DBAPIConnection {
|
||||
module DBApiConnection {
|
||||
/**
|
||||
* A source of instances of DB-API Connections, extend this class to model new instances.
|
||||
*
|
||||
@@ -149,8 +149,8 @@ module SqlAlchemy {
|
||||
*/
|
||||
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
|
||||
|
||||
private class DBAPIConnectionSources extends InstanceSource, PEP249::Connection::InstanceSource {
|
||||
DBAPIConnectionSources() {
|
||||
private class DBApiConnectionSources extends InstanceSource, PEP249::Connection::InstanceSource {
|
||||
DBApiConnectionSources() {
|
||||
this.(DataFlow::MethodCallNode).calls(Engine::instance(), "raw_connection")
|
||||
or
|
||||
this.(DataFlow::AttrRead).accesses(Connection::instance(), "connection")
|
||||
@@ -169,6 +169,9 @@ module SqlAlchemy {
|
||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for DBApiConnection */
|
||||
deprecated module DBAPIConnection = DBApiConnection;
|
||||
|
||||
/**
|
||||
* Provides models for the `sqlalchemy.orm.Session` class
|
||||
*
|
||||
|
||||
@@ -13,6 +13,9 @@ private import semmle.python.frameworks.PEP249
|
||||
private import semmle.python.frameworks.internal.PoorMansFunctionResolution
|
||||
private import semmle.python.frameworks.internal.SelfRefMixin
|
||||
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
|
||||
// modeling split over multiple files to keep this file from becoming too big
|
||||
private import semmle.python.frameworks.Stdlib.Urllib
|
||||
private import semmle.python.frameworks.Stdlib.Urllib2
|
||||
|
||||
/** Provides models for the Python standard library. */
|
||||
module Stdlib {
|
||||
@@ -87,7 +90,7 @@ module Stdlib {
|
||||
* https://github.com/python/cpython/blob/64f54b7ccd49764b0304e076bfd79b5482988f53/Lib/http/client.py#L175
|
||||
* and https://docs.python.org/3.9/library/email.compat32-message.html#email.message.Message
|
||||
*/
|
||||
module HTTPMessage {
|
||||
module HttpMessage {
|
||||
/**
|
||||
* A source of instances of `http.client.HTTPMessage`, extend this class to model new instances.
|
||||
*
|
||||
@@ -99,7 +102,7 @@ module Stdlib {
|
||||
*/
|
||||
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
|
||||
|
||||
/** Gets a reference to an instance of `http.client.HTTPMessage`. */
|
||||
/** Gets a reference to an instance of `http.client.HttpMessage`. */
|
||||
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result instanceof InstanceSource
|
||||
@@ -107,7 +110,7 @@ module Stdlib {
|
||||
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `http.client.HTTPMessage`. */
|
||||
/** Gets a reference to an instance of `http.client.HttpMessage`. */
|
||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
|
||||
/**
|
||||
@@ -126,6 +129,9 @@ module Stdlib {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for HttpMessage */
|
||||
deprecated module HTTPMessage = HttpMessage;
|
||||
|
||||
/**
|
||||
* Provides models for the `http.cookies.Morsel` class
|
||||
*
|
||||
@@ -302,7 +308,7 @@ private module StdlibPrivate {
|
||||
API::Node os() { result = API::moduleImport("os") }
|
||||
|
||||
/** Provides models for the `os` module. */
|
||||
module os {
|
||||
module OS {
|
||||
/** Gets a reference to the `os.path` module. */
|
||||
API::Node path() {
|
||||
result = os().getMember("path")
|
||||
@@ -317,7 +323,7 @@ private module StdlibPrivate {
|
||||
}
|
||||
|
||||
/** Provides models for the `os.path` module */
|
||||
module path {
|
||||
module OsPath {
|
||||
/** Gets a reference to the `os.path.join` function. */
|
||||
API::Node join() { result = path().getMember("join") }
|
||||
}
|
||||
@@ -939,7 +945,7 @@ private module StdlibPrivate {
|
||||
// these raise errors if the file does not exist
|
||||
"getatime", "getmtime", "getctime", "getsize"
|
||||
] and
|
||||
this = os::path().getMember(name).getACall()
|
||||
this = OS::path().getMember(name).getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
@@ -955,7 +961,7 @@ private module StdlibPrivate {
|
||||
|
||||
/** A call to `os.path.samefile` will raise an exception if an `os.stat()` call on either pathname fails. */
|
||||
private class OsPathSamefileCall extends FileSystemAccess::Range, DataFlow::CallCfgNode {
|
||||
OsPathSamefileCall() { this = os::path().getMember("samefile").getACall() }
|
||||
OsPathSamefileCall() { this = OS::path().getMember("samefile").getACall() }
|
||||
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
result in [
|
||||
@@ -989,7 +995,7 @@ private module StdlibPrivate {
|
||||
|
||||
OsPathComputation() {
|
||||
methodName = pathComputation() and
|
||||
this = os::path().getMember(methodName).getACall()
|
||||
this = OS::path().getMember(methodName).getACall()
|
||||
}
|
||||
|
||||
DataFlow::Node getPathArg() {
|
||||
@@ -1016,7 +1022,7 @@ private module StdlibPrivate {
|
||||
* See https://docs.python.org/3/library/os.path.html#os.path.normpath
|
||||
*/
|
||||
private class OsPathNormpathCall extends Path::PathNormalization::Range, DataFlow::CallCfgNode {
|
||||
OsPathNormpathCall() { this = os::path().getMember("normpath").getACall() }
|
||||
OsPathNormpathCall() { this = OS::path().getMember("normpath").getACall() }
|
||||
|
||||
override DataFlow::Node getPathArg() { result in [this.getArg(0), this.getArgByName("path")] }
|
||||
}
|
||||
@@ -1026,7 +1032,7 @@ private module StdlibPrivate {
|
||||
* See https://docs.python.org/3/library/os.path.html#os.path.abspath
|
||||
*/
|
||||
private class OsPathAbspathCall extends Path::PathNormalization::Range, DataFlow::CallCfgNode {
|
||||
OsPathAbspathCall() { this = os::path().getMember("abspath").getACall() }
|
||||
OsPathAbspathCall() { this = OS::path().getMember("abspath").getACall() }
|
||||
|
||||
override DataFlow::Node getPathArg() { result in [this.getArg(0), this.getArgByName("path")] }
|
||||
}
|
||||
@@ -1036,7 +1042,7 @@ private module StdlibPrivate {
|
||||
* See https://docs.python.org/3/library/os.path.html#os.path.realpath
|
||||
*/
|
||||
private class OsPathRealpathCall extends Path::PathNormalization::Range, DataFlow::CallCfgNode {
|
||||
OsPathRealpathCall() { this = os::path().getMember("realpath").getACall() }
|
||||
OsPathRealpathCall() { this = OS::path().getMember("realpath").getACall() }
|
||||
|
||||
override DataFlow::Node getPathArg() { result in [this.getArg(0), this.getArgByName("path")] }
|
||||
}
|
||||
@@ -1137,7 +1143,7 @@ private module StdlibPrivate {
|
||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
exists(CallNode call |
|
||||
nodeTo.asCfgNode() = call and
|
||||
call = os::path::join().getACall().asCfgNode() and
|
||||
call = OS::OsPath::join().getACall().asCfgNode() and
|
||||
call.getAnArg() = nodeFrom.asCfgNode()
|
||||
)
|
||||
// TODO: Handle pathlib (like we do for os.path.join)
|
||||
@@ -1656,7 +1662,7 @@ private module StdlibPrivate {
|
||||
API::Node cgi() { result = API::moduleImport("cgi") }
|
||||
|
||||
/** Provides models for the `cgi` module. */
|
||||
module cgi {
|
||||
module Cgi {
|
||||
/**
|
||||
* Provides models for the `cgi.FieldStorage` class
|
||||
*
|
||||
@@ -1780,42 +1786,63 @@ private module StdlibPrivate {
|
||||
// ---------------------------------------------------------------------------
|
||||
// BaseHTTPServer (Python 2 only)
|
||||
// ---------------------------------------------------------------------------
|
||||
/** Gets a reference to the `BaseHTTPServer` module. */
|
||||
API::Node baseHTTPServer() { result = API::moduleImport("BaseHTTPServer") }
|
||||
/** Gets a reference to the `BaseHttpServer` module. */
|
||||
API::Node baseHttpServer() { result = API::moduleImport("BaseHTTPServer") }
|
||||
|
||||
/** Provides models for the `BaseHTTPServer` module. */
|
||||
module BaseHTTPServer {
|
||||
/** DEPRECATED: Alias for baseHttpServer */
|
||||
deprecated API::Node baseHTTPServer() { result = baseHttpServer() }
|
||||
|
||||
/** Provides models for the `BaseHttpServer` module. */
|
||||
module BaseHttpServer {
|
||||
/**
|
||||
* Provides models for the `BaseHTTPServer.BaseHTTPRequestHandler` class (Python 2 only).
|
||||
*/
|
||||
module BaseHTTPRequestHandler {
|
||||
/** Gets a reference to the `BaseHTTPServer.BaseHTTPRequestHandler` class. */
|
||||
API::Node classRef() { result = baseHTTPServer().getMember("BaseHTTPRequestHandler") }
|
||||
module BaseHttpRequestHandler {
|
||||
/** Gets a reference to the `BaseHttpServer.BaseHttpRequestHandler` class. */
|
||||
API::Node classRef() { result = baseHttpServer().getMember("BaseHTTPRequestHandler") }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for BaseHttpRequestHandler */
|
||||
deprecated module BaseHTTPRequestHandler = BaseHttpRequestHandler;
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for BaseHttpServer */
|
||||
deprecated module BaseHTTPServer = BaseHttpServer;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// SimpleHTTPServer (Python 2 only)
|
||||
// ---------------------------------------------------------------------------
|
||||
/** Gets a reference to the `SimpleHTTPServer` module. */
|
||||
API::Node simpleHTTPServer() { result = API::moduleImport("SimpleHTTPServer") }
|
||||
/** Gets a reference to the `SimpleHttpServer` module. */
|
||||
API::Node simpleHttpServer() { result = API::moduleImport("SimpleHTTPServer") }
|
||||
|
||||
/** Provides models for the `SimpleHTTPServer` module. */
|
||||
module SimpleHTTPServer {
|
||||
/** DEPRECATED: Alias for simpleHttpServer */
|
||||
deprecated API::Node simpleHTTPServer() { result = simpleHttpServer() }
|
||||
|
||||
/** Provides models for the `SimpleHttpServer` module. */
|
||||
module SimpleHttpServer {
|
||||
/**
|
||||
* Provides models for the `SimpleHTTPServer.SimpleHTTPRequestHandler` class (Python 2 only).
|
||||
*/
|
||||
module SimpleHTTPRequestHandler {
|
||||
/** Gets a reference to the `SimpleHTTPServer.SimpleHTTPRequestHandler` class. */
|
||||
API::Node classRef() { result = simpleHTTPServer().getMember("SimpleHTTPRequestHandler") }
|
||||
module SimpleHttpRequestHandler {
|
||||
/** Gets a reference to the `SimpleHttpServer.SimpleHttpRequestHandler` class. */
|
||||
API::Node classRef() { result = simpleHttpServer().getMember("SimpleHTTPRequestHandler") }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for SimpleHttpRequestHandler */
|
||||
deprecated module SimpleHTTPRequestHandler = SimpleHttpRequestHandler;
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for SimpleHttpServer */
|
||||
deprecated module SimpleHTTPServer = SimpleHttpServer;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// CGIHTTPServer (Python 2 only)
|
||||
// ---------------------------------------------------------------------------
|
||||
/** Gets a reference to the `CGIHTTPServer` module. */
|
||||
API::Node cgiHTTPServer() { result = API::moduleImport("CGIHTTPServer") }
|
||||
API::Node cgiHttpServer() { result = API::moduleImport("CGIHTTPServer") }
|
||||
|
||||
/** DEPRECATED: Alias for cgiHttpServer */
|
||||
deprecated API::Node cgiHTTPServer() { result = cgiHttpServer() }
|
||||
|
||||
/** Provides models for the `CGIHTTPServer` module. */
|
||||
module CGIHTTPServer {
|
||||
@@ -1824,7 +1851,7 @@ private module StdlibPrivate {
|
||||
*/
|
||||
module CGIHTTPRequestHandler {
|
||||
/** Gets a reference to the `CGIHTTPServer.CGIHTTPRequestHandler` class. */
|
||||
API::Node classRef() { result = cgiHTTPServer().getMember("CGIHTTPRequestHandler") }
|
||||
API::Node classRef() { result = cgiHttpServer().getMember("CGIHTTPRequestHandler") }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1835,7 +1862,7 @@ private module StdlibPrivate {
|
||||
API::Node http() { result = API::moduleImport("http") }
|
||||
|
||||
/** Provides models for the `http` module. */
|
||||
module http {
|
||||
module Http {
|
||||
// -------------------------------------------------------------------------
|
||||
// http.server
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -1843,27 +1870,33 @@ private module StdlibPrivate {
|
||||
API::Node server() { result = http().getMember("server") }
|
||||
|
||||
/** Provides models for the `http.server` module */
|
||||
module server {
|
||||
module Server {
|
||||
/**
|
||||
* Provides models for the `http.server.BaseHTTPRequestHandler` class (Python 3 only).
|
||||
*
|
||||
* See https://docs.python.org/3.9/library/http.server.html#http.server.BaseHTTPRequestHandler.
|
||||
*/
|
||||
module BaseHTTPRequestHandler {
|
||||
/** Gets a reference to the `http.server.BaseHTTPRequestHandler` class. */
|
||||
module BaseHttpRequestHandler {
|
||||
/** Gets a reference to the `http.server.BaseHttpRequestHandler` class. */
|
||||
API::Node classRef() { result = server().getMember("BaseHTTPRequestHandler") }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for BaseHttpRequestHandler */
|
||||
deprecated module BaseHTTPRequestHandler = BaseHttpRequestHandler;
|
||||
|
||||
/**
|
||||
* Provides models for the `http.server.SimpleHTTPRequestHandler` class (Python 3 only).
|
||||
*
|
||||
* See https://docs.python.org/3.9/library/http.server.html#http.server.SimpleHTTPRequestHandler.
|
||||
*/
|
||||
module SimpleHTTPRequestHandler {
|
||||
/** Gets a reference to the `http.server.SimpleHTTPRequestHandler` class. */
|
||||
module SimpleHttpRequestHandler {
|
||||
/** Gets a reference to the `http.server.SimpleHttpRequestHandler` class. */
|
||||
API::Node classRef() { result = server().getMember("SimpleHTTPRequestHandler") }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for SimpleHttpRequestHandler */
|
||||
deprecated module SimpleHTTPRequestHandler = SimpleHttpRequestHandler;
|
||||
|
||||
/**
|
||||
* Provides models for the `http.server.CGIHTTPRequestHandler` class (Python 3 only).
|
||||
*
|
||||
@@ -1883,27 +1916,30 @@ private module StdlibPrivate {
|
||||
* - https://docs.python.org/3.9/library/http.server.html#http.server.BaseHTTPRequestHandler
|
||||
* - https://docs.python.org/2.7/library/basehttpserver.html#BaseHTTPServer.BaseHTTPRequestHandler
|
||||
*/
|
||||
private module HTTPRequestHandler {
|
||||
/** Gets a reference to the `BaseHTTPRequestHandler` class or any subclass. */
|
||||
private module HttpRequestHandler {
|
||||
/** Gets a reference to the `BaseHttpRequestHandler` class or any subclass. */
|
||||
API::Node subclassRef() {
|
||||
result =
|
||||
[
|
||||
// Python 2
|
||||
BaseHTTPServer::BaseHTTPRequestHandler::classRef(),
|
||||
SimpleHTTPServer::SimpleHTTPRequestHandler::classRef(),
|
||||
BaseHttpServer::BaseHttpRequestHandler::classRef(),
|
||||
SimpleHttpServer::SimpleHttpRequestHandler::classRef(),
|
||||
CGIHTTPServer::CGIHTTPRequestHandler::classRef(),
|
||||
// Python 3
|
||||
http::server::BaseHTTPRequestHandler::classRef(),
|
||||
http::server::SimpleHTTPRequestHandler::classRef(),
|
||||
http::server::CGIHTTPRequestHandler::classRef()
|
||||
Http::Server::BaseHttpRequestHandler::classRef(),
|
||||
Http::Server::SimpleHttpRequestHandler::classRef(),
|
||||
Http::Server::CGIHTTPRequestHandler::classRef()
|
||||
].getASubclass*()
|
||||
}
|
||||
|
||||
/** A HTTPRequestHandler class definition (most likely in project code). */
|
||||
class HTTPRequestHandlerClassDef extends Class {
|
||||
HTTPRequestHandlerClassDef() { this.getParent() = subclassRef().getAUse().asExpr() }
|
||||
/** A HttpRequestHandler class definition (most likely in project code). */
|
||||
class HttpRequestHandlerClassDef extends Class {
|
||||
HttpRequestHandlerClassDef() { this.getParent() = subclassRef().getAUse().asExpr() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for HttpRequestHandlerClassDef */
|
||||
deprecated class HTTPRequestHandlerClassDef = HttpRequestHandlerClassDef;
|
||||
|
||||
/**
|
||||
* A source of instances of the `BaseHTTPRequestHandler` class or any subclass, extend this class to model new instances.
|
||||
*
|
||||
@@ -1915,16 +1951,16 @@ private module StdlibPrivate {
|
||||
*/
|
||||
abstract class InstanceSource extends DataFlow::Node { }
|
||||
|
||||
/** The `self` parameter in a method on the `BaseHTTPRequestHandler` class or any subclass. */
|
||||
/** The `self` parameter in a method on the `BaseHttpRequestHandler` class or any subclass. */
|
||||
private class SelfParam extends InstanceSource, RemoteFlowSource::Range, DataFlow::ParameterNode {
|
||||
SelfParam() {
|
||||
exists(HTTPRequestHandlerClassDef cls | cls.getAMethod().getArg(0) = this.getParameter())
|
||||
exists(HttpRequestHandlerClassDef cls | cls.getAMethod().getArg(0) = this.getParameter())
|
||||
}
|
||||
|
||||
override string getSourceType() { result = "stdlib HTTPRequestHandler" }
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of the `BaseHTTPRequestHandler` class or any subclass. */
|
||||
/** Gets a reference to an instance of the `BaseHttpRequestHandler` class or any subclass. */
|
||||
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result instanceof InstanceSource
|
||||
@@ -1932,7 +1968,7 @@ private module StdlibPrivate {
|
||||
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of the `BaseHTTPRequestHandler` class or any subclass. */
|
||||
/** Gets a reference to an instance of the `BaseHttpRequestHandler` class or any subclass. */
|
||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
|
||||
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||
@@ -1953,16 +1989,16 @@ private module StdlibPrivate {
|
||||
}
|
||||
}
|
||||
|
||||
/** An `HTTPMessage` instance that originates from a `BaseHTTPRequestHandler` instance. */
|
||||
private class BaseHTTPRequestHandlerHeadersInstances extends Stdlib::HTTPMessage::InstanceSource {
|
||||
BaseHTTPRequestHandlerHeadersInstances() {
|
||||
/** An `HttpMessage` instance that originates from a `BaseHttpRequestHandler` instance. */
|
||||
private class BaseHttpRequestHandlerHeadersInstances extends Stdlib::HttpMessage::InstanceSource {
|
||||
BaseHttpRequestHandlerHeadersInstances() {
|
||||
this.(DataFlow::AttrRead).accesses(instance(), "headers")
|
||||
}
|
||||
}
|
||||
|
||||
/** A file-like object that originates from a `BaseHTTPRequestHandler` instance. */
|
||||
private class BaseHTTPRequestHandlerFileLikeObjectInstances extends Stdlib::FileLikeObject::InstanceSource {
|
||||
BaseHTTPRequestHandlerFileLikeObjectInstances() {
|
||||
/** A file-like object that originates from a `BaseHttpRequestHandler` instance. */
|
||||
private class BaseHttpRequestHandlerFileLikeObjectInstances extends Stdlib::FileLikeObject::InstanceSource {
|
||||
BaseHttpRequestHandlerFileLikeObjectInstances() {
|
||||
this.(DataFlow::AttrRead).accesses(instance(), "rfile")
|
||||
}
|
||||
}
|
||||
@@ -1974,7 +2010,7 @@ private module StdlibPrivate {
|
||||
*/
|
||||
private class RequestHandlerFunc extends HTTP::Server::RequestHandler::Range {
|
||||
RequestHandlerFunc() {
|
||||
this = any(HTTPRequestHandlerClassDef cls).getAMethod() and
|
||||
this = any(HttpRequestHandlerClassDef cls).getAMethod() and
|
||||
this.getName() = "do_" + HTTP::httpVerb()
|
||||
}
|
||||
|
||||
@@ -2152,8 +2188,8 @@ private module StdlibPrivate {
|
||||
* - https://docs.python.org/2.7/library/httplib.html#httplib.HTTPConnection
|
||||
* - https://docs.python.org/2.7/library/httplib.html#httplib.HTTPSConnection
|
||||
*/
|
||||
module HTTPConnection {
|
||||
/** Gets a reference to the `http.client.HTTPConnection` class. */
|
||||
module HttpConnection {
|
||||
/** Gets a reference to the `http.client.HttpConnection` class. */
|
||||
private API::Node classRef() {
|
||||
exists(string className | className in ["HTTPConnection", "HTTPSConnection"] |
|
||||
// Python 3
|
||||
@@ -2181,7 +2217,7 @@ private module StdlibPrivate {
|
||||
abstract DataFlow::Node getHostArgument();
|
||||
}
|
||||
|
||||
/** A direct instantiation of `http.client.HTTPConnection`. */
|
||||
/** A direct instantiation of `http.client.HttpConnection`. */
|
||||
private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
|
||||
ClassInstantiation() { this = classRef().getACall() }
|
||||
|
||||
@@ -2209,7 +2245,7 @@ private module StdlibPrivate {
|
||||
instance(DataFlow::TypeTracker::end(), hostArg).flowsTo(result)
|
||||
}
|
||||
|
||||
/** A method call on a HTTPConnection that sends off a request */
|
||||
/** A method call on a HttpConnection that sends off a request */
|
||||
private class RequestCall extends HTTP::Client::Request::Range, DataFlow::MethodCallNode {
|
||||
RequestCall() { this.calls(instance(_), ["request", "_send_request", "putrequest"]) }
|
||||
|
||||
@@ -2235,7 +2271,7 @@ private module StdlibPrivate {
|
||||
|
||||
/** A call to the `getresponse` method. */
|
||||
private class HttpConnectionGetResponseCall extends DataFlow::MethodCallNode,
|
||||
HTTPResponse::InstanceSource {
|
||||
HttpResponse::InstanceSource {
|
||||
HttpConnectionGetResponseCall() { this.calls(instance(_), "getresponse") }
|
||||
}
|
||||
|
||||
@@ -2266,6 +2302,9 @@ private module StdlibPrivate {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for HttpConnection */
|
||||
deprecated module HTTPConnection = HttpConnection;
|
||||
|
||||
/**
|
||||
* Provides models for the `http.client.HTTPResponse` class
|
||||
*
|
||||
@@ -2273,8 +2312,8 @@ private module StdlibPrivate {
|
||||
* - https://docs.python.org/3.10/library/http.client.html#httpresponse-objects
|
||||
* - https://docs.python.org/3/library/http.client.html#http.client.HTTPResponse.
|
||||
*/
|
||||
module HTTPResponse {
|
||||
/** Gets a reference to the `http.client.HTTPResponse` class. */
|
||||
module HttpResponse {
|
||||
/** Gets a reference to the `http.client.HttpResponse` class. */
|
||||
private API::Node classRef() {
|
||||
result = API::moduleImport("http").getMember("client").getMember("HTTPResponse")
|
||||
}
|
||||
@@ -2293,12 +2332,12 @@ private module StdlibPrivate {
|
||||
abstract class InstanceSource extends Stdlib::FileLikeObject::InstanceSource,
|
||||
DataFlow::LocalSourceNode { }
|
||||
|
||||
/** A direct instantiation of `http.client.HTTPResponse`. */
|
||||
/** A direct instantiation of `http.client.HttpResponse`. */
|
||||
private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
|
||||
ClassInstantiation() { this = classRef().getACall() }
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `http.client.HTTPResponse`. */
|
||||
/** Gets a reference to an instance of `http.client.HttpResponse`. */
|
||||
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result instanceof InstanceSource
|
||||
@@ -2306,7 +2345,7 @@ private module StdlibPrivate {
|
||||
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `http.client.HTTPResponse`. */
|
||||
/** Gets a reference to an instance of `http.client.HttpResponse`. */
|
||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
|
||||
/**
|
||||
@@ -2324,9 +2363,9 @@ private module StdlibPrivate {
|
||||
override string getAsyncMethodName() { none() }
|
||||
}
|
||||
|
||||
/** An attribute read that is a HTTPMessage instance. */
|
||||
private class HTTPMessageInstances extends Stdlib::HTTPMessage::InstanceSource {
|
||||
HTTPMessageInstances() {
|
||||
/** An attribute read that is a HttpMessage instance. */
|
||||
private class HttpMessageInstances extends Stdlib::HttpMessage::InstanceSource {
|
||||
HttpMessageInstances() {
|
||||
this.(DataFlow::AttrRead).accesses(instance(), ["headers", "msg"])
|
||||
or
|
||||
this.(DataFlow::MethodCallNode).calls(instance(), "info")
|
||||
@@ -2334,11 +2373,15 @@ private module StdlibPrivate {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for HttpResponse */
|
||||
deprecated module HTTPResponse = HttpResponse;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// sqlite3
|
||||
// ---------------------------------------------------------------------------
|
||||
/**
|
||||
* sqlite3 implements PEP 249, providing ways to execute SQL statements against a database.
|
||||
* A model of sqlite3 as a module that implements PEP 249, providing ways to execute SQL statements
|
||||
* against a database.
|
||||
*
|
||||
* See https://devdocs.io/python~3.9/library/sqlite3
|
||||
*/
|
||||
@@ -2836,6 +2879,70 @@ private module StdlibPrivate {
|
||||
override string getKind() { result = Escaping::getRegexKind() }
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// xml.etree.ElementTree
|
||||
// ---------------------------------------------------------------------------
|
||||
/**
|
||||
* An instance of `xml.etree.ElementTree.ElementTree`.
|
||||
*
|
||||
* See https://docs.python.org/3.10/library/xml.etree.elementtree.html#xml.etree.ElementTree.ElementTree
|
||||
*/
|
||||
private API::Node elementTreeInstance() {
|
||||
//parse to a tree
|
||||
result =
|
||||
API::moduleImport("xml")
|
||||
.getMember("etree")
|
||||
.getMember("ElementTree")
|
||||
.getMember("parse")
|
||||
.getReturn()
|
||||
or
|
||||
// construct a tree without parsing
|
||||
result =
|
||||
API::moduleImport("xml")
|
||||
.getMember("etree")
|
||||
.getMember("ElementTree")
|
||||
.getMember("ElementTree")
|
||||
.getReturn()
|
||||
}
|
||||
|
||||
/**
|
||||
* An instance of `xml.etree.ElementTree.Element`.
|
||||
*
|
||||
* See https://docs.python.org/3.10/library/xml.etree.elementtree.html#xml.etree.ElementTree.Element
|
||||
*/
|
||||
private API::Node elementInstance() {
|
||||
// parse or go to the root of a tree
|
||||
result = elementTreeInstance().getMember(["parse", "getroot"]).getReturn()
|
||||
or
|
||||
// parse directly to an element
|
||||
result =
|
||||
API::moduleImport("xml")
|
||||
.getMember("etree")
|
||||
.getMember("ElementTree")
|
||||
.getMember(["fromstring", "fromstringlist", "XML"])
|
||||
.getReturn()
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to a find method on a tree or an element will execute an XPath expression.
|
||||
*/
|
||||
private class ElementTreeFindCall extends XML::XPathExecution::Range, DataFlow::CallCfgNode {
|
||||
string methodName;
|
||||
|
||||
ElementTreeFindCall() {
|
||||
methodName in ["find", "findall", "findtext"] and
|
||||
(
|
||||
this = elementTreeInstance().getMember(methodName).getACall()
|
||||
or
|
||||
this = elementInstance().getMember(methodName).getACall()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getXPath() { result in [this.getArg(0), this.getArgByName("match")] }
|
||||
|
||||
override string getName() { result = "xml.etree" }
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// urllib
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
71
python/ql/lib/semmle/python/frameworks/Stdlib/Urllib.qll
Normal file
71
python/ql/lib/semmle/python/frameworks/Stdlib/Urllib.qll
Normal file
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `urllib` module, part of
|
||||
* the Python standard library.
|
||||
*
|
||||
* See
|
||||
* - https://docs.python.org/2/library/urllib.html
|
||||
* - https://docs.python.org/3/library/urllib.html
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
/**
|
||||
* Provides models for the `urllib` module, part of
|
||||
* the Python standard library.
|
||||
*
|
||||
* See
|
||||
* - https://docs.python.org/2/library/urllib.html
|
||||
* - https://docs.python.org/3/library/urllib.html
|
||||
*/
|
||||
private module Urllib {
|
||||
/**
|
||||
* Provides models for the `urllib.request` extension library
|
||||
*
|
||||
* See https://docs.python.org/3.9/library/urllib.request.html
|
||||
*/
|
||||
module Request {
|
||||
/**
|
||||
* See
|
||||
* - https://docs.python.org/3.9/library/urllib.request.html#urllib.request.Request
|
||||
*/
|
||||
private class RequestCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode {
|
||||
RequestCall() {
|
||||
this = API::moduleImport("urllib").getMember("request").getMember("Request").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAUrlPart() { result in [this.getArg(0), this.getArgByName("url")] }
|
||||
|
||||
override string getFramework() { result = "urllib.request.Request" }
|
||||
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
// TODO: Look into disabling certificate validation
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See
|
||||
* - https://docs.python.org/3.9/library/urllib.request.html#urllib.request.urlopen
|
||||
*/
|
||||
private class UrlOpenCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode {
|
||||
UrlOpenCall() {
|
||||
this = API::moduleImport("urllib").getMember("request").getMember("urlopen").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAUrlPart() { result in [this.getArg(0), this.getArgByName("url")] }
|
||||
|
||||
override string getFramework() { result = "urllib.request.urlopen" }
|
||||
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
// TODO: Look into disabling certificate validation
|
||||
none()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
56
python/ql/lib/semmle/python/frameworks/Stdlib/Urllib2.qll
Normal file
56
python/ql/lib/semmle/python/frameworks/Stdlib/Urllib2.qll
Normal file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `urllib2` module, part of
|
||||
* the Python 2 standard library.
|
||||
*
|
||||
* See https://docs.python.org/2/library/urllib2.html
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
/**
|
||||
* Provides models for the the `urllib2` module, part of
|
||||
* the Python 2 standard library.
|
||||
*
|
||||
* See https://docs.python.org/2/library/urllib2.html
|
||||
*/
|
||||
private module Urllib2 {
|
||||
/**
|
||||
* See
|
||||
* - https://docs.python.org/2/library/urllib2.html#urllib2.Request
|
||||
*/
|
||||
private class RequestCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode {
|
||||
RequestCall() { this = API::moduleImport("urllib2").getMember("Request").getACall() }
|
||||
|
||||
override DataFlow::Node getAUrlPart() { result in [this.getArg(0), this.getArgByName("url")] }
|
||||
|
||||
override string getFramework() { result = "urllib2.Request" }
|
||||
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
// TODO: Look into disabling certificate validation
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See
|
||||
* - https://docs.python.org/2/library/urllib2.html#urllib2.urlopen
|
||||
*/
|
||||
private class UrlOpenCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode {
|
||||
UrlOpenCall() { this = API::moduleImport("urllib2").getMember("urlopen").getACall() }
|
||||
|
||||
override DataFlow::Node getAUrlPart() { result in [this.getArg(0), this.getArgByName("url")] }
|
||||
|
||||
override string getFramework() { result = "urllib2.urlopen" }
|
||||
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
// TODO: Look into disabling certificate validation
|
||||
none()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ private module Tornado {
|
||||
*
|
||||
* See https://www.tornadoweb.org/en/stable/httputil.html#tornado.httputil.HTTPHeaders.
|
||||
*/
|
||||
module HTTPHeaders {
|
||||
module HttpHeaders {
|
||||
/**
|
||||
* A source of instances of `tornado.httputil.HTTPHeaders`, extend this class to model new instances.
|
||||
*
|
||||
@@ -35,7 +35,7 @@ private module Tornado {
|
||||
*/
|
||||
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
|
||||
|
||||
/** Gets a reference to an instance of `tornado.httputil.HTTPHeaders`. */
|
||||
/** Gets a reference to an instance of `tornado.httputil.HttpHeaders`. */
|
||||
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result instanceof InstanceSource
|
||||
@@ -43,7 +43,7 @@ private module Tornado {
|
||||
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `tornado.httputil.HTTPHeaders`. */
|
||||
/** Gets a reference to an instance of `tornado.httputil.HttpHeaders`. */
|
||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
|
||||
/**
|
||||
@@ -62,6 +62,9 @@ private module Tornado {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for HttpHeaders */
|
||||
deprecated module HTTPHeaders = HttpHeaders;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// tornado
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -69,7 +72,7 @@ private module Tornado {
|
||||
API::Node tornado() { result = API::moduleImport("tornado") }
|
||||
|
||||
/** Provides models for the `tornado` module. */
|
||||
module tornado {
|
||||
module TornadoModule {
|
||||
// -------------------------------------------------------------------------
|
||||
// tornado.web
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -77,7 +80,7 @@ private module Tornado {
|
||||
API::Node web() { result = tornado().getMember("web") }
|
||||
|
||||
/** Provides models for the `tornado.web` module */
|
||||
module web {
|
||||
module Web {
|
||||
/**
|
||||
* Provides models for the `tornado.web.RequestHandler` class and subclasses.
|
||||
*
|
||||
@@ -102,7 +105,7 @@ private module Tornado {
|
||||
/** Gets a reference to this class. */
|
||||
private DataFlow::TypeTrackingNode getARef(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result.asExpr().(ClassExpr) = this.getParent()
|
||||
result.asExpr() = this.getParent()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = this.getARef(t2).track(t2, t))
|
||||
}
|
||||
@@ -196,7 +199,7 @@ private module Tornado {
|
||||
override string getAsyncMethodName() { none() }
|
||||
}
|
||||
|
||||
private class RequestAttrAccess extends tornado::httputil::HttpServerRequest::InstanceSource {
|
||||
private class RequestAttrAccess extends TornadoModule::HttpUtil::HttpServerRequest::InstanceSource {
|
||||
RequestAttrAccess() {
|
||||
this.(DataFlow::AttrRead).getObject() = instance() and
|
||||
this.(DataFlow::AttrRead).getAttributeName() = "request"
|
||||
@@ -260,7 +263,7 @@ private module Tornado {
|
||||
API::Node httputil() { result = tornado().getMember("httputil") }
|
||||
|
||||
/** Provides models for the `tornado.httputil` module */
|
||||
module httputil {
|
||||
module HttpUtil {
|
||||
/**
|
||||
* Provides models for the `tornado.httputil.HttpServerRequest` class
|
||||
*
|
||||
@@ -323,9 +326,9 @@ private module Tornado {
|
||||
override string getAsyncMethodName() { none() }
|
||||
}
|
||||
|
||||
/** An `HTTPHeaders` instance that originates from a Tornado request. */
|
||||
private class TornadoRequestHTTPHeadersInstances extends HTTPHeaders::InstanceSource {
|
||||
TornadoRequestHTTPHeadersInstances() {
|
||||
/** An `HttpHeaders` instance that originates from a Tornado request. */
|
||||
private class TornadoRequestHttpHeadersInstances extends HttpHeaders::InstanceSource {
|
||||
TornadoRequestHttpHeadersInstances() {
|
||||
this.(DataFlow::AttrRead).accesses(instance(), "headers")
|
||||
}
|
||||
}
|
||||
@@ -354,14 +357,16 @@ private module Tornado {
|
||||
// ---------------------------------------------------------------------------
|
||||
// routing
|
||||
// ---------------------------------------------------------------------------
|
||||
/** A sequence that defines a number of route rules */
|
||||
/** Gets a sequence that defines a number of route rules */
|
||||
SequenceNode routeSetupRuleList() {
|
||||
exists(CallNode call | call = any(tornado::web::Application::ClassInstantiation c).asCfgNode() |
|
||||
exists(CallNode call |
|
||||
call = any(TornadoModule::Web::Application::ClassInstantiation c).asCfgNode()
|
||||
|
|
||||
result in [call.getArg(0), call.getArgByName("handlers")]
|
||||
)
|
||||
or
|
||||
exists(CallNode call |
|
||||
call.getFunction() = tornado::web::Application::add_handlers().asCfgNode()
|
||||
call.getFunction() = TornadoModule::Web::Application::add_handlers().asCfgNode()
|
||||
|
|
||||
result in [call.getArg(1), call.getArgByName("host_handlers")]
|
||||
)
|
||||
@@ -403,7 +408,7 @@ private module Tornado {
|
||||
override DataFlow::Node getUrlPatternArg() { result.asCfgNode() = node.getElement(0) }
|
||||
|
||||
override Function getARequestHandler() {
|
||||
exists(tornado::web::RequestHandler::RequestHandlerClass cls |
|
||||
exists(TornadoModule::Web::RequestHandler::RequestHandlerClass cls |
|
||||
cls.getARef().asCfgNode() = node.getElement(1) and
|
||||
result = cls.getARequestHandler()
|
||||
)
|
||||
@@ -434,7 +439,7 @@ private module Tornado {
|
||||
/** A request handler defined in a tornado RequestHandler class, that has no known route. */
|
||||
private class TornadoRequestHandlerWithoutKnownRoute extends HTTP::Server::RequestHandler::Range {
|
||||
TornadoRequestHandlerWithoutKnownRoute() {
|
||||
exists(tornado::web::RequestHandler::RequestHandlerClass cls |
|
||||
exists(TornadoModule::Web::RequestHandler::RequestHandlerClass cls |
|
||||
cls.getARequestHandler() = this
|
||||
) and
|
||||
not exists(TornadoRouteSetup setup | setup.getARequestHandler() = this)
|
||||
@@ -462,7 +467,7 @@ private module Tornado {
|
||||
private class TornadoRequestHandlerRedirectCall extends HTTP::Server::HttpRedirectResponse::Range,
|
||||
DataFlow::CallCfgNode {
|
||||
TornadoRequestHandlerRedirectCall() {
|
||||
this.getFunction() = tornado::web::RequestHandler::redirectMethod()
|
||||
this.getFunction() = TornadoModule::Web::RequestHandler::redirectMethod()
|
||||
}
|
||||
|
||||
override DataFlow::Node getRedirectLocation() {
|
||||
@@ -484,7 +489,7 @@ private module Tornado {
|
||||
private class TornadoRequestHandlerWriteCall extends HTTP::Server::HttpResponse::Range,
|
||||
DataFlow::CallCfgNode {
|
||||
TornadoRequestHandlerWriteCall() {
|
||||
this.getFunction() = tornado::web::RequestHandler::writeMethod()
|
||||
this.getFunction() = TornadoModule::Web::RequestHandler::writeMethod()
|
||||
}
|
||||
|
||||
override DataFlow::Node getBody() { result in [this.getArg(0), this.getArgByName("chunk")] }
|
||||
@@ -502,7 +507,7 @@ private module Tornado {
|
||||
class TornadoRequestHandlerSetCookieCall extends HTTP::Server::CookieWrite::Range,
|
||||
DataFlow::MethodCallNode {
|
||||
TornadoRequestHandlerSetCookieCall() {
|
||||
this.calls(tornado::web::RequestHandler::instance(), "set_cookie")
|
||||
this.calls(TornadoModule::Web::RequestHandler::instance(), "set_cookie")
|
||||
}
|
||||
|
||||
override DataFlow::Node getHeaderArg() { none() }
|
||||
|
||||
75
python/ql/lib/semmle/python/frameworks/Urllib3.qll
Normal file
75
python/ql/lib/semmle/python/frameworks/Urllib3.qll
Normal file
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `urllib3` PyPI package.
|
||||
*
|
||||
* See
|
||||
* - https://pypi.org/project/urllib3/
|
||||
* - https://urllib3.readthedocs.io/en/stable/reference/
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
/**
|
||||
* Provides models for the `urllib3` PyPI package.
|
||||
*
|
||||
* See
|
||||
* - https://pypi.org/project/urllib3/
|
||||
* - https://urllib3.readthedocs.io/en/stable/reference/
|
||||
*/
|
||||
private module Urllib3 {
|
||||
/**
|
||||
* Provides models for the `urllib3.request.RequestMethods` class and subclasses, such
|
||||
* as the `urllib3.PoolManager` class
|
||||
*
|
||||
* See
|
||||
* - https://urllib3.readthedocs.io/en/stable/reference/urllib3.request.html#urllib3.request.RequestMethods
|
||||
*
|
||||
*
|
||||
* https://urllib3.readthedocs.io/en/stable/reference/urllib3.poolmanager.html.
|
||||
*/
|
||||
module PoolManager {
|
||||
/** Gets a reference to the `urllib3.PoolManager` class. */
|
||||
private API::Node classRef() {
|
||||
result =
|
||||
API::moduleImport("urllib3")
|
||||
.getMember(["PoolManager", "ProxyManager", "HTTPConnectionPool", "HTTPSConnectionPool"])
|
||||
or
|
||||
result =
|
||||
API::moduleImport("urllib3")
|
||||
.getMember("request")
|
||||
.getMember("RequestMethods")
|
||||
.getASubclass+()
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of a `urllib3.request.RequestMethods` subclass. */
|
||||
private API::Node instance() { result = classRef().getReturn() }
|
||||
|
||||
/**
|
||||
* A call to a method making an outgoing request.
|
||||
*
|
||||
* See
|
||||
* - https://urllib3.readthedocs.io/en/stable/reference/urllib3.request.html#urllib3.request.RequestMethods
|
||||
* - https://urllib3.readthedocs.io/en/stable/reference/urllib3.connectionpool.html#urllib3.HTTPConnectionPool.urlopen
|
||||
*/
|
||||
private class RequestCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode {
|
||||
RequestCall() {
|
||||
this =
|
||||
instance()
|
||||
.getMember(["request", "request_encode_url", "request_encode_body", "urlopen"])
|
||||
.getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAUrlPart() { result in [this.getArg(1), this.getArgByName("url")] }
|
||||
|
||||
override string getFramework() { result = "urllib3.PoolManager" }
|
||||
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
// TODO: Look into disabling certificate validation
|
||||
none()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ private module NotExposed {
|
||||
concat(string newModelFullyQualified |
|
||||
newModel(any(MySpec spec), newModelFullyQualified, _, _, _)
|
||||
|
|
||||
fullyQualifiedToAPIGraphPath(newModelFullyQualified), " or this = API::"
|
||||
fullyQualifiedToApiGraphPath(newModelFullyQualified), " or this = API::"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -69,10 +69,15 @@ private module NotExposed {
|
||||
//
|
||||
//
|
||||
bindingset[fullyQaulified]
|
||||
string fullyQualifiedToAPIGraphPath(string fullyQaulified) {
|
||||
string fullyQualifiedToApiGraphPath(string fullyQaulified) {
|
||||
result = "moduleImport(\"" + fullyQaulified.replaceAll(".", "\").getMember(\"") + "\")"
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for fullyQualifiedToApiGraphPath */
|
||||
deprecated string fullyQualifiedToAPIGraphPath(string fullyQaulified) {
|
||||
result = fullyQualifiedToApiGraphPath(fullyQaulified)
|
||||
}
|
||||
|
||||
bindingset[this]
|
||||
abstract class FindSubclassesSpec extends string {
|
||||
abstract API::Node getAlreadyModeledClass();
|
||||
@@ -99,13 +104,13 @@ private module NotExposed {
|
||||
or
|
||||
exists(string newSubclassName |
|
||||
newModel(spec, newSubclassName, _, _, _) and
|
||||
result.getPath() = fullyQualifiedToAPIGraphPath(newSubclassName)
|
||||
result.getPath() = fullyQualifiedToApiGraphPath(newSubclassName)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[fullyQualifiedName]
|
||||
predicate alreadyModeled(FindSubclassesSpec spec, string fullyQualifiedName) {
|
||||
fullyQualifiedToAPIGraphPath(fullyQualifiedName) = spec.getAlreadyModeledClass().getPath()
|
||||
fullyQualifiedToApiGraphPath(fullyQualifiedName) = spec.getAlreadyModeledClass().getPath()
|
||||
}
|
||||
|
||||
predicate isNonTestProjectCode(AstNode ast) {
|
||||
|
||||
@@ -3,26 +3,6 @@
|
||||
import python
|
||||
private import semmle.python.pointsto.PointsTo
|
||||
|
||||
/** A method that to a sub-class of `zope.interface.Interface` */
|
||||
deprecated class ZopeInterfaceMethod extends PyFunctionObject {
|
||||
/** Holds if this method belongs to a class that sub-classes `zope.interface.Interface` */
|
||||
ZopeInterfaceMethod() {
|
||||
exists(Object interface, ClassObject owner |
|
||||
interface = ModuleObject::named("zope.interface").attr("Interface") and
|
||||
owner.declaredAttribute(_) = this and
|
||||
owner.getAnImproperSuperType().getABaseType() = interface
|
||||
)
|
||||
}
|
||||
|
||||
override int minParameters() { result = super.minParameters() + 1 }
|
||||
|
||||
override int maxParameters() {
|
||||
if exists(this.getFunction().getVararg())
|
||||
then result = super.maxParameters()
|
||||
else result = super.maxParameters() + 1
|
||||
}
|
||||
}
|
||||
|
||||
/** A method that belongs to a sub-class of `zope.interface.Interface` */
|
||||
class ZopeInterfaceMethodValue extends PythonFunctionValue {
|
||||
/** Holds if this method belongs to a class that sub-classes `zope.interface.Interface` */
|
||||
|
||||
@@ -53,7 +53,7 @@ abstract class CallableObjectInternal extends ObjectInternal {
|
||||
override ObjectInternal getIterNext() { none() }
|
||||
}
|
||||
|
||||
/** Class representing Python functions */
|
||||
/** A Python function. */
|
||||
class PythonFunctionObjectInternal extends CallableObjectInternal, TPythonFunctionObject {
|
||||
override Function getScope() {
|
||||
exists(CallableExpr expr |
|
||||
@@ -167,7 +167,7 @@ private BasicBlock blockReturningNone(Function func) {
|
||||
)
|
||||
}
|
||||
|
||||
/** Class representing built-in functions such as `len` or `print`. */
|
||||
/** A built-in function such as `len` or `print`. */
|
||||
class BuiltinFunctionObjectInternal extends CallableObjectInternal, TBuiltinFunctionObject {
|
||||
override Builtin getBuiltin() { this = TBuiltinFunctionObject(result) }
|
||||
|
||||
@@ -290,7 +290,7 @@ private Builtin getBuiltinFunctionReturnType(Builtin func) {
|
||||
)
|
||||
}
|
||||
|
||||
/** Class representing methods of built-in classes (otherwise known as method-descriptors) such as `list.append`. */
|
||||
/** A method of a built-in class (otherwise known as method-descriptors) such as `list.append`. */
|
||||
class BuiltinMethodObjectInternal extends CallableObjectInternal, TBuiltinMethodObject {
|
||||
override Builtin getBuiltin() { this = TBuiltinMethodObject(result) }
|
||||
|
||||
@@ -380,7 +380,7 @@ class BuiltinMethodObjectInternal extends CallableObjectInternal, TBuiltinMethod
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing bound-methods.
|
||||
* A bound-method.
|
||||
* Note that built-in methods, such as `[].append` are also represented as bound-methods.
|
||||
* Although built-in methods and bound-methods are distinct classes in CPython, their behavior
|
||||
* is the same and we treat them identically.
|
||||
|
||||
@@ -6,7 +6,7 @@ private import semmle.python.pointsto.PointsToContext
|
||||
private import semmle.python.pointsto.MRO
|
||||
private import semmle.python.types.Builtins
|
||||
|
||||
/** Class representing classes */
|
||||
/** A class. */
|
||||
abstract class ClassObjectInternal extends ObjectInternal {
|
||||
override string getName() { result = this.getClassDeclaration().getName() }
|
||||
|
||||
@@ -109,7 +109,7 @@ abstract class ClassObjectInternal extends ObjectInternal {
|
||||
override predicate isNotSubscriptedType() { any() }
|
||||
}
|
||||
|
||||
/** Class representing Python source classes */
|
||||
/** A class that is defined in Python source. */
|
||||
class PythonClassObjectInternal extends ClassObjectInternal, TPythonClassObject {
|
||||
/** Gets the scope for this Python class */
|
||||
Class getScope() {
|
||||
@@ -163,7 +163,7 @@ class PythonClassObjectInternal extends ClassObjectInternal, TPythonClassObject
|
||||
}
|
||||
}
|
||||
|
||||
/** Class representing built-in classes, except `type` */
|
||||
/** A built-in class, except `type`. */
|
||||
class BuiltinClassObjectInternal extends ClassObjectInternal, TBuiltinClassObject {
|
||||
override Builtin getBuiltin() { this = TBuiltinClassObject(result) }
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ private import semmle.python.pointsto.PointsToContext
|
||||
private import semmle.python.types.Builtins
|
||||
|
||||
/**
|
||||
* Class representing constants.
|
||||
* A constant.
|
||||
* Includes `None`, `True` and `False` as
|
||||
* well as strings and integers.
|
||||
*/
|
||||
|
||||
@@ -6,7 +6,7 @@ private import semmle.python.pointsto.PointsToContext
|
||||
private import semmle.python.pointsto.MRO
|
||||
private import semmle.python.types.Builtins
|
||||
|
||||
/** Class representing property objects in Python */
|
||||
/** A property object. */
|
||||
class PropertyInternal extends ObjectInternal, TProperty {
|
||||
/** Gets the name of this property */
|
||||
override string getName() { result = this.getGetter().getName() }
|
||||
|
||||
@@ -178,7 +178,6 @@ class SelfInstanceInternal extends TSelfInstance, InstanceObject {
|
||||
result = "self instance of " + this.getClass().(ClassObjectInternal).getName()
|
||||
}
|
||||
|
||||
/** The boolean value of this object, if it has one */
|
||||
override boolean booleanValue() {
|
||||
//result = this.getClass().instancesBooleanValue()
|
||||
result = maybe()
|
||||
|
||||
@@ -18,7 +18,7 @@ private import semmle.python.types.Builtins
|
||||
|
||||
class ObjectSource = Object;
|
||||
|
||||
/* Aliases for scopes */
|
||||
/** An alias for Function used for scopes */
|
||||
class FunctionScope = Function;
|
||||
|
||||
class ClassScope = Class;
|
||||
@@ -26,7 +26,7 @@ class ClassScope = Class;
|
||||
class ModuleScope = Module;
|
||||
|
||||
/**
|
||||
* Class representing values in the Python program
|
||||
* A value in the Python program.
|
||||
* Each `Value` is a static approximation to a set of one or more real objects.
|
||||
*/
|
||||
class Value extends TObject {
|
||||
@@ -144,7 +144,7 @@ class Value extends TObject {
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing modules in the Python program
|
||||
* A module in the Python program.
|
||||
* Each `ModuleValue` represents a module object in the Python program.
|
||||
*/
|
||||
class ModuleValue extends Value instanceof ModuleObjectInternal {
|
||||
@@ -339,7 +339,7 @@ module Value {
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing callables in the Python program
|
||||
* A callable in the Python program.
|
||||
* Callables include Python functions, built-in functions and bound-methods,
|
||||
* but not classes.
|
||||
*/
|
||||
@@ -447,7 +447,7 @@ class CallableValue extends Value {
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing bound-methods, such as `o.func`, where `o` is an instance
|
||||
* A bound-method, such as `o.func`, where `o` is an instance
|
||||
* of a class that has a callable attribute `func`.
|
||||
*/
|
||||
class BoundMethodValue extends CallableValue instanceof BoundMethodObjectInternal {
|
||||
@@ -468,7 +468,7 @@ class BoundMethodValue extends CallableValue instanceof BoundMethodObjectInterna
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing classes in the Python program, both Python and built-in.
|
||||
* A class in the Python program, both Python and built-in.
|
||||
*/
|
||||
class ClassValue extends Value {
|
||||
ClassValue() { this.(ObjectInternal).isClass() = true }
|
||||
@@ -655,7 +655,7 @@ class ClassValue extends Value {
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing functions in the Python program, both Python and built-in.
|
||||
* A function in the Python program, both Python and built-in.
|
||||
* Note that this does not include other callables such as bound-methods.
|
||||
*/
|
||||
abstract class FunctionValue extends CallableValue {
|
||||
@@ -721,7 +721,7 @@ abstract class FunctionValue extends CallableValue {
|
||||
predicate isLambda() { this.getOrigin().getNode() instanceof Lambda }
|
||||
}
|
||||
|
||||
/** Class representing Python functions */
|
||||
/** A Python function. */
|
||||
class PythonFunctionValue extends FunctionValue {
|
||||
PythonFunctionValue() { this instanceof PythonFunctionObjectInternal }
|
||||
|
||||
@@ -769,7 +769,7 @@ class PythonFunctionValue extends FunctionValue {
|
||||
}
|
||||
}
|
||||
|
||||
/** Class representing builtin functions, such as `len` or `print` */
|
||||
/** A builtin function, such as `len` or `print`. */
|
||||
class BuiltinFunctionValue extends FunctionValue {
|
||||
BuiltinFunctionValue() { this instanceof BuiltinFunctionObjectInternal }
|
||||
|
||||
@@ -796,7 +796,7 @@ class BuiltinFunctionValue extends FunctionValue {
|
||||
}
|
||||
}
|
||||
|
||||
/** Class representing builtin methods, such as `list.append` or `set.add` */
|
||||
/** A builtin method, such as `list.append` or `set.add` */
|
||||
class BuiltinMethodValue extends FunctionValue {
|
||||
BuiltinMethodValue() { this instanceof BuiltinMethodObjectInternal }
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ class ObjectInternal extends TObject {
|
||||
abstract string toString();
|
||||
|
||||
/**
|
||||
* The boolean value of this object, this may be both
|
||||
* Gets the boolean value of this object. This may be both
|
||||
* true and false if the "object" represents a set of possible objects.
|
||||
*/
|
||||
abstract boolean booleanValue();
|
||||
@@ -88,14 +88,14 @@ class ObjectInternal extends TObject {
|
||||
abstract predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin);
|
||||
|
||||
/**
|
||||
* The integer value of things that have integer values and whose integer value is
|
||||
* Gets the integer value of things that have integer values and whose integer value is
|
||||
* tracked.
|
||||
* That is, some ints, mainly small numbers, and bools.
|
||||
*/
|
||||
abstract int intValue();
|
||||
|
||||
/**
|
||||
* The string value of things that have string values.
|
||||
* Gets the string value of things that have string values.
|
||||
* That is, strings.
|
||||
*/
|
||||
abstract string strValue();
|
||||
@@ -497,7 +497,7 @@ module ObjectInternal {
|
||||
|
||||
ObjectInternal superType() { result = TBuiltinClassObject(Builtin::special("super")) }
|
||||
|
||||
/** The old-style class type (Python 2 only) */
|
||||
/** Gets the old-style class type (Python 2 only) */
|
||||
ObjectInternal classType() { result = TBuiltinClassObject(Builtin::special("ClassType")) }
|
||||
|
||||
ObjectInternal emptyTuple() { result.(BuiltinTupleObjectInternal).length() = 0 }
|
||||
|
||||
@@ -90,16 +90,8 @@ abstract class TupleObjectInternal extends SequenceObjectInternal {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* The integer value of things that have integer values.
|
||||
* That is, ints and bools.
|
||||
*/
|
||||
override int intValue() { none() }
|
||||
|
||||
/**
|
||||
* The integer value of things that have integer values.
|
||||
* That is, strings.
|
||||
*/
|
||||
override string strValue() { none() }
|
||||
|
||||
override predicate calleeAndOffset(Function scope, int paramOffset) { none() }
|
||||
@@ -241,16 +233,8 @@ class SysVersionInfoObjectInternal extends TSysVersionInfo, SequenceObjectIntern
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* The integer value of things that have integer values.
|
||||
* That is, ints and bools.
|
||||
*/
|
||||
override int intValue() { none() }
|
||||
|
||||
/**
|
||||
* The integer value of things that have integer values.
|
||||
* That is, strings.
|
||||
*/
|
||||
override string strValue() { none() }
|
||||
|
||||
override predicate calleeAndOffset(Function scope, int paramOffset) { none() }
|
||||
@@ -261,10 +245,6 @@ class SysVersionInfoObjectInternal extends TSysVersionInfo, SequenceObjectIntern
|
||||
|
||||
override predicate subscriptUnknown() { none() }
|
||||
|
||||
/**
|
||||
* Gets the length of the sequence that this "object" represents.
|
||||
* Always returns a value for a sequence, will be -1 if object has no fixed length.
|
||||
*/
|
||||
override int length() { result = 5 }
|
||||
|
||||
override predicate functionAndOffset(CallableObjectInternal function, int offset) { none() }
|
||||
|
||||
@@ -453,7 +453,7 @@ predicate common_module_name(string name) { name = ["zope.interface", "six.moves
|
||||
* This acts as a helper for ClassObjectInternal allowing some lookup without
|
||||
* recursion.
|
||||
*/
|
||||
library class ClassDecl extends @py_object {
|
||||
class ClassDecl extends @py_object {
|
||||
ClassDecl() {
|
||||
this.(Builtin).isClass() and not this = Builtin::unknownType()
|
||||
or
|
||||
|
||||
@@ -25,13 +25,13 @@ module BasePointsTo {
|
||||
}
|
||||
}
|
||||
|
||||
/** The kwargs parameter (**kwargs) in a function definition is always a dict */
|
||||
/** Gets the kwargs parameter (`**kwargs`). In a function definition this is always a dict. */
|
||||
predicate kwargs_points_to(ControlFlowNode f, ClassObject cls) {
|
||||
exists(Function func | func.getKwarg() = f.getNode()) and
|
||||
cls = theDictType()
|
||||
}
|
||||
|
||||
/** The varargs (*varargs) in a function definition is always a tuple */
|
||||
/** Gets the varargs parameter (`*varargs`). In a function definition this is always a tuple. */
|
||||
predicate varargs_points_to(ControlFlowNode f, ClassObject cls) {
|
||||
exists(Function func | func.getVararg() = f.getNode()) and
|
||||
cls = theTupleType()
|
||||
@@ -120,11 +120,11 @@ int version_tuple_compare(Object t) {
|
||||
version_tuple_value(t) > major_minor() and result = 1
|
||||
}
|
||||
|
||||
/* Holds if `cls` is a new-style class if it were to have no explicit base classes */
|
||||
/** Holds if `cls` is a new-style class if it were to have no explicit base classes */
|
||||
predicate baseless_is_new_style(ClassObject cls) {
|
||||
cls.isBuiltin()
|
||||
or
|
||||
major_version() = 3
|
||||
major_version() = 3 and exists(cls)
|
||||
or
|
||||
exists(cls.declaredMetaClass())
|
||||
}
|
||||
@@ -185,22 +185,6 @@ predicate function_can_never_return(FunctionObject func) {
|
||||
func = ModuleObject::named("sys").attr("exit")
|
||||
}
|
||||
|
||||
private newtype TIterationDefinition =
|
||||
TIterationDefinition_(SsaSourceVariable var, ControlFlowNode def, ControlFlowNode sequence) {
|
||||
SsaSource::iteration_defined_variable(var, def, sequence)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED. For backwards compatibility only.
|
||||
* A definition of a variable in a for loop `for v in ...:`
|
||||
*/
|
||||
deprecated class IterationDefinition extends TIterationDefinition {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = "IterationDefinition" }
|
||||
|
||||
ControlFlowNode getSequence() { this = TIterationDefinition_(_, _, result) }
|
||||
}
|
||||
|
||||
/** Hold if outer contains inner, both are contained within a test and inner is a use is a plain use or an attribute lookup */
|
||||
pragma[noinline]
|
||||
predicate contains_interesting_expression_within_test(ControlFlowNode outer, ControlFlowNode inner) {
|
||||
|
||||
@@ -23,6 +23,8 @@ private newtype TTInvocation =
|
||||
}
|
||||
|
||||
/**
|
||||
* A function invocation.
|
||||
*
|
||||
* This class represents a static approximation to the
|
||||
* dynamic call-graph. A `FunctionInvocation` represents
|
||||
* all calls made to a function for a given context.
|
||||
|
||||
@@ -1,5 +1,2 @@
|
||||
/* For backwards compatibility */
|
||||
import PointsTo::PointsTo as P
|
||||
|
||||
/** DEPRECATED: Use `PointsTo` instead */
|
||||
deprecated module FinalPointsTo = P;
|
||||
|
||||
@@ -114,49 +114,33 @@ class ClassList extends TClassList {
|
||||
this = Empty() and result = Empty()
|
||||
}
|
||||
|
||||
predicate legalMergeHead(ClassObjectInternal cls) {
|
||||
this.getTail().doesNotContain(cls)
|
||||
or
|
||||
this = Empty()
|
||||
}
|
||||
|
||||
predicate contains(ClassObjectInternal cls) {
|
||||
cls = this.getHead()
|
||||
or
|
||||
this.getTail().contains(cls)
|
||||
}
|
||||
|
||||
/** Use negative formulation to avoid negative recursion */
|
||||
predicate doesNotContain(ClassObjectInternal cls) {
|
||||
this.relevantForContains(cls) and
|
||||
cls != this.getHead() and
|
||||
this.getTail().doesNotContain(cls)
|
||||
or
|
||||
this = Empty()
|
||||
}
|
||||
|
||||
private predicate relevantForContains(ClassObjectInternal cls) {
|
||||
exists(ClassListList list |
|
||||
list.getItem(_).getHead() = cls and
|
||||
list.getItem(_) = this
|
||||
)
|
||||
or
|
||||
exists(ClassList l |
|
||||
l.relevantForContains(cls) and
|
||||
this = l.getTail()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
ClassObjectInternal findDeclaringClass(string name) {
|
||||
exists(ClassDecl head | head = this.getHead().getClassDeclaration() |
|
||||
if head.declaresAttribute(name)
|
||||
then result = this.getHead()
|
||||
else result = this.getTail().findDeclaringClass(name)
|
||||
exists(ClassObjectInternal head, ClassList tail, ClassDecl decl |
|
||||
this = Cons(head, tail) and decl = head.getClassDeclaration()
|
||||
|
|
||||
if decl.declaresAttribute(name) then result = head else result = tail.findDeclaringClass(name)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private ClassObjectInternal findDeclaringClassAttribute(string name) {
|
||||
result = this.findDeclaringClass(name) and
|
||||
(
|
||||
exists(any(Builtin b).getMember(name))
|
||||
or
|
||||
declaredAttributeVar(_, name, _)
|
||||
)
|
||||
}
|
||||
|
||||
predicate lookup(string name, ObjectInternal value, CfgOrigin origin) {
|
||||
exists(ClassObjectInternal decl | decl = this.findDeclaringClass(name) |
|
||||
exists(ClassObjectInternal decl | decl = this.findDeclaringClassAttribute(name) |
|
||||
Types::declaredAttribute(decl, name, value, origin)
|
||||
)
|
||||
}
|
||||
@@ -199,12 +183,18 @@ class ClassList extends TClassList {
|
||||
or
|
||||
this.duplicate(n) and result = this.deduplicate(n + 1)
|
||||
or
|
||||
exists(ClassObjectInternal cls |
|
||||
n = this.firstIndex(cls) and
|
||||
result = Cons(cls, this.deduplicate(n + 1))
|
||||
exists(ClassObjectInternal cls, ClassList tail |
|
||||
this.deduplicateCons(n, cls, tail) and
|
||||
result = Cons(cls, tail)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate deduplicateCons(int n, ClassObjectInternal cls, ClassList tail) {
|
||||
n = this.firstIndex(cls) and
|
||||
tail = this.deduplicate(n + 1)
|
||||
}
|
||||
|
||||
predicate isEmpty() { this = Empty() }
|
||||
|
||||
ClassList reverse() { reverse_step(this, Empty(), result) }
|
||||
@@ -273,6 +263,24 @@ private class ClassListList extends TClassListList {
|
||||
result = this.getTail().getItem(n - 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as
|
||||
*
|
||||
* ```ql
|
||||
* result = this.getItem(n) and n = this.length() - 1
|
||||
* ```
|
||||
*
|
||||
* but avoids non-linear recursion.
|
||||
*/
|
||||
ClassList getLastItem(int n) {
|
||||
n = 0 and this = ConsList(result, EmptyList())
|
||||
or
|
||||
exists(ClassListList tail |
|
||||
this = ConsList(_, tail) and
|
||||
result = tail.getLastItem(n - 1)
|
||||
)
|
||||
}
|
||||
|
||||
private ClassObjectInternal getAHead() {
|
||||
result = this.getHead().getHead()
|
||||
or
|
||||
@@ -295,38 +303,63 @@ private class ClassListList extends TClassListList {
|
||||
ClassObjectInternal cls, ClassList removed_head, ClassListList removed_tail, int n
|
||||
) {
|
||||
cls = this.bestMergeCandidate() and
|
||||
n = this.length() - 1 and
|
||||
removed_head = this.getItem(n).removeHead(cls) and
|
||||
removed_head = this.getLastItem(n).removeHead(cls) and
|
||||
removed_tail = EmptyList()
|
||||
or
|
||||
removed_head = this.removedClassPartsCons1(cls, removed_tail, n).removeHead(cls)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate removedClassPartsCons0(ClassObjectInternal cls, ClassListList removed_tail, int n) {
|
||||
exists(ClassList prev_head, ClassListList prev_tail |
|
||||
this.removedClassParts(cls, prev_head, prev_tail, n + 1) and
|
||||
removed_head = this.getItem(n).removeHead(cls) and
|
||||
removed_tail = ConsList(prev_head, prev_tail)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
ClassList removedClassPartsCons1(ClassObjectInternal cls, ClassListList removed_tail, int n) {
|
||||
this.removedClassPartsCons0(cls, removed_tail, n) and
|
||||
result = this.getItem(n)
|
||||
}
|
||||
|
||||
ClassListList remove(ClassObjectInternal cls) {
|
||||
exists(ClassList removed_head, ClassListList removed_tail |
|
||||
this.removedClassParts(cls, removed_head, removed_tail, 0) and
|
||||
result = ConsList(removed_head, removed_tail)
|
||||
)
|
||||
or
|
||||
this = EmptyList() and result = EmptyList()
|
||||
this = EmptyList() and result = EmptyList() and exists(cls)
|
||||
}
|
||||
|
||||
predicate legalMergeCandidate(ClassObjectInternal cls, int n) {
|
||||
cls = this.getAHead() and n = this.length()
|
||||
pragma[nomagic]
|
||||
private predicate legalMergeCandidateNonEmpty(
|
||||
ClassObjectInternal cls, ClassListList remainingList, ClassList remaining
|
||||
) {
|
||||
this.legalMergeCandidate(cls, ConsList(Cons(_, remaining), remainingList))
|
||||
or
|
||||
this.getItem(n).legalMergeHead(cls) and
|
||||
this.legalMergeCandidate(cls, n + 1)
|
||||
exists(ClassObjectInternal head |
|
||||
this.legalMergeCandidateNonEmpty(cls, remainingList, Cons(head, remaining)) and
|
||||
cls != head
|
||||
)
|
||||
}
|
||||
|
||||
predicate legalMergeCandidate(ClassObjectInternal cls) { this.legalMergeCandidate(cls, 0) }
|
||||
private predicate legalMergeCandidate(ClassObjectInternal cls, ClassListList remaining) {
|
||||
cls = this.getAHead() and remaining = this
|
||||
or
|
||||
this.legalMergeCandidate(cls, ConsList(Empty(), remaining))
|
||||
or
|
||||
this.legalMergeCandidateNonEmpty(cls, remaining, Empty())
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
predicate legalMergeCandidate(ClassObjectInternal cls) {
|
||||
this.legalMergeCandidate(cls, EmptyList())
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
predicate illegalMergeCandidate(ClassObjectInternal cls) {
|
||||
cls = this.getAHead() and
|
||||
this.getItem(_).getTail().contains(cls)
|
||||
this.legalMergeCandidateNonEmpty(cls, _, Cons(cls, _))
|
||||
}
|
||||
|
||||
ClassObjectInternal bestMergeCandidate(int n) {
|
||||
@@ -337,6 +370,7 @@ private class ClassListList extends TClassListList {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
ClassObjectInternal bestMergeCandidate() { result = this.bestMergeCandidate(0) }
|
||||
|
||||
/**
|
||||
@@ -417,16 +451,27 @@ private predicate merge_step(
|
||||
remaining_list = original
|
||||
or
|
||||
/* Removes the best merge candidate from `remaining_list` and prepends it to `reversed_mro` */
|
||||
exists(ClassObjectInternal head, ClassList prev_reverse_mro, ClassListList prev_list |
|
||||
merge_step(prev_reverse_mro, prev_list, original) and
|
||||
head = prev_list.bestMergeCandidate() and
|
||||
reversed_mro = Cons(head, prev_reverse_mro) and
|
||||
remaining_list = prev_list.remove(head)
|
||||
exists(ClassObjectInternal head, ClassList prev_reverse_mro |
|
||||
merge_stepCons(head, prev_reverse_mro, remaining_list, original) and
|
||||
reversed_mro = Cons(head, prev_reverse_mro)
|
||||
)
|
||||
or
|
||||
merge_step(reversed_mro, ConsList(Empty(), remaining_list), original)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate merge_stepCons(
|
||||
ClassObjectInternal head, ClassList prev_reverse_mro, ClassListList remaining_list,
|
||||
ClassListList original
|
||||
) {
|
||||
/* Removes the best merge candidate from `remaining_list` and prepends it to `reversed_mro` */
|
||||
exists(ClassListList prev_list |
|
||||
merge_step(prev_reverse_mro, prev_list, original) and
|
||||
head = prev_list.bestMergeCandidate() and
|
||||
remaining_list = prev_list.remove(head)
|
||||
)
|
||||
}
|
||||
|
||||
/* Helpers for `ClassList.reverse()` */
|
||||
private predicate needs_reversing(ClassList lst) {
|
||||
merge_step(lst, EmptyList(), _)
|
||||
@@ -439,10 +484,17 @@ private predicate reverse_step(ClassList lst, ClassList remainder, ClassList rev
|
||||
or
|
||||
exists(ClassObjectInternal head, ClassList tail |
|
||||
reversed = Cons(head, tail) and
|
||||
reverse_step(lst, Cons(head, remainder), tail)
|
||||
reverse_stepCons(lst, remainder, head, tail)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate reverse_stepCons(
|
||||
ClassList lst, ClassList remainder, ClassObjectInternal head, ClassList tail
|
||||
) {
|
||||
reverse_step(lst, Cons(head, remainder), tail)
|
||||
}
|
||||
|
||||
module Mro {
|
||||
cached
|
||||
ClassList newStyleMro(ClassObjectInternal cls) {
|
||||
|
||||
@@ -8,7 +8,7 @@ private import semmle.python.types.Builtins
|
||||
private import semmle.python.types.Extensions
|
||||
|
||||
/* Use this version for speed */
|
||||
library class CfgOrigin extends @py_object {
|
||||
class CfgOrigin extends @py_object {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() {
|
||||
/* Not to be displayed */
|
||||
@@ -145,24 +145,6 @@ module PointsTo {
|
||||
)
|
||||
}
|
||||
|
||||
deprecated predicate ssa_variable_points_to(
|
||||
EssaVariable var, PointsToContext context, Object obj, ClassObject cls, CfgOrigin origin
|
||||
) {
|
||||
exists(ObjectInternal value |
|
||||
PointsToInternal::variablePointsTo(var, context, value, origin) and
|
||||
cls = value.getClass().getSource()
|
||||
|
|
||||
obj = value.getSource()
|
||||
)
|
||||
}
|
||||
|
||||
deprecated CallNode get_a_call(Object func, PointsToContext context) {
|
||||
exists(ObjectInternal value |
|
||||
result = value.(Value).getACall(context) and
|
||||
func = value.getSource()
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate moduleExports(ModuleObjectInternal mod, string name) {
|
||||
InterModulePointsTo::moduleExportsBoolean(mod, name) = true
|
||||
@@ -1076,7 +1058,7 @@ module InterProceduralPointsTo {
|
||||
/** Helper for default_parameter_points_to */
|
||||
pragma[noinline]
|
||||
private predicate context_for_default_value(ParameterDefinition def, PointsToContext context) {
|
||||
context.isRuntime()
|
||||
context.isRuntime() and exists(def)
|
||||
or
|
||||
exists(PointsToContext caller, CallNode call, PythonFunctionObjectInternal func, int n |
|
||||
context.fromCall(call, func, caller) and
|
||||
@@ -1429,20 +1411,51 @@ module Expressions {
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
predicate subscriptPointsTo(
|
||||
private predicate indexPointsToInt(ControlFlowNode index, PointsToContext context, int n) {
|
||||
index = any(SubscriptNode subscr).getIndex() and
|
||||
PointsToInternal::pointsTo(index, context, TInt(n), _)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate getItemSequenceObjectInternal(
|
||||
ObjectInternal value, SequenceObjectInternal objvalue, int n
|
||||
) {
|
||||
value = objvalue.getItem(n)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate subscriptObjectAndIndexPointsToInt(
|
||||
SubscriptNode subscr, PointsToContext context, ControlFlowNode obj, ObjectInternal objvalue,
|
||||
int n
|
||||
) {
|
||||
exists(ControlFlowNode index |
|
||||
subscriptObjectAndIndex(subscr, context, obj, objvalue, index) and
|
||||
indexPointsToInt(index, context, n)
|
||||
)
|
||||
}
|
||||
|
||||
deprecated predicate subscriptPointsTo(
|
||||
SubscriptNode subscr, PointsToContext context, ObjectInternal value, ControlFlowNode origin,
|
||||
ControlFlowNode obj, ObjectInternal objvalue
|
||||
) {
|
||||
subscriptPointsTo(subscr, context, value, obj, objvalue) and
|
||||
origin = subscr
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate subscriptPointsTo(
|
||||
SubscriptNode subscr, PointsToContext context, ObjectInternal value, ControlFlowNode obj,
|
||||
ObjectInternal objvalue
|
||||
) {
|
||||
exists(ControlFlowNode index | subscriptObjectAndIndex(subscr, context, obj, objvalue, index) |
|
||||
objvalue.subscriptUnknown() and
|
||||
value = ObjectInternal::unknown()
|
||||
or
|
||||
exists(int n |
|
||||
PointsToInternal::pointsTo(index, context, TInt(n), _) and
|
||||
value = objvalue.(SequenceObjectInternal).getItem(n)
|
||||
)
|
||||
) and
|
||||
origin = subscr
|
||||
)
|
||||
or
|
||||
exists(int n |
|
||||
subscriptObjectAndIndexPointsToInt(subscr, context, obj, objvalue, n) and
|
||||
getItemSequenceObjectInternal(value, objvalue, n)
|
||||
)
|
||||
}
|
||||
|
||||
predicate subscriptPartsPointsTo(
|
||||
@@ -1466,15 +1479,22 @@ module Expressions {
|
||||
index = subscr.getIndex()
|
||||
}
|
||||
|
||||
deprecated predicate binaryPointsTo(
|
||||
BinaryExprNode b, PointsToContext context, ObjectInternal value, ControlFlowNode origin,
|
||||
ControlFlowNode operand, ObjectInternal opvalue
|
||||
) {
|
||||
binaryPointsTo(b, context, value, operand, opvalue) and
|
||||
origin = b
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracking too many binary expressions is likely to kill performance, so just say anything other than addition or bitwise or is 'unknown'.
|
||||
*/
|
||||
pragma[noinline]
|
||||
predicate binaryPointsTo(
|
||||
BinaryExprNode b, PointsToContext context, ObjectInternal value, ControlFlowNode origin,
|
||||
ControlFlowNode operand, ObjectInternal opvalue
|
||||
private predicate binaryPointsTo(
|
||||
BinaryExprNode b, PointsToContext context, ObjectInternal value, ControlFlowNode operand,
|
||||
ObjectInternal opvalue
|
||||
) {
|
||||
origin = b and
|
||||
operand = genericBinaryOperand(b) and
|
||||
PointsToInternal::pointsTo(operand, context, opvalue, _) and
|
||||
value = ObjectInternal::unknown()
|
||||
@@ -1491,12 +1511,19 @@ module Expressions {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
predicate addPointsTo(
|
||||
deprecated predicate addPointsTo(
|
||||
BinaryExprNode b, PointsToContext context, ObjectInternal value, ControlFlowNode origin,
|
||||
ControlFlowNode operand, ObjectInternal opvalue
|
||||
) {
|
||||
origin = b and
|
||||
addPointsTo(b, context, value, operand, opvalue) and
|
||||
origin = b
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate addPointsTo(
|
||||
BinaryExprNode b, PointsToContext context, ObjectInternal value, ControlFlowNode operand,
|
||||
ObjectInternal opvalue
|
||||
) {
|
||||
exists(Operator op |
|
||||
b.operands(operand, op, _)
|
||||
or
|
||||
@@ -1508,12 +1535,19 @@ module Expressions {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
predicate bitOrPointsTo(
|
||||
deprecated predicate bitOrPointsTo(
|
||||
BinaryExprNode b, PointsToContext context, ObjectInternal value, ControlFlowNode origin,
|
||||
ControlFlowNode operand, ObjectInternal opvalue
|
||||
) {
|
||||
origin = b and
|
||||
bitOrPointsTo(b, context, value, operand, opvalue) and
|
||||
origin = b
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate bitOrPointsTo(
|
||||
BinaryExprNode b, PointsToContext context, ObjectInternal value, ControlFlowNode operand,
|
||||
ObjectInternal opvalue
|
||||
) {
|
||||
exists(Operator op, ControlFlowNode other |
|
||||
b.operands(operand, op, other)
|
||||
or
|
||||
@@ -1533,10 +1567,18 @@ module Expressions {
|
||||
value = obj.intValue()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
predicate unaryPointsTo(
|
||||
deprecated predicate unaryPointsTo(
|
||||
UnaryExprNode u, PointsToContext context, ObjectInternal value, ControlFlowNode origin,
|
||||
ControlFlowNode operand, ObjectInternal opvalue
|
||||
) {
|
||||
unaryPointsTo(u, context, value, operand, opvalue) and
|
||||
origin = u
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate unaryPointsTo(
|
||||
UnaryExprNode u, PointsToContext context, ObjectInternal value, ControlFlowNode operand,
|
||||
ObjectInternal opvalue
|
||||
) {
|
||||
exists(Unaryop op |
|
||||
op = u.getNode().getOp() and
|
||||
@@ -1548,14 +1590,21 @@ module Expressions {
|
||||
op instanceof USub and value = ObjectInternal::fromInt(-opvalue.intValue())
|
||||
or
|
||||
not op instanceof Not and opvalue = ObjectInternal::unknown() and value = opvalue
|
||||
) and
|
||||
origin = u
|
||||
)
|
||||
}
|
||||
|
||||
deprecated predicate builtinCallPointsTo(
|
||||
CallNode call, PointsToContext context, ObjectInternal value, ControlFlowNode origin,
|
||||
ControlFlowNode arg, ObjectInternal argvalue
|
||||
) {
|
||||
builtinCallPointsTo(call, context, value, arg, argvalue) and
|
||||
origin = call
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
predicate builtinCallPointsTo(
|
||||
CallNode call, PointsToContext context, ObjectInternal value, ControlFlowNode origin,
|
||||
ControlFlowNode arg, ObjectInternal argvalue
|
||||
private predicate builtinCallPointsTo(
|
||||
CallNode call, PointsToContext context, ObjectInternal value, ControlFlowNode arg,
|
||||
ObjectInternal argvalue
|
||||
) {
|
||||
PointsToInternal::pointsTo(arg, context, argvalue, _) and
|
||||
arg = call.getArg(0) and
|
||||
@@ -1569,8 +1618,7 @@ module Expressions {
|
||||
callable != ObjectInternal::builtin("hasattr") and
|
||||
callable.isClass() = false and
|
||||
value = ObjectInternal::unknown()
|
||||
) and
|
||||
origin = call
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
@@ -1585,11 +1633,10 @@ module Expressions {
|
||||
|
||||
pragma[noinline]
|
||||
private predicate lenCallPointsTo(
|
||||
CallNode call, PointsToContext context, ObjectInternal value, ControlFlowNode origin,
|
||||
ControlFlowNode arg, ObjectInternal argvalue
|
||||
CallNode call, PointsToContext context, ObjectInternal value, ControlFlowNode arg,
|
||||
ObjectInternal argvalue
|
||||
) {
|
||||
len_call(call, arg, context, argvalue) and
|
||||
origin = call and
|
||||
exists(int len | len = argvalue.length() |
|
||||
value = TInt(len) and len >= 0
|
||||
or
|
||||
@@ -1815,19 +1862,26 @@ module Expressions {
|
||||
) {
|
||||
attributePointsTo(expr, context, value, origin, subexpr, subvalue)
|
||||
or
|
||||
subscriptPointsTo(expr, context, value, origin, subexpr, subvalue)
|
||||
subscriptPointsTo(expr, context, value, subexpr, subvalue) and
|
||||
origin = expr
|
||||
or
|
||||
addPointsTo(expr, context, value, origin, subexpr, subvalue)
|
||||
addPointsTo(expr, context, value, subexpr, subvalue) and
|
||||
origin = expr
|
||||
or
|
||||
bitOrPointsTo(expr, context, value, origin, subexpr, subvalue)
|
||||
bitOrPointsTo(expr, context, value, subexpr, subvalue) and
|
||||
origin = expr
|
||||
or
|
||||
binaryPointsTo(expr, context, value, origin, subexpr, subvalue)
|
||||
binaryPointsTo(expr, context, value, subexpr, subvalue) and
|
||||
origin = expr
|
||||
or
|
||||
unaryPointsTo(expr, context, value, origin, subexpr, subvalue)
|
||||
unaryPointsTo(expr, context, value, subexpr, subvalue) and
|
||||
origin = expr
|
||||
or
|
||||
builtinCallPointsTo(expr, context, value, origin, subexpr, subvalue)
|
||||
builtinCallPointsTo(expr, context, value, subexpr, subvalue) and
|
||||
origin = expr
|
||||
or
|
||||
lenCallPointsTo(expr, context, value, origin, subexpr, subvalue)
|
||||
lenCallPointsTo(expr, context, value, subexpr, subvalue) and
|
||||
origin = expr
|
||||
or
|
||||
typeCallPointsTo(expr, context, value, origin, subexpr, subvalue)
|
||||
or
|
||||
@@ -2068,6 +2122,12 @@ module Conditionals {
|
||||
}
|
||||
}
|
||||
|
||||
/** INTERNAL: Do not use. */
|
||||
predicate declaredAttributeVar(PythonClassObjectInternal cls, string name, EssaVariable var) {
|
||||
name = var.getName() and
|
||||
var.getAUse() = cls.getScope().getANormalExit()
|
||||
}
|
||||
|
||||
cached
|
||||
module Types {
|
||||
cached
|
||||
@@ -2163,8 +2223,7 @@ module Types {
|
||||
or
|
||||
value != ObjectInternal::undefined() and
|
||||
exists(EssaVariable var |
|
||||
name = var.getName() and
|
||||
var.getAUse() = cls.(PythonClassObjectInternal).getScope().getANormalExit() and
|
||||
declaredAttributeVar(cls, name, var) and
|
||||
PointsToInternal::variablePointsTo(var, _, value, origin)
|
||||
)
|
||||
}
|
||||
@@ -2209,7 +2268,7 @@ module Types {
|
||||
func != six_add_metaclass_function() and result = false
|
||||
)
|
||||
or
|
||||
not exists(Module m | m.getName() = "six") and result = false
|
||||
not exists(Module m | m.getName() = "six") and result = false and exists(cls)
|
||||
or
|
||||
exists(Class pycls |
|
||||
pycls = cls.getScope() and
|
||||
@@ -2336,7 +2395,7 @@ module Types {
|
||||
)
|
||||
}
|
||||
|
||||
/* Holds if type inference failed to compute the full class hierarchy for this class for the reason given. */
|
||||
/** Holds if type inference failed to compute the full class hierarchy for this class for the reason given. */
|
||||
private predicate failedInference(ClassObjectInternal cls, string reason, int priority) {
|
||||
strictcount(cls.(PythonClassObjectInternal).getScope().getADecorator()) > 1 and
|
||||
reason = "Multiple decorators" and
|
||||
|
||||
@@ -126,7 +126,7 @@ module Context {
|
||||
}
|
||||
|
||||
/**
|
||||
* Points-to context. Context can be one of:
|
||||
* A points-to context. Context can be one of:
|
||||
* * "main": Used for scripts.
|
||||
* * "import": Use for non-script modules.
|
||||
* * "default": Use for functions and methods without caller context.
|
||||
|
||||
@@ -176,8 +176,8 @@ private module RegexpMatching {
|
||||
}
|
||||
|
||||
/** A class to test whether a regular expression matches certain HTML tags. */
|
||||
class HTMLMatchingRegExp extends RegexpMatching::MatchedRegExp {
|
||||
HTMLMatchingRegExp() {
|
||||
class HtmlMatchingRegExp extends RegexpMatching::MatchedRegExp {
|
||||
HtmlMatchingRegExp() {
|
||||
// the regexp must mention "<" and ">" explicitly.
|
||||
forall(string angleBracket | angleBracket = ["<", ">"] |
|
||||
any(RegExpConstant term | term.getValue().matches("%" + angleBracket + "%")).getRootTerm() =
|
||||
@@ -204,12 +204,15 @@ class HTMLMatchingRegExp extends RegexpMatching::MatchedRegExp {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for HtmlMatchingRegExp */
|
||||
deprecated class HTMLMatchingRegExp = HtmlMatchingRegExp;
|
||||
|
||||
/**
|
||||
* Holds if `regexp` matches some HTML tags, but misses some HTML tags that it should match.
|
||||
*
|
||||
* When adding a new case to this predicate, make sure the test string used in `matches(..)` calls are present in `HTMLMatchingRegExp::test` / `HTMLMatchingRegExp::testWithGroups`.
|
||||
*/
|
||||
predicate isBadRegexpFilter(HTMLMatchingRegExp regexp, string msg) {
|
||||
predicate isBadRegexpFilter(HtmlMatchingRegExp regexp, string msg) {
|
||||
// CVE-2021-33829 - matching both "<!-- foo -->" and "<!-- foo --!>", but in different capture groups
|
||||
regexp.matches("<!-- foo -->") and
|
||||
regexp.matches("<!-- foo --!>") and
|
||||
|
||||
@@ -47,7 +47,7 @@ module PathInjection {
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
override predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) {
|
||||
override predicate isSanitizer(DataFlow::Node node, DataFlow::FlowState state) {
|
||||
// Block `NotNormalized` paths here, since they change state to `NormalizedUnchecked`
|
||||
node instanceof Path::PathNormalization and
|
||||
state instanceof NotNormalized
|
||||
@@ -60,7 +60,7 @@ module PathInjection {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(
|
||||
override predicate isAdditionalTaintStep(
|
||||
DataFlow::Node nodeFrom, DataFlow::FlowState stateFrom, DataFlow::Node nodeTo,
|
||||
DataFlow::FlowState stateTo
|
||||
) {
|
||||
|
||||
@@ -13,8 +13,8 @@ import semmle.python.dataflow.new.TaintTracking
|
||||
/**
|
||||
* Provides a taint-tracking configuration for detecting "reflected server-side cross-site scripting" vulnerabilities.
|
||||
*/
|
||||
module ReflectedXSS {
|
||||
import ReflectedXSSCustomizations::ReflectedXSS
|
||||
module ReflectedXss {
|
||||
import ReflectedXSSCustomizations::ReflectedXss
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for detecting "reflected server-side cross-site scripting" vulnerabilities.
|
||||
@@ -34,9 +34,12 @@ module ReflectedXSS {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for ReflectedXss */
|
||||
deprecated module ReflectedXSS = ReflectedXss;
|
||||
|
||||
/**
|
||||
* DEPRECATED: Don't extend this class for customization, since this will lead to bad
|
||||
* performance, instead use the new `ReflectedXSSCustomizations.qll` file, and extend
|
||||
* its' classes.
|
||||
*/
|
||||
deprecated class ReflectedXssConfiguration = ReflectedXSS::Configuration;
|
||||
deprecated class ReflectedXssConfiguration = ReflectedXss::Configuration;
|
||||
|
||||
@@ -15,7 +15,7 @@ private import semmle.python.dataflow.new.BarrierGuards
|
||||
* "reflected server-side cross-site scripting"
|
||||
* vulnerabilities, as well as extension points for adding your own.
|
||||
*/
|
||||
module ReflectedXSS {
|
||||
module ReflectedXss {
|
||||
/**
|
||||
* A data flow source for "reflected server-side cross-site scripting" vulnerabilities.
|
||||
*/
|
||||
@@ -74,3 +74,6 @@ module ReflectedXSS {
|
||||
*/
|
||||
class StringConstCompareAsSanitizerGuard extends SanitizerGuard, StringConstCompare { }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for ReflectedXss */
|
||||
deprecated module ReflectedXSS = ReflectedXss;
|
||||
|
||||
@@ -39,4 +39,7 @@ module SqlInjection {
|
||||
* performance, instead use the new `SqlInjectionCustomizations.qll` file, and extend
|
||||
* its' classes.
|
||||
*/
|
||||
deprecated class SQLInjectionConfiguration = SqlInjection::Configuration;
|
||||
deprecated class SqlInjectionConfiguration = SqlInjection::Configuration;
|
||||
|
||||
/** DEPRECATED: Alias for SqlInjectionConfiguration */
|
||||
deprecated class SQLInjectionConfiguration = SqlInjectionConfiguration;
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Provides class and predicates to track external data that
|
||||
* may represent malicious xpath query objects.
|
||||
*
|
||||
* This module is intended to be imported into a taint-tracking query.
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.dataflow.new.BarrierGuards
|
||||
|
||||
/** Models Xpath Injection related classes and functions */
|
||||
module XpathInjection {
|
||||
/**
|
||||
* A data flow source for "XPath injection" vulnerabilities.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow sink for "XPath injection" vulnerabilities.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A sanitizer for "XPath injection" vulnerabilities.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A sanitizer guard for "XPath injection" vulnerabilities.
|
||||
*/
|
||||
abstract class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A source of remote user input, considered as a flow source.
|
||||
*/
|
||||
class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { }
|
||||
|
||||
/**
|
||||
* A construction of an XPath expression, considered as a sink.
|
||||
*/
|
||||
class XPathConstructionArg extends Sink {
|
||||
XPathConstructionArg() { this = any(XML::XPathConstruction c).getXPath() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An execution of an XPath expression, considered as a sink.
|
||||
*/
|
||||
class XPathExecutionArg extends Sink {
|
||||
XPathExecutionArg() { this = any(XML::XPathExecution e).getXPath() }
|
||||
}
|
||||
}
|
||||
@@ -119,18 +119,18 @@ class EmptyPositiveSubPatttern extends RegExpSubPattern {
|
||||
* whose root node is not a disjunction.
|
||||
*/
|
||||
class RegExpRoot extends RegExpTerm {
|
||||
RegExpParent parent;
|
||||
|
||||
RegExpRoot() {
|
||||
exists(RegExpAlt alt |
|
||||
alt.isRootTerm() and
|
||||
this = alt.getAChild() and
|
||||
parent = alt.getParent()
|
||||
exists(RegExpParent parent |
|
||||
exists(RegExpAlt alt |
|
||||
alt.isRootTerm() and
|
||||
this = alt.getAChild() and
|
||||
parent = alt.getParent()
|
||||
)
|
||||
or
|
||||
this.isRootTerm() and
|
||||
not this instanceof RegExpAlt and
|
||||
parent = this.getParent()
|
||||
)
|
||||
or
|
||||
this.isRootTerm() and
|
||||
not this instanceof RegExpAlt and
|
||||
parent = this.getParent()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -466,13 +466,14 @@ private module CharacterClasses {
|
||||
* An implementation of `CharacterClass` for \d, \s, and \w.
|
||||
*/
|
||||
private class PositiveCharacterClassEscape extends CharacterClass {
|
||||
RegExpTerm cc;
|
||||
string charClass;
|
||||
|
||||
PositiveCharacterClassEscape() {
|
||||
isEscapeClass(cc, charClass) and
|
||||
this = getCanonicalCharClass(cc) and
|
||||
charClass = ["d", "s", "w"]
|
||||
exists(RegExpTerm cc |
|
||||
isEscapeClass(cc, charClass) and
|
||||
this = getCanonicalCharClass(cc) and
|
||||
charClass = ["d", "s", "w"]
|
||||
)
|
||||
}
|
||||
|
||||
override string getARelevantChar() {
|
||||
@@ -504,13 +505,14 @@ private module CharacterClasses {
|
||||
* An implementation of `CharacterClass` for \D, \S, and \W.
|
||||
*/
|
||||
private class NegativeCharacterClassEscape extends CharacterClass {
|
||||
RegExpTerm cc;
|
||||
string charClass;
|
||||
|
||||
NegativeCharacterClassEscape() {
|
||||
isEscapeClass(cc, charClass) and
|
||||
this = getCanonicalCharClass(cc) and
|
||||
charClass = ["D", "S", "W"]
|
||||
exists(RegExpTerm cc |
|
||||
isEscapeClass(cc, charClass) and
|
||||
this = getCanonicalCharClass(cc) and
|
||||
charClass = ["D", "S", "W"]
|
||||
)
|
||||
}
|
||||
|
||||
override string getARelevantChar() {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user