mirror of
https://github.com/github/codeql.git
synced 2026-05-02 04:05:14 +02:00
Merge branch 'main' into ZipSlip
This commit is contained in:
38
python/.vscode/ql.code-snippets
vendored
38
python/.vscode/ql.code-snippets
vendored
@@ -222,26 +222,6 @@
|
||||
],
|
||||
"description": "Type tracking class (select full class path before inserting)",
|
||||
},
|
||||
"foo": {
|
||||
"scope": "ql",
|
||||
"prefix": "foo",
|
||||
"body": [
|
||||
" /**",
|
||||
" * Taint propagation for `$1`.",
|
||||
" */",
|
||||
" private class InstanceTaintSteps extends InstanceTaintStepsHelper {",
|
||||
" InstanceTaintSteps() { this = \"$1\" }",
|
||||
"",
|
||||
" override DataFlow::Node getInstance() { result = instance() }",
|
||||
"",
|
||||
" override string getAttributeName() { none() }",
|
||||
"",
|
||||
" override string getMethodName() { none() }",
|
||||
"",
|
||||
" override string getAsyncMethodName() { none() }",
|
||||
" }",
|
||||
],
|
||||
},
|
||||
"API graph .getMember chain": {
|
||||
"scope": "ql",
|
||||
"prefix": "api graph .getMember chain",
|
||||
@@ -250,4 +230,22 @@
|
||||
],
|
||||
"description": "API graph .getMember chain (select full path before inserting)",
|
||||
},
|
||||
"debug partial flow": {
|
||||
"scope": "ql",
|
||||
"prefix": "debug partial flow",
|
||||
"body": [
|
||||
"// put the line below inside the configuration",
|
||||
"// override int explorationLimit() { result = 5 }",
|
||||
"// and then run quick evaluation on the predicate below",
|
||||
"// (and potentially limit the set of sources)",
|
||||
"predicate debugPartialFlow(Location loc, DataFlow::PartialPathNode node, int dist) {",
|
||||
" loc = node.getNode().getLocation() and",
|
||||
" exists(loc.getFile().getRelativePath()) and",
|
||||
" exists(Configuration config, DataFlow::PartialPathNode source |",
|
||||
" config.hasPartialFlow(source, node, dist)",
|
||||
" )",
|
||||
"}",
|
||||
],
|
||||
"description": "debug partial flow",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
## 0.0.10
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* The old points-to based modeling has been deprecated. Use the new type-tracking/API-graphs based modeling instead.
|
||||
|
||||
## 0.0.9
|
||||
|
||||
## 0.0.8
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
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
|
||||
---
|
||||
* 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.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The regular expression parser now groups sequences of normal characters. This reduces the number of instances of `RegExpNormalChar`.
|
||||
4
python/ql/lib/change-notes/2022-03-04-add-ssrf-sinks.md
Normal file
4
python/ql/lib/change-notes/2022-03-04-add-ssrf-sinks.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* 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).
|
||||
5
python/ql/lib/change-notes/released/0.0.10.md
Normal file
5
python/ql/lib/change-notes/released/0.0.10.md
Normal file
@@ -0,0 +1,5 @@
|
||||
## 0.0.10
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* The old points-to based modeling has been deprecated. Use the new type-tracking/API-graphs based modeling instead.
|
||||
1
python/ql/lib/change-notes/released/0.0.9.md
Normal file
1
python/ql/lib/change-notes/released/0.0.9.md
Normal file
@@ -0,0 +1 @@
|
||||
## 0.0.9
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.0.8
|
||||
lastReleaseVersion: 0.0.10
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/python-all
|
||||
version: 0.0.9-dev
|
||||
version: 0.0.11-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.
|
||||
*/
|
||||
|
||||
@@ -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,143 @@ 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 {
|
||||
/**
|
||||
* A data-flow node that executes an LDAP query.
|
||||
*
|
||||
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||
* extend `LDAPQuery::Range` instead.
|
||||
*/
|
||||
class LdapExecution extends DataFlow::Node {
|
||||
LdapExecution::Range range;
|
||||
|
||||
LdapExecution() { this = range }
|
||||
|
||||
/** Gets the argument containing the filter string. */
|
||||
DataFlow::Node getFilter() { result = range.getFilter() }
|
||||
|
||||
/** Gets the argument containing the base DN. */
|
||||
DataFlow::Node getBaseDn() { result = range.getBaseDn() }
|
||||
}
|
||||
|
||||
/** Provides classes for modeling new LDAP query execution-related APIs. */
|
||||
module LdapExecution {
|
||||
/**
|
||||
* A data-flow node that executes an LDAP query.
|
||||
*
|
||||
* Extend this class to model new APIs. If you want to refine existing API models,
|
||||
* extend `LDAPQuery` instead.
|
||||
*/
|
||||
abstract class Range extends DataFlow::Node {
|
||||
/** Gets the argument containing the filter string. */
|
||||
abstract DataFlow::Node getFilter();
|
||||
|
||||
/** Gets the argument containing the base DN. */
|
||||
abstract DataFlow::Node getBaseDn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A data-flow node that escapes meta-characters, which could be used to prevent
|
||||
* injection attacks.
|
||||
@@ -506,8 +647,20 @@ module Escaping {
|
||||
/** Gets the escape-kind for escaping a string so it can safely be included in HTML. */
|
||||
string getHtmlKind() { result = "html" }
|
||||
|
||||
/** Gets the escape-kind for escaping a string so it can safely be included in HTML. */
|
||||
/** Gets the escape-kind for escaping a string so it can safely be included in a regular expression. */
|
||||
string getRegexKind() { result = "regex" }
|
||||
|
||||
/**
|
||||
* Gets the escape-kind for escaping a string so it can safely be used as a
|
||||
* distinguished name (DN) in an LDAP search.
|
||||
*/
|
||||
string getLdapDnKind() { result = "ldap_dn" }
|
||||
|
||||
/**
|
||||
* Gets the escape-kind for escaping a string so it can safely be used as a
|
||||
* filter in an LDAP search.
|
||||
*/
|
||||
string getLdapFilterKind() { result = "ldap_filter" }
|
||||
// TODO: If adding an XML kind, update the modeling of the `MarkupSafe` PyPI package.
|
||||
//
|
||||
// Technically it claims to escape for both HTML and XML, but for now we don't have
|
||||
@@ -532,9 +685,28 @@ class RegexEscaping extends Escaping {
|
||||
RegexEscaping() { range.getKind() = Escaping::getRegexKind() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An escape of a string so it can be safely used as a distinguished name (DN)
|
||||
* in an LDAP search.
|
||||
*/
|
||||
class LdapDnEscaping extends Escaping {
|
||||
LdapDnEscaping() { range.getKind() = Escaping::getLdapDnKind() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An escape of a string so it can be safely used as a filter in an LDAP search.
|
||||
*/
|
||||
class LdapFilterEscaping extends Escaping {
|
||||
LdapFilterEscaping() { range.getKind() = Escaping::getLdapFilterKind() }
|
||||
}
|
||||
|
||||
/** Provides classes for modeling HTTP-related APIs. */
|
||||
module HTTP {
|
||||
import semmle.python.web.HttpConstants
|
||||
/** Gets an HTTP verb, in upper case */
|
||||
string httpVerb() { result in ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"] }
|
||||
|
||||
/** Gets an HTTP verb, in lower case */
|
||||
string httpVerbLower() { result = httpVerb().toLowerCase() }
|
||||
|
||||
/** Provides classes for modeling HTTP servers. */
|
||||
module Server {
|
||||
|
||||
@@ -315,7 +315,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 +446,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.
|
||||
@@ -685,7 +687,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 +730,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_ { }
|
||||
|
||||
@@ -155,7 +155,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 +164,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)
|
||||
}
|
||||
|
||||
@@ -19,15 +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
|
||||
@@ -42,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.
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -196,7 +197,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" }
|
||||
|
||||
@@ -39,7 +39,12 @@ newtype TRegExpParent =
|
||||
/** A special character */
|
||||
TRegExpSpecialChar(Regex re, int start, int end) { re.specialCharacter(start, end, _) } or
|
||||
/** A normal character */
|
||||
TRegExpNormalChar(Regex re, int start, int end) { re.normalCharacter(start, end) } or
|
||||
TRegExpNormalChar(Regex re, int start, int end) {
|
||||
re.normalCharacterSequence(start, end)
|
||||
or
|
||||
re.escapedCharacter(start, end) and
|
||||
not re.specialCharacter(start, end, _)
|
||||
} or
|
||||
/** A back reference */
|
||||
TRegExpBackRef(Regex re, int start, int end) { re.backreference(start, end) }
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -204,6 +204,8 @@ abstract class AttrRead extends AttrRef, Node, LocalSourceNode { }
|
||||
private class AttributeReadAsAttrRead extends AttrRead, CfgNode {
|
||||
override AttrNode node;
|
||||
|
||||
AttributeReadAsAttrRead() { node.isLoad() }
|
||||
|
||||
override Node getObject() { result.asCfgNode() = node.getObject() }
|
||||
|
||||
override ExprNode getAttributeNameExpr() {
|
||||
|
||||
@@ -63,7 +63,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.
|
||||
*/
|
||||
@@ -108,7 +108,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() {
|
||||
@@ -122,11 +122,13 @@ 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.
|
||||
*/
|
||||
ArgumentNode argumentPreUpdateNode() {
|
||||
Node argumentPreUpdateNode() {
|
||||
result = any(FunctionCall c).getArg(_)
|
||||
or
|
||||
// Avoid argument 0 of method calls as those have read post-update nodes.
|
||||
@@ -136,9 +138,14 @@ module syntheticPostUpdateNode {
|
||||
or
|
||||
// Avoid argument 0 of class calls as those have non-synthetic post-update nodes.
|
||||
exists(ClassCall c, int n | n > 0 | result = c.getArg(n))
|
||||
or
|
||||
// any argument of any call that we have not been able to resolve
|
||||
exists(CallNode call | not call = any(DataFlowCall c).getNode() |
|
||||
result.(CfgNode).getNode() in [call.getArg(_), call.getArgByName(_)]
|
||||
)
|
||||
}
|
||||
|
||||
/** 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
|
||||
@@ -147,7 +154,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
|
||||
@@ -606,7 +613,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();
|
||||
@@ -704,10 +711,10 @@ newtype TDataFlowCall =
|
||||
TFunctionCall(CallNode call) { call = any(FunctionValue f).getAFunctionCall() } or
|
||||
/** Bound methods need to make room for the explicit self parameter */
|
||||
TMethodCall(CallNode call) { call = any(FunctionValue f).getAMethodCall() } or
|
||||
TClassCall(CallNode call) { call = any(ClassValue c).getACall() } or
|
||||
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();
|
||||
@@ -732,7 +739,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.
|
||||
@@ -819,7 +826,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;
|
||||
|
||||
@@ -1067,19 +1074,18 @@ predicate comprehensionStoreStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `nodeFrom` flows into an attribute (corresponding to `c`) of `nodeTo` via an attribute assignment.
|
||||
* Holds if `nodeFrom` flows into the attribute `c` of `nodeTo` via an attribute assignment.
|
||||
*
|
||||
* For example, in
|
||||
* ```python
|
||||
* obj.foo = x
|
||||
* ```
|
||||
* data flows from `x` to (the post-update node for) `obj` via assignment to `foo`.
|
||||
* data flows from `x` to the attribute `foo` of (the post-update node for) `obj`.
|
||||
*/
|
||||
predicate attributeStoreStep(CfgNode nodeFrom, AttributeContent c, PostUpdateNode nodeTo) {
|
||||
exists(AttrNode attr |
|
||||
nodeFrom.asCfgNode() = attr.(DefinitionNode).getValue() and
|
||||
attr.getName() = c.getAttribute() and
|
||||
attr.getObject() = nodeTo.getPreUpdateNode().(CfgNode).getNode()
|
||||
predicate attributeStoreStep(Node nodeFrom, AttributeContent c, PostUpdateNode nodeTo) {
|
||||
exists(AttrWrite write |
|
||||
write.accesses(nodeTo.getPreUpdateNode(), c.getAttribute()) and
|
||||
nodeFrom = write.getValue()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1613,6 +1619,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).
|
||||
*
|
||||
@@ -1923,21 +1931,16 @@ pragma[noinline]
|
||||
TupleElementContent small_tuple() { result.getIndex() <= 7 }
|
||||
|
||||
/**
|
||||
* Holds if `nodeTo` is a read of an attribute (corresponding to `c`) of the object in `nodeFrom`.
|
||||
* Holds if `nodeTo` is a read of the attribute `c` of the object `nodeFrom`.
|
||||
*
|
||||
* For example, in
|
||||
* For example
|
||||
* ```python
|
||||
* obj.foo
|
||||
* ```
|
||||
* data flows from `obj` to `obj.foo` via a read from `foo`.
|
||||
* is a read of the attribute `foo` from the object `obj`.
|
||||
*/
|
||||
predicate attributeReadStep(CfgNode nodeFrom, AttributeContent c, CfgNode nodeTo) {
|
||||
exists(AttrNode attr |
|
||||
nodeFrom.asCfgNode() = attr.getObject() and
|
||||
nodeTo.asCfgNode() = attr and
|
||||
attr.getName() = c.getAttribute() and
|
||||
attr.isLoad()
|
||||
)
|
||||
predicate attributeReadStep(Node nodeFrom, AttributeContent c, AttrRead nodeTo) {
|
||||
nodeTo.accesses(nodeFrom, c.getAttribute())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1973,6 +1976,18 @@ predicate clearsContent(Node n, Content c) {
|
||||
kwOverflowClearStep(n, c)
|
||||
or
|
||||
matchClearStep(n, c)
|
||||
or
|
||||
attributeClearStep(n, c)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if values stored inside attribute `c` are cleared at node `n`.
|
||||
*
|
||||
* In `obj.foo = x` any old value stored in `foo` is cleared at the pre-update node
|
||||
* associated with `obj`
|
||||
*/
|
||||
predicate attributeClearStep(Node n, AttributeContent c) {
|
||||
exists(PostUpdateNode post | post.getPreUpdateNode() = n | attributeStoreStep(_, c, post))
|
||||
}
|
||||
|
||||
//--------
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPrivate
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
@@ -66,7 +67,12 @@ string prettyNodeForInlineTest(DataFlow::Node node) {
|
||||
result = "[post]" + prettyExpr(e)
|
||||
)
|
||||
or
|
||||
exists(Expr e | e = node.(DataFlowPrivate::SyntheticPreUpdateNode).getPostUpdateNode().asExpr() |
|
||||
result = "[pre]" + prettyExpr(e)
|
||||
)
|
||||
or
|
||||
not exists(node.asExpr()) and
|
||||
not exists(node.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr()) and
|
||||
not exists(node.(DataFlowPrivate::SyntheticPreUpdateNode).getPostUpdateNode().asExpr()) and
|
||||
result = node.toString()
|
||||
}
|
||||
|
||||
@@ -167,8 +167,25 @@ predicate stringManipulation(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeT
|
||||
*/
|
||||
predicate containerStep(DataFlow::CfgNode nodeFrom, DataFlow::Node nodeTo) {
|
||||
// construction by literal
|
||||
// TODO: Not limiting the content argument here feels like a BIG hack, but we currently get nothing for free :|
|
||||
DataFlowPrivate::storeStep(nodeFrom, _, nodeTo)
|
||||
//
|
||||
// TODO: once we have proper flow-summary modeling, we might not need this step any
|
||||
// longer -- but there needs to be a matching read-step for the store-step, and we
|
||||
// don't provide that right now.
|
||||
DataFlowPrivate::listStoreStep(nodeFrom, _, nodeTo)
|
||||
or
|
||||
DataFlowPrivate::setStoreStep(nodeFrom, _, nodeTo)
|
||||
or
|
||||
DataFlowPrivate::tupleStoreStep(nodeFrom, _, nodeTo)
|
||||
or
|
||||
DataFlowPrivate::dictStoreStep(nodeFrom, _, nodeTo)
|
||||
or
|
||||
// comprehension, so there is taint-flow from `x` in `[x for x in xs]` to the
|
||||
// resulting list of the list-comprehension.
|
||||
//
|
||||
// TODO: once we have proper flow-summary modeling, we might not need this step any
|
||||
// longer -- but there needs to be a matching read-step for the store-step, and we
|
||||
// don't provide that right now.
|
||||
DataFlowPrivate::comprehensionStoreStep(nodeFrom, _, nodeTo)
|
||||
or
|
||||
// constructor call
|
||||
exists(DataFlow::CallCfgNode call | call = nodeTo |
|
||||
|
||||
@@ -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::jumpStep/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.
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -15,7 +15,7 @@ deprecated class CallContext extends TaintTrackingContext {
|
||||
)
|
||||
)
|
||||
or
|
||||
this.isTop()
|
||||
this.isTop() and exists(s)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -180,7 +180,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 +561,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,7 +608,7 @@ 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;
|
||||
|
||||
@@ -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]
|
||||
@@ -500,9 +511,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 +529,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 +551,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 +560,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 +571,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 +582,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 +617,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 +626,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 +641,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 +666,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 +689,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 +729,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 +742,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 +761,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 +773,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 +799,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 {
|
||||
|
||||
@@ -1862,7 +1862,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.
|
||||
*
|
||||
@@ -1873,7 +1874,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))
|
||||
}
|
||||
@@ -1973,7 +1974,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 { }
|
||||
@@ -2295,4 +2296,22 @@ module PrivateDjango {
|
||||
|
||||
override string getMimetypeDefault() { none() }
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Logging
|
||||
// ---------------------------------------------------------------------------
|
||||
/**
|
||||
* A standard Python logger instance from Django.
|
||||
* see https://github.com/django/django/blob/stable/4.0.x/django/utils/log.py#L11
|
||||
*/
|
||||
private class DjangoLogger extends Stdlib::Logger::InstanceSource {
|
||||
DjangoLogger() {
|
||||
this =
|
||||
API::moduleImport("django")
|
||||
.getMember("utils")
|
||||
.getMember("log")
|
||||
.getMember("request_logger")
|
||||
.getAnImmediateUse()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.frameworks.Werkzeug
|
||||
private import semmle.python.frameworks.Stdlib
|
||||
private import semmle.python.ApiGraphs
|
||||
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
|
||||
private import semmle.python.security.dataflow.PathInjectionCustomizations
|
||||
@@ -192,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. */
|
||||
@@ -217,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() {
|
||||
@@ -298,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 |
|
||||
@@ -569,4 +570,18 @@ module Flask {
|
||||
result in [this.getArg(0), this.getArgByName("filename_or_fp")]
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Logging
|
||||
// ---------------------------------------------------------------------------
|
||||
/**
|
||||
* A Flask application provides a standard Python logger via the `logger` attribute.
|
||||
*
|
||||
* See
|
||||
* - https://flask.palletsprojects.com/en/2.0.x/api/#flask.Flask.logger
|
||||
* - https://flask.palletsprojects.com/en/2.0.x/logging/
|
||||
*/
|
||||
private class FlaskLogger extends Stdlib::Logger::InstanceSource {
|
||||
FlaskLogger() { this = FlaskApp::instance().getMember("logger").getAnImmediateUse() }
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
75
python/ql/lib/semmle/python/frameworks/Ldap.qll
Normal file
75
python/ql/lib/semmle/python/frameworks/Ldap.qll
Normal file
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `python-ldap` PyPI package (imported as `ldap`).
|
||||
* See https://www.python-ldap.org/en/python-ldap-3.3.0/index.html
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
/**
|
||||
* Provides models for the `python-ldap` PyPI package (imported as `ldap`).
|
||||
*
|
||||
* See https://www.python-ldap.org/en/python-ldap-3.3.0/index.html
|
||||
*/
|
||||
private module Ldap {
|
||||
/**
|
||||
* The execution of an `ldap` query.
|
||||
*
|
||||
* See https://www.python-ldap.org/en/python-ldap-3.3.0/reference/ldap.html#functions
|
||||
*/
|
||||
private class LdapQueryExecution extends DataFlow::CallCfgNode, LDAP::LdapExecution::Range {
|
||||
LdapQueryExecution() {
|
||||
this =
|
||||
API::moduleImport("ldap")
|
||||
.getMember("initialize")
|
||||
.getReturn()
|
||||
.getMember(["search", "search_s", "search_st", "search_ext", "search_ext_s"])
|
||||
.getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getFilter() {
|
||||
result in [this.getArg(2), this.getArgByName("filterstr")]
|
||||
}
|
||||
|
||||
override DataFlow::Node getBaseDn() { result in [this.getArg(0), this.getArgByName("base")] }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `ldap.dn.escape_dn_chars`.
|
||||
*
|
||||
* See https://github.com/python-ldap/python-ldap/blob/7ce471e238cdd9a4dd8d17baccd1c9e05e6f894a/Lib/ldap/dn.py#L17
|
||||
*/
|
||||
private class LdapEscapeDnCall extends DataFlow::CallCfgNode, Escaping::Range {
|
||||
LdapEscapeDnCall() {
|
||||
this = API::moduleImport("ldap").getMember("dn").getMember("escape_dn_chars").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("s")] }
|
||||
|
||||
override DataFlow::Node getOutput() { result = this }
|
||||
|
||||
override string getKind() { result = Escaping::getLdapDnKind() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `ldap.filter.escape_filter_chars`.
|
||||
*
|
||||
* See https://www.python-ldap.org/en/python-ldap-3.3.0/reference/ldap-filter.html#ldap.filter.escape_filter_chars
|
||||
*/
|
||||
private class LdapEscapeFilterCall extends DataFlow::CallCfgNode, Escaping::Range {
|
||||
LdapEscapeFilterCall() {
|
||||
this =
|
||||
API::moduleImport("ldap").getMember("filter").getMember("escape_filter_chars").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAnInput() {
|
||||
result in [this.getArg(0), this.getArgByName("assertion_value")]
|
||||
}
|
||||
|
||||
override DataFlow::Node getOutput() { result = this }
|
||||
|
||||
override string getKind() { result = Escaping::getLdapFilterKind() }
|
||||
}
|
||||
}
|
||||
80
python/ql/lib/semmle/python/frameworks/Ldap3.qll
Normal file
80
python/ql/lib/semmle/python/frameworks/Ldap3.qll
Normal file
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the `ldap3` PyPI package
|
||||
* See https://pypi.org/project/ldap3/
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
/**
|
||||
* Provides models for the `ldap3` PyPI package
|
||||
*
|
||||
* See https://pypi.org/project/ldap3/
|
||||
*/
|
||||
private module Ldap3 {
|
||||
/** The execution of an `ldap` query. */
|
||||
private class LdapQueryExecution extends DataFlow::CallCfgNode, LDAP::LdapExecution::Range {
|
||||
LdapQueryExecution() {
|
||||
this =
|
||||
API::moduleImport("ldap3")
|
||||
.getMember("Connection")
|
||||
.getReturn()
|
||||
.getMember("search")
|
||||
.getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getFilter() {
|
||||
result in [this.getArg(1), this.getArgByName("search_filter")]
|
||||
}
|
||||
|
||||
override DataFlow::Node getBaseDn() {
|
||||
result in [this.getArg(0), this.getArgByName("search_base")]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `ldap3.utils.dn.escape_rdn`.
|
||||
*
|
||||
* See https://github.com/cannatag/ldap3/blob/4d33166f0869b929f59c6e6825a1b9505eb99967/ldap3/utils/dn.py#L390
|
||||
*/
|
||||
private class LdapEscapeDnCall extends DataFlow::CallCfgNode, Escaping::Range {
|
||||
LdapEscapeDnCall() {
|
||||
this =
|
||||
API::moduleImport("ldap3")
|
||||
.getMember("utils")
|
||||
.getMember("dn")
|
||||
.getMember("escape_rdn")
|
||||
.getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("rdn")] }
|
||||
|
||||
override DataFlow::Node getOutput() { result = this }
|
||||
|
||||
override string getKind() { result = Escaping::getLdapDnKind() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `ldap3.utils.conv.escape_filter_chars`.
|
||||
*
|
||||
* See https://github.com/cannatag/ldap3/blob/4d33166f0869b929f59c6e6825a1b9505eb99967/ldap3/utils/conv.py#L91
|
||||
*/
|
||||
private class LdapEscapeFilterCall extends DataFlow::CallCfgNode, Escaping::Range {
|
||||
LdapEscapeFilterCall() {
|
||||
this =
|
||||
API::moduleImport("ldap3")
|
||||
.getMember("utils")
|
||||
.getMember("conv")
|
||||
.getMember("escape_filter_chars")
|
||||
.getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("text")] }
|
||||
|
||||
override DataFlow::Node getOutput() { result = this }
|
||||
|
||||
override string getKind() { result = Escaping::getLdapFilterKind() }
|
||||
}
|
||||
}
|
||||
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") }
|
||||
}
|
||||
|
||||
@@ -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") }
|
||||
}
|
||||
|
||||
@@ -15,7 +15,10 @@ private import semmle.python.frameworks.PEP249
|
||||
* See https://pypi.org/project/PyMySQL/
|
||||
*/
|
||||
private module PyMySQL {
|
||||
/** PyMySQL implements PEP 249, providing ways to execute SQL statements against a database. */
|
||||
/**
|
||||
* 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
@@ -237,6 +240,54 @@ module Stdlib {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// logging
|
||||
// ---------------------------------------------------------------------------
|
||||
/**
|
||||
* Provides models for the `logging.Logger` class and subclasses.
|
||||
*
|
||||
* See https://docs.python.org/3.9/library/logging.html#logging.Logger.
|
||||
*/
|
||||
module Logger {
|
||||
/** Gets a reference to the `logging.Logger` class or any subclass. */
|
||||
private API::Node subclassRef() {
|
||||
result = API::moduleImport("logging").getMember("Logger").getASubclass*()
|
||||
}
|
||||
|
||||
/**
|
||||
* A source of instances of `logging.Logger`, 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 `Logger::instance()` to get references to instances of `logging.Logger`.
|
||||
*/
|
||||
abstract class InstanceSource extends DataFlow::LocalSourceNode { }
|
||||
|
||||
/** A direct instantiation of `logging.Logger`. */
|
||||
private class ClassInstantiation extends InstanceSource, DataFlow::CfgNode {
|
||||
ClassInstantiation() {
|
||||
this = subclassRef().getACall()
|
||||
or
|
||||
this = API::moduleImport("logging").getMember("root").getAnImmediateUse()
|
||||
or
|
||||
this = API::moduleImport("logging").getMember("getLogger").getACall()
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `logging.Logger`. */
|
||||
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result instanceof InstanceSource
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `logging.Logger`. */
|
||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2290,7 +2341,8 @@ private module StdlibPrivate {
|
||||
// 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
|
||||
*/
|
||||
@@ -2642,27 +2694,6 @@ private module StdlibPrivate {
|
||||
// ---------------------------------------------------------------------------
|
||||
// logging
|
||||
// ---------------------------------------------------------------------------
|
||||
/**
|
||||
* Provides models for the `logging.Logger` class and subclasses.
|
||||
*
|
||||
* See https://docs.python.org/3.9/library/logging.html#logging.Logger.
|
||||
*/
|
||||
module Logger {
|
||||
/** Gets a reference to the `logging.Logger` class or any subclass. */
|
||||
API::Node subclassRef() {
|
||||
result = API::moduleImport("logging").getMember("Logger").getASubclass*()
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `logging.Logger` or any subclass. */
|
||||
API::Node instance() {
|
||||
result = subclassRef().getReturn()
|
||||
or
|
||||
result = API::moduleImport("logging").getMember("root")
|
||||
or
|
||||
result = API::moduleImport("logging").getMember("getLogger").getReturn()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to one of the logging methods from `logging` or on a `logging.Logger`
|
||||
* subclass.
|
||||
@@ -2683,14 +2714,14 @@ private module StdlibPrivate {
|
||||
method = "log" and
|
||||
msgIndex = 1
|
||||
|
|
||||
this = Logger::instance().getMember(method).getACall()
|
||||
this.(DataFlow::MethodCallNode).calls(Stdlib::Logger::instance(), method)
|
||||
or
|
||||
this = API::moduleImport("logging").getMember(method).getACall()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAnInput() {
|
||||
result = this.getArgByName("msg")
|
||||
result = this.getArgByName(["msg", "extra"])
|
||||
or
|
||||
result = this.getArg(any(int i | i >= msgIndex))
|
||||
}
|
||||
@@ -2809,6 +2840,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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -102,7 +102,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))
|
||||
}
|
||||
@@ -354,7 +354,7 @@ 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() |
|
||||
result in [call.getArg(0), call.getArgByName("handlers")]
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
@@ -124,7 +124,7 @@ int version_tuple_compare(Object t) {
|
||||
predicate baseless_is_new_style(ClassObject cls) {
|
||||
cls.isBuiltin()
|
||||
or
|
||||
major_version() = 3
|
||||
major_version() = 3 and exists(cls)
|
||||
or
|
||||
exists(cls.declaredMetaClass())
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 */
|
||||
@@ -1076,7 +1076,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 +1429,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 +1497,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 +1529,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 +1553,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 +1585,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 +1608,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 +1636,7 @@ module Expressions {
|
||||
callable != ObjectInternal::builtin("hasattr") and
|
||||
callable.isClass() = false and
|
||||
value = ObjectInternal::unknown()
|
||||
) and
|
||||
origin = call
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
@@ -1585,11 +1651,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 +1880,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 +2140,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 +2241,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 +2286,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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -427,6 +427,7 @@ abstract class RegexString extends Expr {
|
||||
}
|
||||
|
||||
predicate normalCharacter(int start, int end) {
|
||||
end = start + 1 and
|
||||
this.character(start, end) and
|
||||
not this.specialCharacter(start, end, _)
|
||||
}
|
||||
@@ -446,6 +447,49 @@ abstract class RegexString extends Expr {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the range [start:end) consists of only 'normal' characters.
|
||||
*/
|
||||
predicate normalCharacterSequence(int start, int end) {
|
||||
// a normal character inside a character set is interpreted on its own
|
||||
this.normalCharacter(start, end) and
|
||||
this.inCharSet(start)
|
||||
or
|
||||
// a maximal run of normal characters is considered as one constant
|
||||
exists(int s, int e |
|
||||
e = max(int i | this.normalCharacterRun(s, i)) and
|
||||
not this.inCharSet(s)
|
||||
|
|
||||
// 'abc' can be considered one constant, but
|
||||
// 'abc+' has to be broken up into 'ab' and 'c+',
|
||||
// as the qualifier only applies to 'c'.
|
||||
if this.qualifier(e, _, _, _)
|
||||
then
|
||||
end = e and start = e - 1
|
||||
or
|
||||
end = e - 1 and start = s and start < end
|
||||
else (
|
||||
end = e and
|
||||
start = s
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate normalCharacterRun(int start, int end) {
|
||||
(
|
||||
this.normalCharacterRun(start, end - 1)
|
||||
or
|
||||
start = end - 1 and not this.normalCharacter(start - 1, start)
|
||||
) and
|
||||
this.normalCharacter(end - 1, end)
|
||||
}
|
||||
|
||||
private predicate characterItem(int start, int end) {
|
||||
this.normalCharacterSequence(start, end) or
|
||||
this.escapedCharacter(start, end) or
|
||||
this.specialCharacter(start, end, _)
|
||||
}
|
||||
|
||||
/** Whether the text in the range start,end is a group */
|
||||
predicate group(int start, int end) {
|
||||
this.groupContents(start, end, _, _)
|
||||
@@ -717,7 +761,7 @@ abstract class RegexString extends Expr {
|
||||
string getBackrefName(int start, int end) { this.named_backreference(start, end, result) }
|
||||
|
||||
private predicate baseItem(int start, int end) {
|
||||
this.character(start, end) and
|
||||
this.characterItem(start, end) and
|
||||
not exists(int x, int y | this.charSet(x, y) and x <= start and y >= end)
|
||||
or
|
||||
this.group(start, end)
|
||||
@@ -837,14 +881,14 @@ abstract class RegexString extends Expr {
|
||||
}
|
||||
|
||||
private predicate item_start(int start) {
|
||||
this.character(start, _) or
|
||||
this.characterItem(start, _) or
|
||||
this.isGroupStart(start) or
|
||||
this.charSet(start, _) or
|
||||
this.backreference(start, _)
|
||||
}
|
||||
|
||||
private predicate item_end(int end) {
|
||||
this.character(_, end)
|
||||
this.characterItem(_, end)
|
||||
or
|
||||
exists(int endm1 | this.isGroupEnd(endm1) and end = endm1 + 1)
|
||||
or
|
||||
@@ -953,7 +997,7 @@ abstract class RegexString extends Expr {
|
||||
*/
|
||||
predicate firstItem(int start, int end) {
|
||||
(
|
||||
this.character(start, end)
|
||||
this.characterItem(start, end)
|
||||
or
|
||||
this.qualifiedItem(start, end, _, _)
|
||||
or
|
||||
@@ -968,7 +1012,7 @@ abstract class RegexString extends Expr {
|
||||
*/
|
||||
predicate lastItem(int start, int end) {
|
||||
(
|
||||
this.character(start, end)
|
||||
this.characterItem(start, end)
|
||||
or
|
||||
this.qualifiedItem(start, end, _, _)
|
||||
or
|
||||
|
||||
@@ -4,7 +4,7 @@ import semmle.python.security.SensitiveData
|
||||
import semmle.python.dataflow.Files
|
||||
import semmle.python.web.Http
|
||||
|
||||
module ClearTextStorage {
|
||||
deprecated module ClearTextStorage {
|
||||
abstract class Sink extends TaintSink {
|
||||
override predicate sinks(TaintKind kind) { kind instanceof SensitiveData }
|
||||
}
|
||||
@@ -26,7 +26,7 @@ module ClearTextStorage {
|
||||
}
|
||||
}
|
||||
|
||||
module ClearTextLogging {
|
||||
deprecated module ClearTextLogging {
|
||||
abstract class Sink extends TaintSink {
|
||||
override predicate sinks(TaintKind kind) { kind instanceof SensitiveData }
|
||||
}
|
||||
|
||||
@@ -3,12 +3,12 @@ import semmle.python.dataflow.TaintTracking
|
||||
private import semmle.python.security.SensitiveData
|
||||
private import semmle.crypto.Crypto as CryptoLib
|
||||
|
||||
abstract class WeakCryptoSink extends TaintSink {
|
||||
abstract deprecated class WeakCryptoSink extends TaintSink {
|
||||
override predicate sinks(TaintKind taint) { taint instanceof SensitiveData }
|
||||
}
|
||||
|
||||
/** Modeling the 'pycrypto' package https://github.com/dlitz/pycrypto (latest release 2013) */
|
||||
module Pycrypto {
|
||||
deprecated module Pycrypto {
|
||||
ModuleValue cipher(string name) { result = Module::named("Crypto.Cipher").attr(name) }
|
||||
|
||||
class CipherInstance extends TaintKind {
|
||||
@@ -58,7 +58,7 @@ module Pycrypto {
|
||||
}
|
||||
}
|
||||
|
||||
module Cryptography {
|
||||
deprecated module Cryptography {
|
||||
ModuleValue ciphers() {
|
||||
result = Module::named("cryptography.hazmat.primitives.ciphers") and
|
||||
result.isPackage()
|
||||
@@ -128,7 +128,7 @@ module Cryptography {
|
||||
}
|
||||
}
|
||||
|
||||
private class CipherConfig extends TaintTracking::Configuration {
|
||||
deprecated private class CipherConfig extends TaintTracking::Configuration {
|
||||
CipherConfig() { this = "Crypto cipher config" }
|
||||
|
||||
override predicate isSource(TaintTracking::Source source) {
|
||||
|
||||
@@ -7,13 +7,15 @@ import python
|
||||
import semmle.python.dataflow.TaintTracking
|
||||
import semmle.python.security.strings.Basic
|
||||
|
||||
private Value traceback_function(string name) { result = Module::named("traceback").attr(name) }
|
||||
deprecated private Value traceback_function(string name) {
|
||||
result = Module::named("traceback").attr(name)
|
||||
}
|
||||
|
||||
/**
|
||||
* This represents information relating to an exception, for instance the
|
||||
* message, arguments or parts of the exception traceback.
|
||||
*/
|
||||
class ExceptionInfo extends StringKind {
|
||||
deprecated class ExceptionInfo extends StringKind {
|
||||
ExceptionInfo() { this = "exception.info" }
|
||||
|
||||
override string repr() { result = "exception info" }
|
||||
@@ -23,12 +25,12 @@ class ExceptionInfo extends StringKind {
|
||||
* A class representing sources of information about
|
||||
* execution state exposed in tracebacks and the like.
|
||||
*/
|
||||
abstract class ErrorInfoSource extends TaintSource { }
|
||||
abstract deprecated class ErrorInfoSource extends TaintSource { }
|
||||
|
||||
/**
|
||||
* This kind represents exceptions themselves.
|
||||
*/
|
||||
class ExceptionKind extends TaintKind {
|
||||
deprecated class ExceptionKind extends TaintKind {
|
||||
ExceptionKind() { this = "exception.kind" }
|
||||
|
||||
override string repr() { result = "exception" }
|
||||
@@ -44,7 +46,7 @@ class ExceptionKind extends TaintKind {
|
||||
* A source of exception objects, either explicitly created, or captured by an
|
||||
* `except` statement.
|
||||
*/
|
||||
class ExceptionSource extends ErrorInfoSource {
|
||||
deprecated class ExceptionSource extends ErrorInfoSource {
|
||||
ExceptionSource() {
|
||||
exists(ClassValue cls |
|
||||
cls.getASuperType() = ClassValue::baseException() and
|
||||
@@ -63,7 +65,7 @@ class ExceptionSource extends ErrorInfoSource {
|
||||
* Represents a sequence of pieces of information relating to an exception,
|
||||
* for instance the contents of the `args` attribute, or the stack trace.
|
||||
*/
|
||||
class ExceptionInfoSequence extends SequenceKind {
|
||||
deprecated class ExceptionInfoSequence extends SequenceKind {
|
||||
ExceptionInfoSequence() { this.getItem() instanceof ExceptionInfo }
|
||||
}
|
||||
|
||||
@@ -71,7 +73,7 @@ class ExceptionInfoSequence extends SequenceKind {
|
||||
* Represents calls to functions in the `traceback` module that return
|
||||
* sequences of exception information.
|
||||
*/
|
||||
class CallToTracebackFunction extends ErrorInfoSource {
|
||||
deprecated class CallToTracebackFunction extends ErrorInfoSource {
|
||||
CallToTracebackFunction() {
|
||||
exists(string name |
|
||||
name in [
|
||||
@@ -92,7 +94,7 @@ class CallToTracebackFunction extends ErrorInfoSource {
|
||||
* Represents calls to functions in the `traceback` module that return a single
|
||||
* string of information about an exception.
|
||||
*/
|
||||
class FormattedTracebackSource extends ErrorInfoSource {
|
||||
deprecated class FormattedTracebackSource extends ErrorInfoSource {
|
||||
FormattedTracebackSource() { this = traceback_function("format_exc").getACall() }
|
||||
|
||||
override string toString() { result = "exception.info.source" }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import semmle.python.dataflow.Implementation
|
||||
|
||||
module TaintTrackingPaths {
|
||||
deprecated module TaintTrackingPaths {
|
||||
predicate edge(TaintTrackingNode src, TaintTrackingNode dest, string label) {
|
||||
exists(TaintTrackingNode source, TaintTrackingNode sink |
|
||||
source.getConfiguration().hasFlowPath(source, sink) and
|
||||
@@ -11,6 +11,6 @@ module TaintTrackingPaths {
|
||||
}
|
||||
}
|
||||
|
||||
query predicate edges(TaintTrackingNode fromnode, TaintTrackingNode tonode) {
|
||||
deprecated query predicate edges(TaintTrackingNode fromnode, TaintTrackingNode tonode) {
|
||||
TaintTrackingPaths::edge(fromnode, tonode, _)
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import semmle.python.web.HttpRequest
|
||||
import semmle.python.security.internal.SensitiveDataHeuristics
|
||||
private import HeuristicNames
|
||||
|
||||
abstract class SensitiveData extends TaintKind {
|
||||
abstract deprecated class SensitiveData extends TaintKind {
|
||||
bindingset[this]
|
||||
SensitiveData() { this = this }
|
||||
|
||||
@@ -23,7 +23,7 @@ abstract class SensitiveData extends TaintKind {
|
||||
abstract SensitiveDataClassification getClassification();
|
||||
}
|
||||
|
||||
module SensitiveData {
|
||||
deprecated module SensitiveData {
|
||||
class Secret extends SensitiveData {
|
||||
Secret() { this = "sensitive.data.secret" }
|
||||
|
||||
@@ -115,4 +115,4 @@ module SensitiveData {
|
||||
}
|
||||
|
||||
//Backwards compatibility
|
||||
class SensitiveDataSource = SensitiveData::Source;
|
||||
deprecated class SensitiveDataSource = SensitiveData::Source;
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Provides taint-tracking configurations for detecting LDAP injection vulnerabilities
|
||||
*
|
||||
* Note, for performance reasons: only import this file if
|
||||
* `LdapInjection::Configuration` is needed, otherwise
|
||||
* `LdapInjectionCustomizations` should be imported instead.
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.Concepts
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
import semmle.python.dataflow.new.RemoteFlowSources
|
||||
|
||||
/**
|
||||
* Provides aint-tracking configurations for detecting LDAP injection vulnerabilities.class
|
||||
*
|
||||
* Two configurations are provided. One is for detecting LDAP injection
|
||||
* via the distinguished name (DN). The other is for detecting LDAP injection
|
||||
* via the filter. These require different escapings.
|
||||
*/
|
||||
module LdapInjection {
|
||||
import LdapInjectionCustomizations::LdapInjection
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for detecting LDAP injection vulnerabilities
|
||||
* via the distinguished name (DN) parameter of an LDAP search.
|
||||
*/
|
||||
class DnConfiguration extends TaintTracking::Configuration {
|
||||
DnConfiguration() { this = "LdapDnInjection" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof DnSink }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof DnSanitizer }
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof DnSanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for detecting LDAP injection vulnerabilities
|
||||
* via the filter parameter of an LDAP search.
|
||||
*/
|
||||
class FilterConfiguration extends TaintTracking::Configuration {
|
||||
FilterConfiguration() { this = "LdapFilterInjection" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof FilterSink }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof FilterSanitizer }
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof FilterSanitizerGuard
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for detecting
|
||||
* "ldap injection"
|
||||
* vulnerabilities, as well as extension points for adding your own.
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.dataflow.new.BarrierGuards
|
||||
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for detecting
|
||||
* "ldap injection"
|
||||
* vulnerabilities, as well as extension points for adding your own.
|
||||
*/
|
||||
module LdapInjection {
|
||||
/**
|
||||
* A data flow source for "ldap injection" vulnerabilities.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow sink for "ldap injection" vulnerabilities.
|
||||
*/
|
||||
abstract class DnSink extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow sink for "ldap injection" vulnerabilities.
|
||||
*/
|
||||
abstract class FilterSink extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A sanitizer for "ldap injection" vulnerabilities.
|
||||
*/
|
||||
abstract class DnSanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A sanitizer for "ldap injection" vulnerabilities.
|
||||
*/
|
||||
abstract class FilterSanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A sanitizer guard for "ldap injection" vulnerabilities.
|
||||
*/
|
||||
abstract class DnSanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A sanitizer guard for "ldap injection" vulnerabilities.
|
||||
*/
|
||||
abstract class FilterSanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A source of remote user input, considered as a flow source.
|
||||
*/
|
||||
class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { }
|
||||
|
||||
/**
|
||||
* A logging operation, considered as a flow sink.
|
||||
*/
|
||||
class LdapExecutionAsDnSink extends DnSink {
|
||||
LdapExecutionAsDnSink() { this = any(LDAP::LdapExecution ldap).getBaseDn() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A logging operation, considered as a flow sink.
|
||||
*/
|
||||
class LdapExecutionAsFilterSink extends FilterSink {
|
||||
LdapExecutionAsFilterSink() { this = any(LDAP::LdapExecution ldap).getFilter() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A comparison with a constant string, considered as a sanitizer-guard.
|
||||
*/
|
||||
class StringConstCompareAsDnSanitizerGuard extends DnSanitizerGuard, StringConstCompare { }
|
||||
|
||||
/**
|
||||
* A comparison with a constant string, considered as a sanitizer-guard.
|
||||
*/
|
||||
class StringConstCompareAsFilterSanitizerGuard extends FilterSanitizerGuard, StringConstCompare {
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to replace line breaks functions as a sanitizer.
|
||||
*/
|
||||
class LdapDnEscapingSanitizer extends DnSanitizer, DataFlow::CallCfgNode {
|
||||
LdapDnEscapingSanitizer() { this = any(LdapDnEscaping ldapDnEsc).getOutput() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to replace line breaks functions as a sanitizer.
|
||||
*/
|
||||
class LdapFilterEscapingSanitizer extends FilterSanitizer, DataFlow::CallCfgNode {
|
||||
LdapFilterEscapingSanitizer() { this = any(LdapFilterEscaping ldapDnEsc).getOutput() }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Provides a taint-tracking configuration for tracking untrusted user input used in log entries.
|
||||
*
|
||||
* Note, for performance reasons: only import this file if
|
||||
* `LogInjection::Configuration` is needed, otherwise
|
||||
* `LogInjectionCustomizations` should be imported instead.
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
|
||||
/**
|
||||
* Provides a taint-tracking configuration for tracking untrusted user input used in log entries.
|
||||
*/
|
||||
module LogInjection {
|
||||
import LogInjectionCustomizations::LogInjection
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for tracking untrusted user input used in log entries.
|
||||
*/
|
||||
class Configuration extends TaintTracking::Configuration {
|
||||
Configuration() { this = "LogInjection" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for detecting
|
||||
* "log injection"
|
||||
* vulnerabilities, as well as extension points for adding your own.
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.dataflow.new.BarrierGuards
|
||||
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for detecting
|
||||
* "log injection"
|
||||
* vulnerabilities, as well as extension points for adding your own.
|
||||
*/
|
||||
module LogInjection {
|
||||
/**
|
||||
* A data flow source for "log injection" vulnerabilities.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow sink for "log injection" vulnerabilities.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A sanitizer for "log injection" vulnerabilities.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A sanitizer guard for "log 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 logging operation, considered as a flow sink.
|
||||
*/
|
||||
class LoggingAsSink extends Sink {
|
||||
LoggingAsSink() { this = any(Logging write).getAnInput() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A comparison with a constant string, considered as a sanitizer-guard.
|
||||
*/
|
||||
class StringConstCompareAsSanitizerGuard extends SanitizerGuard, StringConstCompare { }
|
||||
|
||||
/**
|
||||
* A call to replace line breaks, considered as a sanitizer.
|
||||
*/
|
||||
class ReplaceLineBreaksSanitizer extends Sanitizer, DataFlow::CallCfgNode {
|
||||
// Note: This sanitizer is not 100% accurate, since:
|
||||
// - we do not check that all kinds of line breaks are replaced
|
||||
// - we do not check that one kind of line breaks is not replaced by another
|
||||
//
|
||||
// However, we lack a simple way to do better, and the query would likely
|
||||
// be too noisy without this.
|
||||
//
|
||||
// TODO: Consider rewriting using flow states.
|
||||
ReplaceLineBreaksSanitizer() {
|
||||
this.getFunction().(DataFlow::AttrRead).getAttributeName() = "replace" and
|
||||
this.getArg(0).asExpr().(StrConst).getText() in ["\r\n", "\n"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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() }
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import python
|
||||
import semmle.python.security.strings.Basic
|
||||
|
||||
/** Assume that taint flows from argument to result for *any* call */
|
||||
class AnyCallStringFlow extends DataFlowExtension::DataFlowNode {
|
||||
deprecated class AnyCallStringFlow extends DataFlowExtension::DataFlowNode {
|
||||
AnyCallStringFlow() { any(CallNode call).getAnArg() = this }
|
||||
|
||||
override ControlFlowNode getASuccessorNode() { result.(CallNode).getAnArg() = this }
|
||||
|
||||
@@ -11,18 +11,18 @@ import semmle.python.dataflow.TaintTracking
|
||||
import semmle.python.security.strings.Untrusted
|
||||
|
||||
/** Abstract taint sink that is potentially vulnerable to malicious shell commands. */
|
||||
abstract class CommandSink extends TaintSink { }
|
||||
abstract deprecated class CommandSink extends TaintSink { }
|
||||
|
||||
private ModuleObject osOrPopenModule() { result.getName() = ["os", "popen2"] }
|
||||
deprecated private ModuleObject osOrPopenModule() { result.getName() = ["os", "popen2"] }
|
||||
|
||||
private Object makeOsCall() {
|
||||
deprecated private Object makeOsCall() {
|
||||
exists(string name | result = ModuleObject::named("subprocess").attr(name) |
|
||||
name = ["Popen", "call", "check_call", "check_output", "run"]
|
||||
)
|
||||
}
|
||||
|
||||
/**Special case for first element in sequence. */
|
||||
class FirstElementKind extends TaintKind {
|
||||
deprecated class FirstElementKind extends TaintKind {
|
||||
FirstElementKind() { this = "sequence[" + any(ExternalStringKind key) + "][0]" }
|
||||
|
||||
override string repr() { result = "first item in sequence of " + this.getItem().repr() }
|
||||
@@ -31,7 +31,7 @@ class FirstElementKind extends TaintKind {
|
||||
ExternalStringKind getItem() { this = "sequence[" + result + "][0]" }
|
||||
}
|
||||
|
||||
class FirstElementFlow extends DataFlowExtension::DataFlowNode {
|
||||
deprecated class FirstElementFlow extends DataFlowExtension::DataFlowNode {
|
||||
FirstElementFlow() { this = any(SequenceNode s).getElement(0) }
|
||||
|
||||
override ControlFlowNode getASuccessorNode(TaintKind fromkind, TaintKind tokind) {
|
||||
@@ -43,7 +43,7 @@ class FirstElementFlow extends DataFlowExtension::DataFlowNode {
|
||||
* A taint sink that is potentially vulnerable to malicious shell commands.
|
||||
* The `vuln` in `subprocess.call(shell=vuln)` and similar calls.
|
||||
*/
|
||||
class ShellCommand extends CommandSink {
|
||||
deprecated class ShellCommand extends CommandSink {
|
||||
override string toString() { result = "shell command" }
|
||||
|
||||
ShellCommand() {
|
||||
@@ -81,7 +81,7 @@ class ShellCommand extends CommandSink {
|
||||
* A taint sink that is potentially vulnerable to malicious shell commands.
|
||||
* The `vuln` in `subprocess.call(vuln, ...)` and similar calls.
|
||||
*/
|
||||
class OsCommandFirstArgument extends CommandSink {
|
||||
deprecated class OsCommandFirstArgument extends CommandSink {
|
||||
override string toString() { result = "OS command first argument" }
|
||||
|
||||
OsCommandFirstArgument() {
|
||||
@@ -111,7 +111,7 @@ class OsCommandFirstArgument extends CommandSink {
|
||||
* A taint sink that is potentially vulnerable to malicious shell commands.
|
||||
* The `vuln` in `invoke.run(vuln, ...)` and similar calls.
|
||||
*/
|
||||
class InvokeRun extends CommandSink {
|
||||
deprecated class InvokeRun extends CommandSink {
|
||||
InvokeRun() {
|
||||
this = Value::named("invoke.run").(FunctionValue).getArgumentForCall(_, 0)
|
||||
or
|
||||
@@ -127,12 +127,12 @@ class InvokeRun extends CommandSink {
|
||||
* Internal TaintKind to track the invoke.Context instance passed to functions
|
||||
* marked with @invoke.task
|
||||
*/
|
||||
private class InvokeContextArg extends TaintKind {
|
||||
deprecated private class InvokeContextArg extends TaintKind {
|
||||
InvokeContextArg() { this = "InvokeContextArg" }
|
||||
}
|
||||
|
||||
/** Internal TaintSource to track the context passed to functions marked with @invoke.task */
|
||||
private class InvokeContextArgSource extends TaintSource {
|
||||
deprecated private class InvokeContextArgSource extends TaintSource {
|
||||
InvokeContextArgSource() {
|
||||
exists(Function f, Expr decorator |
|
||||
count(f.getADecorator()) = 1 and
|
||||
@@ -158,7 +158,7 @@ private class InvokeContextArgSource extends TaintSource {
|
||||
* A taint sink that is potentially vulnerable to malicious shell commands.
|
||||
* The `vuln` in `invoke.Context().run(vuln, ...)` and similar calls.
|
||||
*/
|
||||
class InvokeContextRun extends CommandSink {
|
||||
deprecated class InvokeContextRun extends CommandSink {
|
||||
InvokeContextRun() {
|
||||
exists(CallNode call |
|
||||
any(InvokeContextArg k).taints(call.getFunction().(AttrNode).getObject("run"))
|
||||
@@ -187,7 +187,7 @@ class InvokeContextRun extends CommandSink {
|
||||
* A taint sink that is potentially vulnerable to malicious shell commands.
|
||||
* The `vuln` in `fabric.Group().run(vuln, ...)` and similar calls.
|
||||
*/
|
||||
class FabricGroupRun extends CommandSink {
|
||||
deprecated class FabricGroupRun extends CommandSink {
|
||||
FabricGroupRun() {
|
||||
exists(ClassValue cls |
|
||||
cls.getASuperType() = Value::named("fabric.Group") and
|
||||
@@ -203,7 +203,7 @@ class FabricGroupRun extends CommandSink {
|
||||
// -------------------------------------------------------------------------- //
|
||||
// Modeling of the 'invoke' package and 'fabric' package (v 1.x)
|
||||
// -------------------------------------------------------------------------- //
|
||||
class FabricV1Commands extends CommandSink {
|
||||
deprecated class FabricV1Commands extends CommandSink {
|
||||
FabricV1Commands() {
|
||||
// since `run` and `sudo` are decorated, we can't use FunctionValue's :(
|
||||
exists(CallNode call |
|
||||
@@ -228,7 +228,7 @@ class FabricV1Commands extends CommandSink {
|
||||
* An extension that propagates taint from the arguments of `fabric.api.execute(func, arg0, arg1, ...)`
|
||||
* to the parameters of `func`, since this will call `func(arg0, arg1, ...)`.
|
||||
*/
|
||||
class FabricExecuteExtension extends DataFlowExtension::DataFlowNode {
|
||||
deprecated class FabricExecuteExtension extends DataFlowExtension::DataFlowNode {
|
||||
CallNode call;
|
||||
|
||||
FabricExecuteExtension() {
|
||||
|
||||
@@ -2,7 +2,7 @@ import python
|
||||
import semmle.python.dataflow.TaintTracking
|
||||
|
||||
/** `pickle.loads(untrusted)` vulnerability. */
|
||||
abstract class DeserializationSink extends TaintSink {
|
||||
abstract deprecated class DeserializationSink extends TaintSink {
|
||||
bindingset[this]
|
||||
DeserializationSink() { this = this }
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import semmle.python.security.strings.Untrusted
|
||||
* A taint sink that represents an argument to exec or eval that is vulnerable to malicious input.
|
||||
* The `vuln` in `exec(vuln)` or similar.
|
||||
*/
|
||||
class StringEvaluationNode extends TaintSink {
|
||||
deprecated class StringEvaluationNode extends TaintSink {
|
||||
override string toString() { result = "exec or eval" }
|
||||
|
||||
StringEvaluationNode() {
|
||||
|
||||
@@ -11,13 +11,15 @@ import semmle.python.dataflow.TaintTracking
|
||||
import semmle.python.security.strings.Untrusted
|
||||
import semmle.python.security.injection.Deserialization
|
||||
|
||||
private FunctionObject marshalLoads() { result = ModuleObject::named("marshal").attr("loads") }
|
||||
deprecated private FunctionObject marshalLoads() {
|
||||
result = ModuleObject::named("marshal").attr("loads")
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint sink that is potentially vulnerable to malicious marshaled objects.
|
||||
* The `vuln` in `marshal.loads(vuln)`.
|
||||
*/
|
||||
class UnmarshalingNode extends DeserializationSink {
|
||||
deprecated class UnmarshalingNode extends DeserializationSink {
|
||||
override string toString() { result = "unmarshaling vulnerability" }
|
||||
|
||||
UnmarshalingNode() {
|
||||
|
||||
@@ -6,7 +6,7 @@ import semmle.python.security.strings.Untrusted
|
||||
* Prevents taint flowing through ntpath.normpath()
|
||||
* NormalizedPath below handles that case.
|
||||
*/
|
||||
class PathSanitizer extends Sanitizer {
|
||||
deprecated class PathSanitizer extends Sanitizer {
|
||||
PathSanitizer() { this = "path.sanitizer" }
|
||||
|
||||
override predicate sanitizingNode(TaintKind taint, ControlFlowNode node) {
|
||||
@@ -15,7 +15,7 @@ class PathSanitizer extends Sanitizer {
|
||||
}
|
||||
}
|
||||
|
||||
private FunctionObject abspath() {
|
||||
deprecated private FunctionObject abspath() {
|
||||
exists(ModuleObject os_path | ModuleObject::named("os").attr("path") = os_path |
|
||||
os_path.attr("abspath") = result
|
||||
or
|
||||
@@ -24,18 +24,18 @@ private FunctionObject abspath() {
|
||||
}
|
||||
|
||||
/** A path that has been normalized, but not verified to be safe */
|
||||
class NormalizedPath extends TaintKind {
|
||||
deprecated class NormalizedPath extends TaintKind {
|
||||
NormalizedPath() { this = "normalized.path.injection" }
|
||||
|
||||
override string repr() { result = "normalized path" }
|
||||
}
|
||||
|
||||
private predicate abspath_call(CallNode call, ControlFlowNode arg) {
|
||||
deprecated private predicate abspath_call(CallNode call, ControlFlowNode arg) {
|
||||
call.getFunction().refersTo(abspath()) and
|
||||
arg = call.getArg(0)
|
||||
}
|
||||
|
||||
class AbsPath extends DataFlowExtension::DataFlowNode {
|
||||
deprecated class AbsPath extends DataFlowExtension::DataFlowNode {
|
||||
AbsPath() { abspath_call(_, this) }
|
||||
|
||||
override ControlFlowNode getASuccessorNode(TaintKind fromkind, TaintKind tokind) {
|
||||
@@ -45,7 +45,7 @@ class AbsPath extends DataFlowExtension::DataFlowNode {
|
||||
}
|
||||
}
|
||||
|
||||
class NormalizedPathSanitizer extends Sanitizer {
|
||||
deprecated class NormalizedPathSanitizer extends Sanitizer {
|
||||
NormalizedPathSanitizer() { this = "normalized.path.sanitizer" }
|
||||
|
||||
override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) {
|
||||
@@ -59,7 +59,7 @@ class NormalizedPathSanitizer extends Sanitizer {
|
||||
* A taint sink that is vulnerable to malicious paths.
|
||||
* The `vuln` in `open(vuln)` and similar.
|
||||
*/
|
||||
class OpenNode extends TaintSink {
|
||||
deprecated class OpenNode extends TaintSink {
|
||||
override string toString() { result = "argument to open()" }
|
||||
|
||||
OpenNode() {
|
||||
|
||||
@@ -11,7 +11,7 @@ import semmle.python.dataflow.TaintTracking
|
||||
import semmle.python.security.strings.Untrusted
|
||||
import semmle.python.security.injection.Deserialization
|
||||
|
||||
private ModuleObject pickleModule() {
|
||||
deprecated private ModuleObject pickleModule() {
|
||||
result.getName() = "pickle"
|
||||
or
|
||||
result.getName() = "cPickle"
|
||||
@@ -19,10 +19,10 @@ private ModuleObject pickleModule() {
|
||||
result.getName() = "dill"
|
||||
}
|
||||
|
||||
private FunctionObject pickleLoads() { result = pickleModule().attr("loads") }
|
||||
deprecated private FunctionObject pickleLoads() { result = pickleModule().attr("loads") }
|
||||
|
||||
/** `pickle.loads(untrusted)` vulnerability. */
|
||||
class UnpicklingNode extends DeserializationSink {
|
||||
deprecated class UnpicklingNode extends DeserializationSink {
|
||||
override string toString() { result = "unpickling untrusted data" }
|
||||
|
||||
UnpicklingNode() {
|
||||
|
||||
@@ -11,7 +11,7 @@ import semmle.python.dataflow.TaintTracking
|
||||
import semmle.python.security.strings.Untrusted
|
||||
import semmle.python.security.SQL
|
||||
|
||||
private StringObject first_part(ControlFlowNode command) {
|
||||
deprecated private StringObject first_part(ControlFlowNode command) {
|
||||
command.(BinaryExprNode).getOp() instanceof Add and
|
||||
command.(BinaryExprNode).getLeft().refersTo(result)
|
||||
or
|
||||
@@ -26,7 +26,7 @@ private StringObject first_part(ControlFlowNode command) {
|
||||
}
|
||||
|
||||
/** Holds if `command` appears to be a SQL command string of which `inject` is a part. */
|
||||
predicate probable_sql_command(ControlFlowNode command, ControlFlowNode inject) {
|
||||
deprecated predicate probable_sql_command(ControlFlowNode command, ControlFlowNode inject) {
|
||||
exists(string prefix |
|
||||
inject = command.getAChild*() and
|
||||
first_part(command).getText().regexpMatch(" *" + prefix + ".*")
|
||||
@@ -39,7 +39,7 @@ predicate probable_sql_command(ControlFlowNode command, ControlFlowNode inject)
|
||||
* A taint kind representing a DB cursor.
|
||||
* This will be overridden to provide specific kinds of DB cursor.
|
||||
*/
|
||||
abstract class DbCursor extends TaintKind {
|
||||
abstract deprecated class DbCursor extends TaintKind {
|
||||
bindingset[this]
|
||||
DbCursor() { any() }
|
||||
|
||||
@@ -50,7 +50,7 @@ abstract class DbCursor extends TaintKind {
|
||||
* A part of a string that appears to be a SQL command and is thus
|
||||
* vulnerable to malicious input.
|
||||
*/
|
||||
class SimpleSqlStringInjection extends SqlInjectionSink {
|
||||
deprecated class SimpleSqlStringInjection extends SqlInjectionSink {
|
||||
override string toString() { result = "simple SQL string injection" }
|
||||
|
||||
SimpleSqlStringInjection() { probable_sql_command(_, this) }
|
||||
@@ -62,13 +62,13 @@ class SimpleSqlStringInjection extends SqlInjectionSink {
|
||||
* A taint source representing sources of DB connections.
|
||||
* This will be overridden to provide specific kinds of DB connection sources.
|
||||
*/
|
||||
abstract class DbConnectionSource extends TaintSource { }
|
||||
abstract deprecated class DbConnectionSource extends TaintSource { }
|
||||
|
||||
/**
|
||||
* A taint sink that is vulnerable to malicious SQL queries.
|
||||
* The `vuln` in `db.connection.execute(vuln)` and similar.
|
||||
*/
|
||||
class DbConnectionExecuteArgument extends SqlInjectionSink {
|
||||
deprecated class DbConnectionExecuteArgument extends SqlInjectionSink {
|
||||
override string toString() { result = "db.connection.execute" }
|
||||
|
||||
DbConnectionExecuteArgument() {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user