Merge branch 'main' into ZipSlip

This commit is contained in:
Taus
2022-03-10 13:30:51 +01:00
committed by GitHub
1336 changed files with 124523 additions and 87371 deletions

View File

@@ -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",
},
}

View File

@@ -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

View File

@@ -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.

View File

@@ -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.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The regular expression parser now groups sequences of normal characters. This reduces the number of instances of `RegExpNormalChar`.

View 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).

View 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.

View File

@@ -0,0 +1 @@
## 0.0.9

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.0.8
lastReleaseVersion: 0.0.10

View File

@@ -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

View File

@@ -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.
*/

View File

@@ -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)) }
/**

View File

@@ -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 */

View File

@@ -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();

View File

@@ -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 {

View File

@@ -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_ { }

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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)

View File

@@ -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.
*/

View File

@@ -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)) }

View File

@@ -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) {

View 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 */

View File

@@ -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" }

View File

@@ -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) }

View File

@@ -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() }

View File

@@ -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

View File

@@ -112,6 +112,6 @@ class SpecialMethodCallNode extends PotentialSpecialMethodCallNode {
)
}
/** The method that is called. */
/** Gets the method that is called. */
Value getResolvedSpecialMethod() { result = resolvedSpecialMethod }
}

View File

@@ -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())

View File

@@ -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

View File

@@ -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

View File

@@ -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() {

View File

@@ -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))
}
//--------

View File

@@ -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()
}

View File

@@ -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 |

View File

@@ -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 |

View File

@@ -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.

View File

@@ -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. */

View File

@@ -15,7 +15,7 @@ deprecated class CallContext extends TaintTrackingContext {
)
)
or
this.isTop()
this.isTop() and exists(s)
}
}

View File

@@ -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;

View File

@@ -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" }
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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()
}
}
}
}

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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()
}
}
}

View File

@@ -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() }
}
}

View 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()
}
}
}
}

View 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() }
}
}

View 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() }
}
}

View 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()
}
}
}

View 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" }
}
}

View 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" }
}
}

View File

@@ -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") }
}

View File

@@ -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") }
}

View File

@@ -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") }
}

View 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()
}
}
}
}

View File

@@ -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
// ---------------------------------------------------------------------------

View 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()
}
}
}
}

View 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()
}
}
}

View File

@@ -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")]

View 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()
}
}
}
}

View File

@@ -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.

View File

@@ -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) }

View File

@@ -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.
*/

View File

@@ -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() }

View File

@@ -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()

View File

@@ -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 }

View File

@@ -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 }

View File

@@ -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() }

View File

@@ -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

View File

@@ -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())
}

View File

@@ -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.

View File

@@ -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) {

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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 }
}

View File

@@ -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) {

View File

@@ -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" }

View File

@@ -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, _)
}

View File

@@ -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;

View File

@@ -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
}
}
}

View File

@@ -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() }
}
}

View File

@@ -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
}
}
}

View File

@@ -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"]
}
}
}

View File

@@ -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() }
}
}

View File

@@ -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 }

View File

@@ -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() {

View File

@@ -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 }
}

View File

@@ -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() {

View File

@@ -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() {

View File

@@ -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() {

View File

@@ -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() {

View File

@@ -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