mirror of
https://github.com/github/codeql.git
synced 2025-12-21 19:26:31 +01:00
Merge branch 'main' into refacReDoS
This commit is contained in:
@@ -1,3 +1,26 @@
|
||||
## 0.5.2
|
||||
|
||||
## 0.5.1
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
- The documentation of API graphs (the `API` module) has been expanded, and some of the members predicates of `API::Node`
|
||||
have been renamed as follows:
|
||||
- `getAnImmediateUse` -> `asSource`
|
||||
- `getARhs` -> `asSink`
|
||||
- `getAUse` -> `getAValueReachableFromSource`
|
||||
- `getAValueReachingRhs` -> `getAValueReachingSink`
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Improved modeling of sensitive data sources, so common words like `certain` and `secretary` are no longer considered a certificate and a secret (respectively).
|
||||
|
||||
## 0.5.0
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* The `BarrierGuard` class has been deprecated. Such barriers and sanitizers can now instead be created using the new `BarrierGuard` parameterized module.
|
||||
|
||||
## 0.4.1
|
||||
|
||||
## 0.4.0
|
||||
|
||||
@@ -14,10 +14,13 @@ class Definition extends TLocalDefinition {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = "Definition " + this.getAstNode().getLocation().toString() }
|
||||
|
||||
/** Gets the AST Node associated with this element */
|
||||
AstNode getAstNode() { this = TLocalDefinition(result) }
|
||||
|
||||
/** Gets the Module associated with this element */
|
||||
Module getModule() { result = this.getAstNode().getScope().getEnclosingModule() }
|
||||
|
||||
/** Gets the source location of the AST Node associated with this element */
|
||||
Location getLocation() { result = this.getAstNode().getLocation() }
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
import python
|
||||
import DefinitionTracking
|
||||
import analysis.DefinitionTracking
|
||||
|
||||
external string selectedSourceFile();
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
import python
|
||||
import DefinitionTracking
|
||||
import analysis.DefinitionTracking
|
||||
|
||||
external string selectedSourceFile();
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Change `.getASubclass()` on `API::Node` so it allows to follow subclasses even if the class has a class decorator.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Improved modeling of sensitive data sources, so common words like `certain` and `secretary` are no longer considered a certificate and a secret (respectively).
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
category: deprecated
|
||||
---
|
||||
## 0.5.0
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* The `BarrierGuard` class has been deprecated. Such barriers and sanitizers can now instead be created using the new `BarrierGuard` parameterized module.
|
||||
14
python/ql/lib/change-notes/released/0.5.1.md
Normal file
14
python/ql/lib/change-notes/released/0.5.1.md
Normal file
@@ -0,0 +1,14 @@
|
||||
## 0.5.1
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
- The documentation of API graphs (the `API` module) has been expanded, and some of the members predicates of `API::Node`
|
||||
have been renamed as follows:
|
||||
- `getAnImmediateUse` -> `asSource`
|
||||
- `getARhs` -> `asSink`
|
||||
- `getAUse` -> `getAValueReachableFromSource`
|
||||
- `getAValueReachingRhs` -> `getAValueReachingSink`
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Improved modeling of sensitive data sources, so common words like `certain` and `secretary` are no longer considered a certificate and a secret (respectively).
|
||||
1
python/ql/lib/change-notes/released/0.5.2.md
Normal file
1
python/ql/lib/change-notes/released/0.5.2.md
Normal file
@@ -0,0 +1 @@
|
||||
## 0.5.2
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.4.1
|
||||
lastReleaseVersion: 0.5.2
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/python-all
|
||||
version: 0.5.0-dev
|
||||
version: 0.5.3-dev
|
||||
groups: python
|
||||
dbscheme: semmlecode.python.dbscheme
|
||||
extractor: python
|
||||
|
||||
@@ -12,76 +12,165 @@ import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.internal.CachedStages
|
||||
|
||||
/**
|
||||
* Provides classes and predicates for working with APIs used in a database.
|
||||
* Provides classes and predicates for working with the API boundary between the current
|
||||
* codebase and external libraries.
|
||||
*
|
||||
* See `API::Node` for more in-depth documentation.
|
||||
*/
|
||||
module API {
|
||||
/**
|
||||
* An abstract representation of a definition or use of an API component such as a function
|
||||
* exported by a Python package, or its result.
|
||||
* A node in the API graph, representing a value that has crossed the boundary between this
|
||||
* codebase and an external library (or in general, any external codebase).
|
||||
*
|
||||
* ### Basic usage
|
||||
*
|
||||
* API graphs are typically used to identify "API calls", that is, calls to an external function
|
||||
* whose implementation is not necessarily part of the current codebase.
|
||||
*
|
||||
* The most basic use of API graphs is typically as follows:
|
||||
* 1. Start with `API::moduleImport` for the relevant library.
|
||||
* 2. Follow up with a chain of accessors such as `getMember` describing how to get to the relevant API function.
|
||||
* 3. Map the resulting API graph nodes to data-flow nodes, using `asSource` or `asSink`.
|
||||
*
|
||||
* For example, a simplified way to get the first argument of a call to `json.dumps` would be
|
||||
* ```ql
|
||||
* API::moduleImport("json").getMember("dumps").getParameter(0).asSink()
|
||||
* ```
|
||||
*
|
||||
* The most commonly used accessors are `getMember`, `getParameter`, and `getReturn`.
|
||||
*
|
||||
* ### API graph nodes
|
||||
*
|
||||
* There are two kinds of nodes in the API graphs, distinguished by who is "holding" the value:
|
||||
* - **Use-nodes** represent values held by the current codebase, which came from an external library.
|
||||
* (The current codebase is "using" a value that came from the library).
|
||||
* - **Def-nodes** represent values held by the external library, which came from this codebase.
|
||||
* (The current codebase "defines" the value seen by the library).
|
||||
*
|
||||
* API graph nodes are associated with data-flow nodes in the current codebase.
|
||||
* (API graphs are designed to work when external libraries are not part of the database,
|
||||
* so we do not associate with concrete data-flow nodes from the external library).
|
||||
* - **Use-nodes** are associated with data-flow nodes where a value enters the current codebase,
|
||||
* such as the return value of a call to an external function.
|
||||
* - **Def-nodes** are associated with data-flow nodes where a value leaves the current codebase,
|
||||
* such as an argument passed in a call to an external function.
|
||||
*
|
||||
*
|
||||
* ### Access paths and edge labels
|
||||
*
|
||||
* Nodes in the API graph are associated with a set of access paths, describing a series of operations
|
||||
* that may be performed to obtain that value.
|
||||
*
|
||||
* For example, the access path `API::moduleImport("json").getMember("dumps")` represents the action of
|
||||
* importing `json` and then accessing the member `dumps` on the resulting object.
|
||||
*
|
||||
* Each edge in the graph is labelled by such an "operation". For an edge `A->B`, the type of the `A` node
|
||||
* determines who is performing the operation, and the type of the `B` node determines who ends up holding
|
||||
* the result:
|
||||
* - An edge starting from a use-node describes what the current codebase is doing to a value that
|
||||
* came from a library.
|
||||
* - An edge starting from a def-node describes what the external library might do to a value that
|
||||
* came from the current codebase.
|
||||
* - An edge ending in a use-node means the result ends up in the current codebase (at its associated data-flow node).
|
||||
* - An edge ending in a def-node means the result ends up in external code (its associated data-flow node is
|
||||
* the place where it was "last seen" in the current codebase before flowing out)
|
||||
*
|
||||
* Because the implementation of the external library is not visible, it is not known exactly what operations
|
||||
* it will perform on values that flow there. Instead, the edges starting from a def-node are operations that would
|
||||
* lead to an observable effect within the current codebase; without knowing for certain if the library will actually perform
|
||||
* those operations. (When constructing these edges, we assume the library is somewhat well-behaved).
|
||||
*
|
||||
* For example, given this snippet:
|
||||
* ```python
|
||||
* import foo
|
||||
* foo.bar(lambda x: doSomething(x))
|
||||
* ```
|
||||
* A callback is passed to the external function `foo.bar`. We can't know if `foo.bar` will actually invoke this callback.
|
||||
* But _if_ the library should decide to invoke the callback, then a value will flow into the current codebase via the `x` parameter.
|
||||
* For that reason, an edge is generated representing the argument-passing operation that might be performed by `foo.bar`.
|
||||
* This edge is going from the def-node associated with the callback to the use-node associated with the parameter `x`.
|
||||
*/
|
||||
class Node extends Impl::TApiNode {
|
||||
/**
|
||||
* Gets a data-flow node corresponding to a use of the API component represented by this node.
|
||||
* Gets a data-flow node where this value may flow after entering the current codebase.
|
||||
*
|
||||
* For example, `import re; re.escape` is a use of the `escape` function from the
|
||||
* `re` module, and `import re; re.escape("hello")` is a use of the return of that function.
|
||||
*
|
||||
* This includes indirect uses found via data flow, meaning that in
|
||||
* ```python
|
||||
* def f(x):
|
||||
* pass
|
||||
*
|
||||
* f(obj.foo)
|
||||
* ```
|
||||
* both `obj.foo` and `x` are uses of the `foo` member from `obj`.
|
||||
* This is similar to `asSource()` but additionally includes nodes that are transitively reachable by data flow.
|
||||
* See `asSource()` for examples.
|
||||
*/
|
||||
DataFlow::Node getAUse() {
|
||||
DataFlow::Node getAValueReachableFromSource() {
|
||||
exists(DataFlow::LocalSourceNode src | Impl::use(this, src) |
|
||||
Impl::trackUseNode(src).flowsTo(result)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data-flow node corresponding to the right-hand side of a definition of the API
|
||||
* component represented by this node.
|
||||
* Gets a data-flow node where this value leaves the current codebase and flows into an
|
||||
* external library (or in general, any external codebase).
|
||||
*
|
||||
* For example, in the property write `foo.bar = x`, variable `x` is the the right-hand side
|
||||
* of a write to the `bar` property of `foo`.
|
||||
* Concretely, this is either an argument passed to a call to external code,
|
||||
* or the right-hand side of an attribute write on an object flowing into such a call.
|
||||
*
|
||||
* Note that for parameters, it is the arguments flowing into that parameter that count as
|
||||
* right-hand sides of the definition, not the declaration of the parameter itself.
|
||||
* Consequently, in :
|
||||
* For example:
|
||||
* ```python
|
||||
* from mypkg import foo;
|
||||
* import foo
|
||||
*
|
||||
* # 'x' is matched by API::moduleImport("foo").getMember("bar").getParameter(0).asSink()
|
||||
* foo.bar(x)
|
||||
*
|
||||
* # 'x' is matched by API::moduleImport("foo").getMember("bar").getParameter(0).getMember("prop").asSink()
|
||||
* obj.prop = x
|
||||
* foo.bar(obj);
|
||||
* ```
|
||||
* `x` is the right-hand side of a definition of the first parameter of `bar` from the `mypkg.foo` module.
|
||||
*
|
||||
* This predicate does not include nodes transitively reaching the sink by data flow;
|
||||
* use `getAValueReachingSink` for that.
|
||||
*/
|
||||
DataFlow::Node getARhs() { Impl::rhs(this, result) }
|
||||
DataFlow::Node asSink() { Impl::rhs(this, result) }
|
||||
|
||||
/**
|
||||
* Gets a data-flow node that may interprocedurally flow to the right-hand side of a definition
|
||||
* of the API component represented by this node.
|
||||
* Gets a data-flow node that transitively flows to an external library (or in general, any external codebase).
|
||||
*
|
||||
* This is similar to `asSink()` but additionally includes nodes that transitively reach a sink by data flow.
|
||||
* See `asSink()` for examples.
|
||||
*/
|
||||
DataFlow::Node getAValueReachingRhs() { result = Impl::trackDefNode(this.getARhs()) }
|
||||
DataFlow::Node getAValueReachingSink() { result = Impl::trackDefNode(this.asSink()) }
|
||||
|
||||
/**
|
||||
* Gets an immediate use of the API component represented by this node.
|
||||
* Gets a data-flow node where this value enters the current codebase.
|
||||
*
|
||||
* For example, `import re; re.escape` is a an immediate use of the `escape` member
|
||||
* from the `re` module.
|
||||
* For example:
|
||||
* ```python
|
||||
* # API::moduleImport("re").asSource()
|
||||
* import re
|
||||
*
|
||||
* Unlike `getAUse()`, this predicate only gets the immediate references, not the indirect uses
|
||||
* found via data flow. This means that in `x = re.escape` only `re.escape` is a reference
|
||||
* to the `escape` member of `re`, neither `x` nor any node that `x` flows to is a reference to
|
||||
* this API component.
|
||||
* # API::moduleImport("re").getMember("escape").asSource()
|
||||
* re.escape
|
||||
*
|
||||
* # API::moduleImport("re").getMember("escape").getReturn().asSource()
|
||||
* re.escape()
|
||||
* ```
|
||||
*
|
||||
* This predicate does not include nodes transitively reachable by data flow;
|
||||
* use `getAValueReachableFromSource` for that.
|
||||
*/
|
||||
DataFlow::LocalSourceNode getAnImmediateUse() { Impl::use(this, result) }
|
||||
DataFlow::LocalSourceNode asSource() { Impl::use(this, result) }
|
||||
|
||||
/** DEPRECATED. This predicate has been renamed to `getAValueReachableFromSource()`. */
|
||||
deprecated DataFlow::Node getAUse() { result = this.getAValueReachableFromSource() }
|
||||
|
||||
/** DEPRECATED. This predicate has been renamed to `asSource()`. */
|
||||
deprecated DataFlow::LocalSourceNode getAnImmediateUse() { result = this.asSource() }
|
||||
|
||||
/** DEPRECATED. This predicate has been renamed to `asSink()`. */
|
||||
deprecated DataFlow::Node getARhs() { result = this.asSink() }
|
||||
|
||||
/** DEPRECATED. This predicate has been renamed to `getAValueReachingSink()`. */
|
||||
deprecated DataFlow::Node getAValueReachingRhs() { result = this.getAValueReachingSink() }
|
||||
|
||||
/**
|
||||
* Gets a call to the function represented by this API component.
|
||||
*/
|
||||
CallNode getACall() { result = this.getReturn().getAnImmediateUse() }
|
||||
CallNode getACall() { result = this.getReturn().asSource() }
|
||||
|
||||
/**
|
||||
* Gets a node representing member `m` of this API component.
|
||||
@@ -306,7 +395,7 @@ module API {
|
||||
class CallNode extends DataFlow::CallCfgNode {
|
||||
API::Node callee;
|
||||
|
||||
CallNode() { this = callee.getReturn().getAnImmediateUse() }
|
||||
CallNode() { this = callee.getReturn().asSource() }
|
||||
|
||||
/** Gets the API node for the `i`th parameter of this invocation. */
|
||||
pragma[nomagic]
|
||||
@@ -319,14 +408,14 @@ module API {
|
||||
* Gets an API node where a RHS of the node is the `i`th argument to this call.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private Node getAParameterCandidate(int i) { result.getARhs() = this.getArg(i) }
|
||||
private Node getAParameterCandidate(int i) { result.asSink() = this.getArg(i) }
|
||||
|
||||
/** Gets the API node for a parameter of this invocation. */
|
||||
Node getAParameter() { result = this.getParameter(_) }
|
||||
|
||||
/** Gets the object that this method-call is being called on, if this is a method-call */
|
||||
Node getSelfParameter() {
|
||||
result.getARhs() = this.(DataFlow::MethodCallNode).getObject() and
|
||||
result.asSink() = this.(DataFlow::MethodCallNode).getObject() and
|
||||
result = callee.getSelfParameter()
|
||||
}
|
||||
|
||||
@@ -346,13 +435,13 @@ module API {
|
||||
|
||||
pragma[noinline]
|
||||
private Node getAKeywordParameterCandidate(string name) {
|
||||
result.getARhs() = this.getArgByName(name)
|
||||
result.asSink() = this.getArgByName(name)
|
||||
}
|
||||
|
||||
/** Gets the API node for the return value of this call. */
|
||||
Node getReturn() {
|
||||
result = callee.getReturn() and
|
||||
result.getAnImmediateUse() = this
|
||||
result.asSource() = this
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -591,8 +680,16 @@ module API {
|
||||
or
|
||||
// Subclassing a node
|
||||
lbl = Label::subclass() and
|
||||
exists(DataFlow::Node superclass | pred.flowsTo(superclass) |
|
||||
ref.asExpr().(PY::ClassExpr).getABase() = superclass.asExpr()
|
||||
exists(PY::ClassExpr clsExpr, DataFlow::Node superclass | pred.flowsTo(superclass) |
|
||||
clsExpr.getABase() = superclass.asExpr() and
|
||||
// Potentially a class decorator could do anything, but we assume they are
|
||||
// "benign" and let subclasses edges flow through anyway.
|
||||
// see example in https://github.com/django/django/blob/c2250cfb80e27cdf8d098428824da2800a18cadf/tests/auth_tests/test_views.py#L40-L46
|
||||
(
|
||||
ref.asExpr() = clsExpr
|
||||
or
|
||||
ref.asExpr() = clsExpr.getADecoratorCall()
|
||||
)
|
||||
)
|
||||
or
|
||||
// awaiting
|
||||
|
||||
@@ -1000,11 +1000,22 @@ class RegExpBackRef extends RegExpTerm, TRegExpBackRef {
|
||||
|
||||
/** Gets the capture group this back reference refers to. */
|
||||
RegExpGroup getGroup() {
|
||||
result.getLiteral() = this.getLiteral() and
|
||||
(
|
||||
result.getNumber() = this.getNumber() or
|
||||
result.getName() = this.getName()
|
||||
)
|
||||
this.hasLiteralAndNumber(result.getLiteral(), result.getNumber()) or
|
||||
this.hasLiteralAndName(result.getLiteral(), result.getName())
|
||||
}
|
||||
|
||||
/** Join-order helper for `getGroup`. */
|
||||
pragma[nomagic]
|
||||
private predicate hasLiteralAndNumber(RegExpLiteral literal, int number) {
|
||||
literal = this.getLiteral() and
|
||||
number = this.getNumber()
|
||||
}
|
||||
|
||||
/** Join-order helper for `getGroup`. */
|
||||
pragma[nomagic]
|
||||
private predicate hasLiteralAndName(RegExpLiteral literal, string name) {
|
||||
literal = this.getLiteral() and
|
||||
name = this.getName()
|
||||
}
|
||||
|
||||
override RegExpTerm getChild(int i) { none() }
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -527,16 +527,55 @@ class StarPatternElementNode extends Node, TStarPatternElementNode {
|
||||
override Location getLocation() { result = consumer.getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node that controls whether other nodes are evaluated.
|
||||
*
|
||||
* In the base case, this is the last node of `conditionBlock`, and `flipped` is `false`.
|
||||
* This definition accounts for (short circuting) `and`- and `or`-expressions, as the structure
|
||||
* of basic blocks will reflect their semantics.
|
||||
*
|
||||
* However, in the program
|
||||
* ```python
|
||||
* if not is_safe(path):
|
||||
* return
|
||||
* ```
|
||||
* the last node in the `ConditionBlock` is `not is_safe(path)`.
|
||||
*
|
||||
* We would like to consider also `is_safe(path)` a guard node, albeit with `flipped` being `true`.
|
||||
* Thus we recurse through `not`-expressions.
|
||||
*/
|
||||
ControlFlowNode guardNode(ConditionBlock conditionBlock, boolean flipped) {
|
||||
// Base case: the last node truly does determine which successor is chosen
|
||||
result = conditionBlock.getLastNode() and
|
||||
flipped = false
|
||||
or
|
||||
// Recursive case: if a guard node is a `not`-expression,
|
||||
// the operand is also a guard node, but with inverted polarity.
|
||||
exists(UnaryExprNode notNode |
|
||||
result = notNode.getOperand() and
|
||||
notNode.getNode().getOp() instanceof Not
|
||||
|
|
||||
notNode = guardNode(conditionBlock, flipped.booleanNot())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A node that controls whether other nodes are evaluated.
|
||||
*
|
||||
* The field `flipped` allows us to match `GuardNode`s underneath
|
||||
* `not`-expressions and still choose the appropriate branch.
|
||||
*/
|
||||
class GuardNode extends ControlFlowNode {
|
||||
ConditionBlock conditionBlock;
|
||||
boolean flipped;
|
||||
|
||||
GuardNode() { this = conditionBlock.getLastNode() }
|
||||
GuardNode() { this = guardNode(conditionBlock, flipped) }
|
||||
|
||||
/** Holds if this guard controls block `b` upon evaluating to `branch`. */
|
||||
predicate controlsBlock(BasicBlock b, boolean branch) { conditionBlock.controls(b, branch) }
|
||||
predicate controlsBlock(BasicBlock b, boolean branch) {
|
||||
branch in [true, false] and
|
||||
conditionBlock.controls(b, branch.booleanXor(flipped))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,7 +9,7 @@ class UnitTestClass extends TestScope, Class {
|
||||
testCaseString.matches("%TestCase") and
|
||||
testCaseClass = any(API::Node mod).getMember(testCaseString)
|
||||
|
|
||||
this.getParent() = testCaseClass.getASubclass*().getAnImmediateUse().asExpr()
|
||||
this.getParent() = testCaseClass.getASubclass*().asSource().asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,9 +243,7 @@ module AiohttpWebModel {
|
||||
|
||||
/** A class that has a super-type which is an aiohttp.web View class. */
|
||||
class AiohttpViewClassFromSuperClass extends AiohttpViewClass {
|
||||
AiohttpViewClassFromSuperClass() {
|
||||
this.getParent() = View::subclassRef().getAnImmediateUse().asExpr()
|
||||
}
|
||||
AiohttpViewClassFromSuperClass() { this.getParent() = View::subclassRef().asSource().asExpr() }
|
||||
}
|
||||
|
||||
/** A class that is used in a route-setup, therefore being considered an aiohttp.web View class. */
|
||||
@@ -628,7 +626,8 @@ module AiohttpWebModel {
|
||||
// and just go with the LHS
|
||||
this.asCfgNode() = subscript
|
||||
|
|
||||
subscript.getObject() = aiohttpResponseInstance().getMember("cookies").getAUse().asCfgNode() and
|
||||
subscript.getObject() =
|
||||
aiohttpResponseInstance().getMember("cookies").getAValueReachableFromSource().asCfgNode() and
|
||||
value.asCfgNode() = subscript.(DefinitionNode).getValue() and
|
||||
index.asCfgNode() = subscript.getIndex()
|
||||
)
|
||||
@@ -686,8 +685,8 @@ private module AiohttpClientModel {
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
exists(API::Node param | param = this.getKeywordParameter(["ssl", "verify_ssl"]) |
|
||||
disablingNode = param.getARhs() and
|
||||
argumentOrigin = param.getAValueReachingRhs() and
|
||||
disablingNode = param.asSink() and
|
||||
argumentOrigin = param.getAValueReachingSink() and
|
||||
// aiohttp.client treats `None` as the default and all other "falsey" values as `False`.
|
||||
argumentOrigin.asExpr().(ImmutableLiteral).booleanValue() = false and
|
||||
not argumentOrigin.asExpr() instanceof None
|
||||
|
||||
@@ -53,7 +53,7 @@ private module Aiomysql {
|
||||
class CursorExecuteCall extends SqlConstruction::Range, API::CallNode {
|
||||
CursorExecuteCall() { this = cursor().getMember("execute").getACall() }
|
||||
|
||||
override DataFlow::Node getSql() { result = this.getParameter(0, "operation").getARhs() }
|
||||
override DataFlow::Node getSql() { result = this.getParameter(0, "operation").asSink() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,7 +63,7 @@ private module Aiomysql {
|
||||
class AwaitedCursorExecuteCall extends SqlExecution::Range {
|
||||
CursorExecuteCall executeCall;
|
||||
|
||||
AwaitedCursorExecuteCall() { this = executeCall.getReturn().getAwaited().getAnImmediateUse() }
|
||||
AwaitedCursorExecuteCall() { this = executeCall.getReturn().getAwaited().asSource() }
|
||||
|
||||
override DataFlow::Node getSql() { result = executeCall.getSql() }
|
||||
}
|
||||
@@ -94,7 +94,7 @@ private module Aiomysql {
|
||||
class SAConnectionExecuteCall extends SqlConstruction::Range, API::CallNode {
|
||||
SAConnectionExecuteCall() { this = saConnection().getMember("execute").getACall() }
|
||||
|
||||
override DataFlow::Node getSql() { result = this.getParameter(0, "query").getARhs() }
|
||||
override DataFlow::Node getSql() { result = this.getParameter(0, "query").asSink() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,7 +104,7 @@ private module Aiomysql {
|
||||
class AwaitedSAConnectionExecuteCall extends SqlExecution::Range {
|
||||
SAConnectionExecuteCall execute;
|
||||
|
||||
AwaitedSAConnectionExecuteCall() { this = execute.getReturn().getAwaited().getAnImmediateUse() }
|
||||
AwaitedSAConnectionExecuteCall() { this = execute.getReturn().getAwaited().asSource() }
|
||||
|
||||
override DataFlow::Node getSql() { result = execute.getSql() }
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ private module Aiopg {
|
||||
class CursorExecuteCall extends SqlConstruction::Range, API::CallNode {
|
||||
CursorExecuteCall() { this = cursor().getMember("execute").getACall() }
|
||||
|
||||
override DataFlow::Node getSql() { result = this.getParameter(0, "operation").getARhs() }
|
||||
override DataFlow::Node getSql() { result = this.getParameter(0, "operation").asSink() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,7 +63,7 @@ private module Aiopg {
|
||||
class AwaitedCursorExecuteCall extends SqlExecution::Range {
|
||||
CursorExecuteCall execute;
|
||||
|
||||
AwaitedCursorExecuteCall() { this = execute.getReturn().getAwaited().getAnImmediateUse() }
|
||||
AwaitedCursorExecuteCall() { this = execute.getReturn().getAwaited().asSource() }
|
||||
|
||||
override DataFlow::Node getSql() { result = execute.getSql() }
|
||||
}
|
||||
@@ -90,7 +90,7 @@ private module Aiopg {
|
||||
class SAConnectionExecuteCall extends SqlConstruction::Range, API::CallNode {
|
||||
SAConnectionExecuteCall() { this = saConnection().getMember("execute").getACall() }
|
||||
|
||||
override DataFlow::Node getSql() { result = this.getParameter(0, "query").getARhs() }
|
||||
override DataFlow::Node getSql() { result = this.getParameter(0, "query").asSink() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,7 +100,7 @@ private module Aiopg {
|
||||
class AwaitedSAConnectionExecuteCall extends SqlExecution::Range {
|
||||
SAConnectionExecuteCall excute;
|
||||
|
||||
AwaitedSAConnectionExecuteCall() { this = excute.getReturn().getAwaited().getAnImmediateUse() }
|
||||
AwaitedSAConnectionExecuteCall() { this = excute.getReturn().getAwaited().asSource() }
|
||||
|
||||
override DataFlow::Node getSql() { result = excute.getSql() }
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ private module Asyncpg {
|
||||
this = ModelOutput::getATypeNode("asyncpg", "Connection").getMember("cursor").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getSql() { result = this.getParameter(0, "query").getARhs() }
|
||||
override DataFlow::Node getSql() { result = this.getParameter(0, "query").asSink() }
|
||||
}
|
||||
|
||||
/** The creation of a `Cursor` executes the associated query. */
|
||||
@@ -71,14 +71,14 @@ private module Asyncpg {
|
||||
CursorCreation() {
|
||||
exists(CursorConstruction c |
|
||||
sql = c.getSql() and
|
||||
this = c.getReturn().getAwaited().getAnImmediateUse()
|
||||
this = c.getReturn().getAwaited().asSource()
|
||||
)
|
||||
or
|
||||
exists(API::CallNode prepareCall |
|
||||
prepareCall =
|
||||
ModelOutput::getATypeNode("asyncpg", "Connection").getMember("prepare").getACall()
|
||||
|
|
||||
sql = prepareCall.getParameter(0, "query").getARhs() and
|
||||
sql = prepareCall.getParameter(0, "query").asSink() and
|
||||
this =
|
||||
prepareCall
|
||||
.getReturn()
|
||||
@@ -86,7 +86,7 @@ private module Asyncpg {
|
||||
.getMember("cursor")
|
||||
.getReturn()
|
||||
.getAwaited()
|
||||
.getAnImmediateUse()
|
||||
.asSource()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -164,7 +164,7 @@ private module CryptodomeModel {
|
||||
.getMember("Cipher")
|
||||
.getMember(cipherName)
|
||||
.getMember(modeName)
|
||||
.getAUse()
|
||||
.getAValueReachableFromSource()
|
||||
|
|
||||
result = modeName.splitAt("_", 1)
|
||||
)
|
||||
|
||||
@@ -144,12 +144,10 @@ private module CryptographyModel {
|
||||
DataFlow::Node getCurveArg() { result in [this.getArg(0), this.getArgByName("curve")] }
|
||||
|
||||
override int getKeySizeWithOrigin(DataFlow::Node origin) {
|
||||
exists(API::Node n |
|
||||
n = Ecc::predefinedCurveClass(result) and origin = n.getAnImmediateUse()
|
||||
|
|
||||
this.getCurveArg() = n.getAUse()
|
||||
exists(API::Node n | n = Ecc::predefinedCurveClass(result) and origin = n.asSource() |
|
||||
this.getCurveArg() = n.getAValueReachableFromSource()
|
||||
or
|
||||
this.getCurveArg() = n.getReturn().getAUse()
|
||||
this.getCurveArg() = n.getReturn().getAValueReachableFromSource()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -191,12 +189,12 @@ private module CryptographyModel {
|
||||
.getMember("ciphers")
|
||||
.getMember("Cipher")
|
||||
.getACall() and
|
||||
algorithmClassRef(algorithmName).getReturn().getAUse() in [
|
||||
algorithmClassRef(algorithmName).getReturn().getAValueReachableFromSource() in [
|
||||
call.getArg(0), call.getArgByName("algorithm")
|
||||
] and
|
||||
exists(DataFlow::Node modeArg | modeArg in [call.getArg(1), call.getArgByName("mode")] |
|
||||
if modeArg = modeClassRef(_).getReturn().getAUse()
|
||||
then modeArg = modeClassRef(modeName).getReturn().getAUse()
|
||||
if modeArg = modeClassRef(_).getReturn().getAValueReachableFromSource()
|
||||
then modeArg = modeClassRef(modeName).getReturn().getAValueReachableFromSource()
|
||||
else modeName = "<None or unknown>"
|
||||
)
|
||||
)
|
||||
@@ -254,7 +252,7 @@ private module CryptographyModel {
|
||||
.getMember("hashes")
|
||||
.getMember("Hash")
|
||||
.getACall() and
|
||||
algorithmClassRef(algorithmName).getReturn().getAUse() in [
|
||||
algorithmClassRef(algorithmName).getReturn().getAValueReachableFromSource() in [
|
||||
call.getArg(0), call.getArgByName("algorithm")
|
||||
]
|
||||
)
|
||||
|
||||
@@ -562,7 +562,7 @@ module PrivateDjango {
|
||||
|
||||
/** A `django.db.connection` is a PEP249 compliant DB connection. */
|
||||
class DjangoDbConnection extends PEP249::Connection::InstanceSource {
|
||||
DjangoDbConnection() { this = connection().getAnImmediateUse() }
|
||||
DjangoDbConnection() { this = connection().asSource() }
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -869,7 +869,7 @@ module PrivateDjango {
|
||||
|
||||
/** Gets the (AST) class of the Django model class `modelClass`. */
|
||||
Class getModelClassClass(API::Node modelClass) {
|
||||
result.getParent() = modelClass.getAnImmediateUse().asExpr() and
|
||||
result.getParent() = modelClass.asSource().asExpr() and
|
||||
modelClass = Model::subclassRef()
|
||||
}
|
||||
|
||||
@@ -2202,9 +2202,7 @@ module PrivateDjango {
|
||||
* thereby handling user input.
|
||||
*/
|
||||
class DjangoFormClass extends Class, SelfRefMixin {
|
||||
DjangoFormClass() {
|
||||
this.getParent() = Django::Forms::Form::subclassRef().getAnImmediateUse().asExpr()
|
||||
}
|
||||
DjangoFormClass() { this.getParent() = Django::Forms::Form::subclassRef().asSource().asExpr() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2237,7 +2235,7 @@ module PrivateDjango {
|
||||
*/
|
||||
class DjangoFormFieldClass extends Class {
|
||||
DjangoFormFieldClass() {
|
||||
this.getParent() = Django::Forms::Field::subclassRef().getAnImmediateUse().asExpr()
|
||||
this.getParent() = Django::Forms::Field::subclassRef().asSource().asExpr()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2340,7 +2338,7 @@ module PrivateDjango {
|
||||
*/
|
||||
class DjangoViewClassFromSuperClass extends DjangoViewClass {
|
||||
DjangoViewClassFromSuperClass() {
|
||||
this.getParent() = Django::Views::View::subclassRef().getAnImmediateUse().asExpr()
|
||||
this.getParent() = Django::Views::View::subclassRef().asSource().asExpr()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2743,7 +2741,7 @@ module PrivateDjango {
|
||||
.getMember("utils")
|
||||
.getMember("log")
|
||||
.getMember("request_logger")
|
||||
.getAnImmediateUse()
|
||||
.asSource()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2801,7 +2799,7 @@ module PrivateDjango {
|
||||
.getMember("decorators")
|
||||
.getMember("csrf")
|
||||
.getMember(decoratorName)
|
||||
.getAUse() and
|
||||
.getAValueReachableFromSource() and
|
||||
this.asExpr() = function.getADecorator()
|
||||
}
|
||||
|
||||
|
||||
@@ -179,7 +179,7 @@ private module FabricV2 {
|
||||
DataFlow::ParameterNode {
|
||||
FabricTaskFirstParamConnectionInstance() {
|
||||
exists(Function func |
|
||||
func.getADecorator() = Fabric::Tasks::task().getAUse().asExpr() and
|
||||
func.getADecorator() = Fabric::Tasks::task().getAValueReachableFromSource().asExpr() and
|
||||
this.getParameter() = func.getArg(0)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -90,7 +90,8 @@ private module FastApi {
|
||||
private class PydanticModelRequestHandlerParam extends Pydantic::BaseModel::InstanceSource,
|
||||
DataFlow::ParameterNode {
|
||||
PydanticModelRequestHandlerParam() {
|
||||
this.getParameter().getAnnotation() = Pydantic::BaseModel::subclassRef().getAUse().asExpr() and
|
||||
this.getParameter().getAnnotation() =
|
||||
Pydantic::BaseModel::subclassRef().getAValueReachableFromSource().asExpr() and
|
||||
any(FastApiRouteSetup rs).getARequestHandler().getArgByName(_) = this.getParameter()
|
||||
}
|
||||
}
|
||||
@@ -104,7 +105,8 @@ private module FastApi {
|
||||
private class WebSocketRequestHandlerParam extends Starlette::WebSocket::InstanceSource,
|
||||
DataFlow::ParameterNode {
|
||||
WebSocketRequestHandlerParam() {
|
||||
this.getParameter().getAnnotation() = Starlette::WebSocket::classRef().getAUse().asExpr() and
|
||||
this.getParameter().getAnnotation() =
|
||||
Starlette::WebSocket::classRef().getAValueReachableFromSource().asExpr() and
|
||||
any(FastApiRouteSetup rs).getARequestHandler().getArgByName(_) = this.getParameter()
|
||||
}
|
||||
}
|
||||
@@ -165,8 +167,8 @@ private module FastApi {
|
||||
// user-defined subclasses
|
||||
exists(Class cls, API::Node base |
|
||||
base = getModeledResponseClass(_).getASubclass*() and
|
||||
cls.getABase() = base.getAUse().asExpr() and
|
||||
responseClass.getAnImmediateUse().asExpr() = cls.getParent()
|
||||
cls.getABase() = base.getAValueReachableFromSource().asExpr() and
|
||||
responseClass.asSource().asExpr() = cls.getParent()
|
||||
|
|
||||
exists(Assign assign | assign = cls.getAStmt() |
|
||||
assign.getATarget().(Name).getId() = "media_type" and
|
||||
@@ -257,7 +259,7 @@ private module FastApi {
|
||||
|
||||
override string getMimetypeDefault() {
|
||||
exists(API::Node responseClass |
|
||||
responseClass.getAUse() = routeSetup.getResponseClassArg() and
|
||||
responseClass.getAValueReachableFromSource() = routeSetup.getResponseClassArg() and
|
||||
result = getDefaultMimeType(responseClass)
|
||||
)
|
||||
or
|
||||
@@ -274,7 +276,7 @@ private module FastApi {
|
||||
FileSystemAccess::Range {
|
||||
FastApiRequestHandlerFileResponseReturn() {
|
||||
exists(API::Node responseClass |
|
||||
responseClass.getAUse() = routeSetup.getResponseClassArg() and
|
||||
responseClass.getAValueReachableFromSource() = routeSetup.getResponseClassArg() and
|
||||
responseClass = getModeledResponseClass("FileResponse").getASubclass*()
|
||||
)
|
||||
}
|
||||
@@ -292,7 +294,7 @@ private module FastApi {
|
||||
HTTP::Server::HttpRedirectResponse::Range {
|
||||
FastApiRequestHandlerRedirectReturn() {
|
||||
exists(API::Node responseClass |
|
||||
responseClass.getAUse() = routeSetup.getResponseClassArg() and
|
||||
responseClass.getAValueReachableFromSource() = routeSetup.getResponseClassArg() and
|
||||
responseClass = getModeledResponseClass("RedirectResponse").getASubclass*()
|
||||
)
|
||||
}
|
||||
@@ -311,7 +313,7 @@ private module FastApi {
|
||||
class RequestHandlerParam extends InstanceSource, DataFlow::ParameterNode {
|
||||
RequestHandlerParam() {
|
||||
this.getParameter().getAnnotation() =
|
||||
getModeledResponseClass(_).getASubclass*().getAUse().asExpr() and
|
||||
getModeledResponseClass(_).getASubclass*().getAValueReachableFromSource().asExpr() and
|
||||
any(FastApiRouteSetup rs).getARequestHandler().getArgByName(_) = this.getParameter()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ module Flask {
|
||||
|
||||
FlaskViewClass() {
|
||||
api_node = Views::View::subclassRef() and
|
||||
this.getParent() = api_node.getAnImmediateUse().asExpr()
|
||||
this.getParent() = api_node.asSource().asExpr()
|
||||
}
|
||||
|
||||
/** Gets a function that could handle incoming requests, if any. */
|
||||
@@ -220,7 +220,7 @@ module Flask {
|
||||
class FlaskMethodViewClass extends FlaskViewClass {
|
||||
FlaskMethodViewClass() {
|
||||
api_node = Views::MethodView::subclassRef() and
|
||||
this.getParent() = api_node.getAnImmediateUse().asExpr()
|
||||
this.getParent() = api_node.asSource().asExpr()
|
||||
}
|
||||
|
||||
override Function getARequestHandler() {
|
||||
@@ -305,7 +305,7 @@ module Flask {
|
||||
)
|
||||
or
|
||||
exists(FlaskViewClass vc |
|
||||
this.getViewArg() = vc.asViewResult().getAUse() and
|
||||
this.getViewArg() = vc.asViewResult().getAValueReachableFromSource() and
|
||||
result = vc.getARequestHandler()
|
||||
)
|
||||
}
|
||||
@@ -339,7 +339,7 @@ module Flask {
|
||||
*/
|
||||
private class FlaskRequestSource extends RemoteFlowSource::Range {
|
||||
FlaskRequestSource() {
|
||||
this = request().getAUse() and
|
||||
this = request().getAValueReachableFromSource() and
|
||||
not any(Import imp).contains(this.asExpr()) and
|
||||
not exists(ControlFlowNode def | this.asVar().getSourceVariable().hasDefiningNode(def) |
|
||||
any(Import imp).contains(def.getNode())
|
||||
@@ -357,7 +357,7 @@ module Flask {
|
||||
private class InstanceTaintSteps extends InstanceTaintStepsHelper {
|
||||
InstanceTaintSteps() { this = "flask.Request" }
|
||||
|
||||
override DataFlow::Node getInstance() { result = request().getAUse() }
|
||||
override DataFlow::Node getInstance() { result = request().getAValueReachableFromSource() }
|
||||
|
||||
override string getAttributeName() {
|
||||
result in [
|
||||
@@ -404,7 +404,7 @@ module Flask {
|
||||
|
||||
private class RequestAttrMultiDict extends Werkzeug::MultiDict::InstanceSource {
|
||||
RequestAttrMultiDict() {
|
||||
this = request().getMember(["args", "values", "form", "files"]).getAnImmediateUse()
|
||||
this = request().getMember(["args", "values", "form", "files"]).asSource()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -415,26 +415,25 @@ module Flask {
|
||||
// be able to do something more structured for providing modeling of the members
|
||||
// of a container-object.
|
||||
exists(API::Node files | files = request().getMember("files") |
|
||||
this.asCfgNode().(SubscriptNode).getObject() = files.getAUse().asCfgNode()
|
||||
this.asCfgNode().(SubscriptNode).getObject() =
|
||||
files.getAValueReachableFromSource().asCfgNode()
|
||||
or
|
||||
this = files.getMember("get").getACall()
|
||||
or
|
||||
this.asCfgNode().(SubscriptNode).getObject() =
|
||||
files.getMember("getlist").getReturn().getAUse().asCfgNode()
|
||||
files.getMember("getlist").getReturn().getAValueReachableFromSource().asCfgNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** An `Headers` instance that originates from a flask request. */
|
||||
private class FlaskRequestHeadersInstances extends Werkzeug::Headers::InstanceSource {
|
||||
FlaskRequestHeadersInstances() { this = request().getMember("headers").getAnImmediateUse() }
|
||||
FlaskRequestHeadersInstances() { this = request().getMember("headers").asSource() }
|
||||
}
|
||||
|
||||
/** An `Authorization` instance that originates from a flask request. */
|
||||
private class FlaskRequestAuthorizationInstances extends Werkzeug::Authorization::InstanceSource {
|
||||
FlaskRequestAuthorizationInstances() {
|
||||
this = request().getMember("authorization").getAnImmediateUse()
|
||||
}
|
||||
FlaskRequestAuthorizationInstances() { this = request().getMember("authorization").asSource() }
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -574,6 +573,6 @@ module Flask {
|
||||
* - https://flask.palletsprojects.com/en/2.0.x/logging/
|
||||
*/
|
||||
private class FlaskLogger extends Stdlib::Logger::InstanceSource {
|
||||
FlaskLogger() { this = FlaskApp::instance().getMember("logger").getAnImmediateUse() }
|
||||
FlaskLogger() { this = FlaskApp::instance().getMember("logger").asSource() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ private module FlaskSqlAlchemy {
|
||||
/** Access on a DB resulting in an Engine */
|
||||
private class DbEngine extends SqlAlchemy::Engine::InstanceSource {
|
||||
DbEngine() {
|
||||
this = dbInstance().getMember("engine").getAnImmediateUse()
|
||||
this = dbInstance().getMember("engine").asSource()
|
||||
or
|
||||
this = dbInstance().getMember("get_engine").getACall()
|
||||
}
|
||||
@@ -44,7 +44,7 @@ private module FlaskSqlAlchemy {
|
||||
/** Access on a DB resulting in a Session */
|
||||
private class DbSession extends SqlAlchemy::Session::InstanceSource {
|
||||
DbSession() {
|
||||
this = dbInstance().getMember("session").getAnImmediateUse()
|
||||
this = dbInstance().getMember("session").asSource()
|
||||
or
|
||||
this = dbInstance().getMember("create_session").getReturn().getACall()
|
||||
or
|
||||
|
||||
@@ -44,8 +44,8 @@ private module HttpxModel {
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
disablingNode = this.getKeywordParameter("verify").getARhs() and
|
||||
argumentOrigin = this.getKeywordParameter("verify").getAValueReachingRhs() and
|
||||
disablingNode = this.getKeywordParameter("verify").asSink() and
|
||||
argumentOrigin = this.getKeywordParameter("verify").getAValueReachingSink() and
|
||||
// unlike `requests`, httpx treats `None` as turning off verify (and not as the default)
|
||||
argumentOrigin.asExpr().(ImmutableLiteral).booleanValue() = false
|
||||
// TODO: Handling of insecure SSLContext passed to verify argument
|
||||
@@ -89,8 +89,8 @@ private module HttpxModel {
|
||||
constructor = classRef().getACall() and
|
||||
this = constructor.getReturn().getMember(methodName).getACall()
|
||||
|
|
||||
disablingNode = constructor.getKeywordParameter("verify").getARhs() and
|
||||
argumentOrigin = constructor.getKeywordParameter("verify").getAValueReachingRhs() and
|
||||
disablingNode = constructor.getKeywordParameter("verify").asSink() and
|
||||
argumentOrigin = constructor.getKeywordParameter("verify").getAValueReachingSink() and
|
||||
// unlike `requests`, httpx treats `None` as turning off verify (and not as the default)
|
||||
argumentOrigin.asExpr().(ImmutableLiteral).booleanValue() = false
|
||||
// TODO: Handling of insecure SSLContext passed to verify argument
|
||||
|
||||
@@ -39,7 +39,8 @@ private module Invoke {
|
||||
result = InvokeModule::Context::ContextClass::classRef().getACall()
|
||||
or
|
||||
exists(Function func |
|
||||
func.getADecorator() = invoke().getMember("task").getAUse().asExpr() and
|
||||
func.getADecorator() =
|
||||
invoke().getMember("task").getAValueReachableFromSource().asExpr() and
|
||||
result.(DataFlow::ParameterNode).getParameter() = func.getArg(0)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -141,17 +141,18 @@ private module Lxml {
|
||||
// resolve_entities has default True
|
||||
not exists(this.getArgByName("resolve_entities"))
|
||||
or
|
||||
this.getKeywordParameter("resolve_entities").getAValueReachingRhs().asExpr() = any(True t)
|
||||
this.getKeywordParameter("resolve_entities").getAValueReachingSink().asExpr() =
|
||||
any(True t)
|
||||
)
|
||||
or
|
||||
kind.isXmlBomb() and
|
||||
this.getKeywordParameter("huge_tree").getAValueReachingRhs().asExpr() = any(True t) and
|
||||
not this.getKeywordParameter("resolve_entities").getAValueReachingRhs().asExpr() =
|
||||
this.getKeywordParameter("huge_tree").getAValueReachingSink().asExpr() = any(True t) and
|
||||
not this.getKeywordParameter("resolve_entities").getAValueReachingSink().asExpr() =
|
||||
any(False t)
|
||||
or
|
||||
kind.isDtdRetrieval() and
|
||||
this.getKeywordParameter("load_dtd").getAValueReachingRhs().asExpr() = any(True t) and
|
||||
this.getKeywordParameter("no_network").getAValueReachingRhs().asExpr() = any(False t)
|
||||
this.getKeywordParameter("load_dtd").getAValueReachingSink().asExpr() = any(True t) and
|
||||
this.getKeywordParameter("no_network").getAValueReachingSink().asExpr() = any(False t)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,11 +319,11 @@ private module Lxml {
|
||||
kind.isXxe()
|
||||
or
|
||||
kind.isXmlBomb() and
|
||||
this.getKeywordParameter("huge_tree").getAValueReachingRhs().asExpr() = any(True t)
|
||||
this.getKeywordParameter("huge_tree").getAValueReachingSink().asExpr() = any(True t)
|
||||
or
|
||||
kind.isDtdRetrieval() and
|
||||
this.getKeywordParameter("load_dtd").getAValueReachingRhs().asExpr() = any(True t) and
|
||||
this.getKeywordParameter("no_network").getAValueReachingRhs().asExpr() = any(False t)
|
||||
this.getKeywordParameter("load_dtd").getAValueReachingSink().asExpr() = any(True t) and
|
||||
this.getKeywordParameter("no_network").getAValueReachingSink().asExpr() = any(False t)
|
||||
}
|
||||
|
||||
override predicate mayExecuteInput() { none() }
|
||||
|
||||
@@ -61,8 +61,8 @@ private module Requests {
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
disablingNode = this.getKeywordParameter("verify").getARhs() and
|
||||
argumentOrigin = this.getKeywordParameter("verify").getAValueReachingRhs() and
|
||||
disablingNode = this.getKeywordParameter("verify").asSink() and
|
||||
argumentOrigin = this.getKeywordParameter("verify").getAValueReachingSink() and
|
||||
// requests treats `None` as the default and all other "falsey" values as `False`.
|
||||
argumentOrigin.asExpr().(ImmutableLiteral).booleanValue() = false and
|
||||
not argumentOrigin.asExpr() instanceof None
|
||||
|
||||
@@ -115,7 +115,7 @@ private module RestFramework {
|
||||
*/
|
||||
class RestFrameworkApiViewClass extends PrivateDjango::DjangoViewClassFromSuperClass {
|
||||
RestFrameworkApiViewClass() {
|
||||
this.getParent() = any(ModeledApiViewClasses c).getASubclass*().getAnImmediateUse().asExpr()
|
||||
this.getParent() = any(ModeledApiViewClasses c).getASubclass*().asSource().asExpr()
|
||||
}
|
||||
|
||||
override Function getARequestHandler() {
|
||||
|
||||
@@ -44,7 +44,7 @@ private module RuamelYaml {
|
||||
API::moduleImport("ruamel")
|
||||
.getMember("yaml")
|
||||
.getMember(["SafeLoader", "BaseLoader", "CSafeLoader", "CBaseLoader"])
|
||||
.getAUse()
|
||||
.getAValueReachableFromSource()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -274,7 +274,7 @@ module Stdlib {
|
||||
ClassInstantiation() {
|
||||
this = subclassRef().getACall()
|
||||
or
|
||||
this = API::moduleImport("logging").getMember("root").getAnImmediateUse()
|
||||
this = API::moduleImport("logging").getMember("root").asSource()
|
||||
or
|
||||
this = API::moduleImport("logging").getMember("getLogger").getACall()
|
||||
}
|
||||
@@ -1727,15 +1727,16 @@ private module StdlibPrivate {
|
||||
private DataFlow::TypeTrackingNode fieldList(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
// TODO: Should have better handling of subscripting
|
||||
result.asCfgNode().(SubscriptNode).getObject() = instance().getAUse().asCfgNode()
|
||||
result.asCfgNode().(SubscriptNode).getObject() =
|
||||
instance().getAValueReachableFromSource().asCfgNode()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = fieldList(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to a list of fields. */
|
||||
DataFlow::Node fieldList() {
|
||||
result = getlistResult().getAUse() or
|
||||
result = getvalueResult().getAUse() or
|
||||
result = getlistResult().getAValueReachableFromSource() or
|
||||
result = getvalueResult().getAValueReachableFromSource() or
|
||||
fieldList(DataFlow::TypeTracker::end()).flowsTo(result)
|
||||
}
|
||||
|
||||
@@ -1744,16 +1745,16 @@ private module StdlibPrivate {
|
||||
t.start() and
|
||||
// TODO: Should have better handling of subscripting
|
||||
result.asCfgNode().(SubscriptNode).getObject() =
|
||||
[instance().getAUse(), fieldList()].asCfgNode()
|
||||
[instance().getAValueReachableFromSource(), fieldList()].asCfgNode()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = field(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a reference to a field. */
|
||||
DataFlow::Node field() {
|
||||
result = getfirstResult().getAUse()
|
||||
result = getfirstResult().getAValueReachableFromSource()
|
||||
or
|
||||
result = getvalueResult().getAUse()
|
||||
result = getvalueResult().getAValueReachableFromSource()
|
||||
or
|
||||
field(DataFlow::TypeTracker::end()).flowsTo(result)
|
||||
}
|
||||
@@ -1762,20 +1763,23 @@ private module StdlibPrivate {
|
||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
// Methods
|
||||
nodeFrom = nodeTo.(DataFlow::AttrRead).getObject() and
|
||||
nodeFrom = instance().getAUse() and
|
||||
nodeTo = [getvalueRef(), getfirstRef(), getlistRef()].getAUse()
|
||||
nodeFrom = instance().getAValueReachableFromSource() and
|
||||
nodeTo = [getvalueRef(), getfirstRef(), getlistRef()].getAValueReachableFromSource()
|
||||
or
|
||||
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(CallNode).getFunction() and
|
||||
(
|
||||
nodeFrom = getvalueRef().getAUse() and nodeTo = getvalueResult().getAnImmediateUse()
|
||||
nodeFrom = getvalueRef().getAValueReachableFromSource() and
|
||||
nodeTo = getvalueResult().asSource()
|
||||
or
|
||||
nodeFrom = getfirstRef().getAUse() and nodeTo = getfirstResult().getAnImmediateUse()
|
||||
nodeFrom = getfirstRef().getAValueReachableFromSource() and
|
||||
nodeTo = getfirstResult().asSource()
|
||||
or
|
||||
nodeFrom = getlistRef().getAUse() and nodeTo = getlistResult().getAnImmediateUse()
|
||||
nodeFrom = getlistRef().getAValueReachableFromSource() and
|
||||
nodeTo = getlistResult().asSource()
|
||||
)
|
||||
or
|
||||
// Indexing
|
||||
nodeFrom in [instance().getAUse(), fieldList()] and
|
||||
nodeFrom in [instance().getAValueReachableFromSource(), fieldList()] and
|
||||
nodeTo.asCfgNode().(SubscriptNode).getObject() = nodeFrom.asCfgNode()
|
||||
or
|
||||
// Attributes on Field
|
||||
@@ -1939,7 +1943,7 @@ private module StdlibPrivate {
|
||||
|
||||
/** A HttpRequestHandler class definition (most likely in project code). */
|
||||
class HttpRequestHandlerClassDef extends Class {
|
||||
HttpRequestHandlerClassDef() { this.getParent() = subclassRef().getAnImmediateUse().asExpr() }
|
||||
HttpRequestHandlerClassDef() { this.getParent() = subclassRef().asSource().asExpr() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for HttpRequestHandlerClassDef */
|
||||
@@ -2037,7 +2041,7 @@ private module StdlibPrivate {
|
||||
.getMember("simple_server")
|
||||
.getMember("WSGIServer")
|
||||
.getASubclass*()
|
||||
.getAnImmediateUse()
|
||||
.asSource()
|
||||
.asExpr()
|
||||
}
|
||||
}
|
||||
@@ -2553,7 +2557,7 @@ private module StdlibPrivate {
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
result = super.getAPathArgument()
|
||||
or
|
||||
result = this.getParameter(0, "target").getARhs()
|
||||
result = this.getParameter(0, "target").asSink()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2570,7 +2574,7 @@ private module StdlibPrivate {
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
result = super.getAPathArgument()
|
||||
or
|
||||
result = this.getParameter(0, "target").getARhs()
|
||||
result = this.getParameter(0, "target").asSink()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2585,7 +2589,7 @@ private module StdlibPrivate {
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
result = super.getAPathArgument()
|
||||
or
|
||||
result = this.getParameter(0, "other_path").getARhs()
|
||||
result = this.getParameter(0, "other_path").asSink()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2653,7 +2657,7 @@ private module StdlibPrivate {
|
||||
/** Gets a call to `hashlib.new` with `algorithmName` as the first argument. */
|
||||
private API::CallNode hashlibNewCall(string algorithmName) {
|
||||
algorithmName =
|
||||
result.getParameter(0, "name").getAValueReachingRhs().asExpr().(StrConst).getText() and
|
||||
result.getParameter(0, "name").getAValueReachingSink().asExpr().(StrConst).getText() and
|
||||
result = API::moduleImport("hashlib").getMember("new").getACall()
|
||||
}
|
||||
|
||||
@@ -2670,7 +2674,7 @@ private module StdlibPrivate {
|
||||
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() { result.matchesName(hashName) }
|
||||
|
||||
override DataFlow::Node getAnInput() { result = this.getParameter(1, "data").getARhs() }
|
||||
override DataFlow::Node getAnInput() { result = this.getParameter(1, "data").asSink() }
|
||||
|
||||
override Cryptography::BlockMode getBlockMode() { none() }
|
||||
}
|
||||
@@ -3433,13 +3437,13 @@ private module StdlibPrivate {
|
||||
private DataFlow::Node saxParserWithFeatureExternalGesTurnedOn(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
exists(SaxParserSetFeatureCall call |
|
||||
call.getFeatureArg().getARhs() =
|
||||
call.getFeatureArg().asSink() =
|
||||
API::moduleImport("xml")
|
||||
.getMember("sax")
|
||||
.getMember("handler")
|
||||
.getMember("feature_external_ges")
|
||||
.getAUse() and
|
||||
call.getStateArg().getAValueReachingRhs().asExpr().(BooleanLiteral).booleanValue() = true and
|
||||
.getAValueReachableFromSource() and
|
||||
call.getStateArg().getAValueReachingSink().asExpr().(BooleanLiteral).booleanValue() = true and
|
||||
result = call.getObject()
|
||||
)
|
||||
or
|
||||
@@ -3449,13 +3453,13 @@ private module StdlibPrivate {
|
||||
// take account of that we can set the feature to False, which makes the parser safe again
|
||||
not exists(SaxParserSetFeatureCall call |
|
||||
call.getObject() = result and
|
||||
call.getFeatureArg().getARhs() =
|
||||
call.getFeatureArg().asSink() =
|
||||
API::moduleImport("xml")
|
||||
.getMember("sax")
|
||||
.getMember("handler")
|
||||
.getMember("feature_external_ges")
|
||||
.getAUse() and
|
||||
call.getStateArg().getAValueReachingRhs().asExpr().(BooleanLiteral).booleanValue() = false
|
||||
.getAValueReachableFromSource() and
|
||||
call.getStateArg().getAValueReachingSink().asExpr().(BooleanLiteral).booleanValue() = false
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ private module Tornado {
|
||||
|
||||
/** A RequestHandler class (most likely in project code). */
|
||||
class RequestHandlerClass extends Class {
|
||||
RequestHandlerClass() { this.getParent() = subclassRef().getAnImmediateUse().asExpr() }
|
||||
RequestHandlerClass() { this.getParent() = subclassRef().asSource().asExpr() }
|
||||
|
||||
/** Gets a function that could handle incoming requests, if any. */
|
||||
Function getARequestHandler() {
|
||||
|
||||
@@ -33,7 +33,7 @@ private module Twisted {
|
||||
.getMember("resource")
|
||||
.getMember("Resource")
|
||||
.getASubclass*()
|
||||
.getAnImmediateUse()
|
||||
.asSource()
|
||||
.asExpr()
|
||||
}
|
||||
|
||||
|
||||
@@ -71,14 +71,15 @@ private module Urllib3 {
|
||||
|
|
||||
// cert_reqs
|
||||
// see https://urllib3.readthedocs.io/en/stable/user-guide.html?highlight=cert_reqs#certificate-verification
|
||||
disablingNode = constructor.getKeywordParameter("cert_reqs").getARhs() and
|
||||
argumentOrigin = constructor.getKeywordParameter("cert_reqs").getAValueReachingRhs() and
|
||||
disablingNode = constructor.getKeywordParameter("cert_reqs").asSink() and
|
||||
argumentOrigin = constructor.getKeywordParameter("cert_reqs").getAValueReachingSink() and
|
||||
argumentOrigin.asExpr().(StrConst).getText() = "CERT_NONE"
|
||||
or
|
||||
// assert_hostname
|
||||
// see https://urllib3.readthedocs.io/en/stable/reference/urllib3.connectionpool.html?highlight=assert_hostname#urllib3.HTTPSConnectionPool
|
||||
disablingNode = constructor.getKeywordParameter("assert_hostname").getARhs() and
|
||||
argumentOrigin = constructor.getKeywordParameter("assert_hostname").getAValueReachingRhs() and
|
||||
disablingNode = constructor.getKeywordParameter("assert_hostname").asSink() and
|
||||
argumentOrigin =
|
||||
constructor.getKeywordParameter("assert_hostname").getAValueReachingSink() and
|
||||
argumentOrigin.asExpr().(BooleanLiteral).booleanValue() = false
|
||||
)
|
||||
}
|
||||
|
||||
@@ -285,7 +285,7 @@ private module WerkzeugOld {
|
||||
* See https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.Headers.getlist
|
||||
*/
|
||||
deprecated DataFlow::Node getlist() {
|
||||
result = any(InstanceSourceApiNode a).getMember("getlist").getAUse()
|
||||
result = any(InstanceSourceApiNode a).getMember("getlist").getAValueReachableFromSource()
|
||||
}
|
||||
|
||||
private class MultiDictAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||
@@ -331,7 +331,9 @@ private module WerkzeugOld {
|
||||
abstract deprecated class InstanceSourceApiNode extends API::Node { }
|
||||
|
||||
/** Gets a reference to an instance of `werkzeug.datastructures.FileStorage`. */
|
||||
deprecated DataFlow::Node instance() { result = any(InstanceSourceApiNode a).getAUse() }
|
||||
deprecated DataFlow::Node instance() {
|
||||
result = any(InstanceSourceApiNode a).getAValueReachableFromSource()
|
||||
}
|
||||
|
||||
private class FileStorageAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
|
||||
@@ -29,7 +29,7 @@ private module Xmltodict {
|
||||
|
||||
override predicate vulnerableTo(XML::XmlParsingVulnerabilityKind kind) {
|
||||
kind.isXmlBomb() and
|
||||
this.getKeywordParameter("disable_entities").getAValueReachingRhs().asExpr() = any(False f)
|
||||
this.getKeywordParameter("disable_entities").getAValueReachingSink().asExpr() = any(False f)
|
||||
}
|
||||
|
||||
override predicate mayExecuteInput() { none() }
|
||||
|
||||
@@ -64,7 +64,7 @@ private module Yaml {
|
||||
loader_arg =
|
||||
API::moduleImport("yaml")
|
||||
.getMember(["SafeLoader", "BaseLoader", "CSafeLoader", "CBaseLoader"])
|
||||
.getAUse()
|
||||
.getAValueReachableFromSource()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ private import semmle.python.dataflow.new.TaintTracking
|
||||
* A remote flow source originating from a CSV source row.
|
||||
*/
|
||||
private class RemoteFlowSourceFromCsv extends RemoteFlowSource {
|
||||
RemoteFlowSourceFromCsv() { this = ModelOutput::getASourceNode("remote").getAnImmediateUse() }
|
||||
RemoteFlowSourceFromCsv() { this = ModelOutput::getASourceNode("remote").asSource() }
|
||||
|
||||
override string getSourceType() { result = "Remote flow (from model)" }
|
||||
}
|
||||
@@ -34,8 +34,8 @@ private class RemoteFlowSourceFromCsv extends RemoteFlowSource {
|
||||
private predicate summaryStepNodes(DataFlow::Node pred, DataFlow::Node succ, string kind) {
|
||||
exists(API::Node predNode, API::Node succNode |
|
||||
Specific::summaryStep(predNode, succNode, kind) and
|
||||
pred = predNode.getARhs() and
|
||||
succ = succNode.getAnImmediateUse()
|
||||
pred = predNode.asSink() and
|
||||
succ = succNode.asSource()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,9 @@ module PEP249 {
|
||||
}
|
||||
|
||||
/** Gets a reference to the `connect` function of a module that implements PEP 249. */
|
||||
DataFlow::Node connect() { result = any(PEP249ModuleApiNode a).getMember("connect").getAUse() }
|
||||
DataFlow::Node connect() {
|
||||
result = any(PEP249ModuleApiNode a).getMember("connect").getAValueReachableFromSource()
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides models for database connections (following PEP 249).
|
||||
|
||||
@@ -74,9 +74,7 @@ private module NotExposed {
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for fullyQualifiedToApiGraphPath */
|
||||
deprecated string fullyQualifiedToAPIGraphPath(string fullyQaulified) {
|
||||
result = fullyQualifiedToApiGraphPath(fullyQaulified)
|
||||
}
|
||||
deprecated predicate fullyQualifiedToAPIGraphPath = fullyQualifiedToApiGraphPath/1;
|
||||
|
||||
bindingset[this]
|
||||
abstract class FindSubclassesSpec extends string {
|
||||
@@ -152,7 +150,7 @@ private module NotExposed {
|
||||
FindSubclassesSpec spec, string newAliasFullyQualified, ImportMember importMember, Module mod,
|
||||
Location loc
|
||||
) {
|
||||
importMember = newOrExistingModeling(spec).getAUse().asExpr() and
|
||||
importMember = newOrExistingModeling(spec).getAValueReachableFromSource().asExpr() and
|
||||
importMember.getScope() = mod and
|
||||
loc = importMember.getLocation() and
|
||||
(
|
||||
@@ -182,7 +180,7 @@ private module NotExposed {
|
||||
// WHAT A HACK :D :D
|
||||
relevantClass.getPath() =
|
||||
relevantClass.getAPredecessor().getPath() + ".getMember(\"" + relevantName + "\")" and
|
||||
relevantClass.getAPredecessor().getAUse().asExpr() = importStar.getModule() and
|
||||
relevantClass.getAPredecessor().getAValueReachableFromSource().asExpr() = importStar.getModule() and
|
||||
(
|
||||
mod.isPackageInit() and
|
||||
newAliasFullyQualified = mod.getPackageName() + "." + relevantName
|
||||
@@ -204,7 +202,7 @@ private module NotExposed {
|
||||
FindSubclassesSpec spec, string newSubclassQualified, ClassExpr classExpr, Module mod,
|
||||
Location loc
|
||||
) {
|
||||
classExpr = newOrExistingModeling(spec).getASubclass*().getAnImmediateUse().asExpr() and
|
||||
classExpr = newOrExistingModeling(spec).getASubclass*().asSource().asExpr() and
|
||||
classExpr.getScope() = mod and
|
||||
newSubclassQualified = mod.getName() + "." + classExpr.getName() and
|
||||
loc = classExpr.getLocation() and
|
||||
|
||||
@@ -121,7 +121,7 @@ module Stages {
|
||||
or
|
||||
exists(any(NewDataFlow::TypeTracker t).append(_))
|
||||
or
|
||||
exists(any(API::Node n).getAMember().getAUse())
|
||||
exists(any(API::Node n).getAMember().getAValueReachableFromSource())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -344,12 +344,12 @@ private class ClassListList extends TClassListList {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate legalMergeCandidate(ClassObjectInternal cls, ClassListList remaining) {
|
||||
cls = this.getAHead() and remaining = this
|
||||
private predicate legalMergeCandidate(ClassObjectInternal cls, ClassListList remainingList) {
|
||||
cls = this.getAHead() and remainingList = this
|
||||
or
|
||||
this.legalMergeCandidate(cls, ConsList(Empty(), remaining))
|
||||
this.legalMergeCandidate(cls, ConsList(Empty(), remainingList))
|
||||
or
|
||||
this.legalMergeCandidateNonEmpty(cls, remaining, Empty())
|
||||
this.legalMergeCandidateNonEmpty(cls, remainingList, Empty())
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
@@ -386,10 +386,10 @@ private class ClassListList extends TClassListList {
|
||||
|
||||
private ClassList flatten_list(ClassListList list, int n) {
|
||||
need_flattening(list) and
|
||||
exists(ClassList head, ClassListList tail | list = ConsList(head, tail) |
|
||||
exists(ClassList head, ClassListList tail | pragma[only_bind_out](list) = ConsList(head, tail) |
|
||||
n = head.length() and result = tail.flatten()
|
||||
or
|
||||
result = Cons(head.getItem(n), flatten_list(list, n + 1))
|
||||
result = Cons(head.getItem(n), flatten_list(pragma[only_bind_out](list), n + 1))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -419,7 +419,9 @@ private ClassListList list_of_linearization_of_bases_plus_bases(ClassObjectInter
|
||||
result = ConsList(bases(cls), EmptyList()) and n = Types::base_count(cls) and n > 1
|
||||
or
|
||||
exists(ClassListList partial |
|
||||
partial = list_of_linearization_of_bases_plus_bases(cls, n + 1) and
|
||||
partial =
|
||||
list_of_linearization_of_bases_plus_bases(pragma[only_bind_into](cls),
|
||||
pragma[only_bind_into](n + 1)) and
|
||||
result = ConsList(Mro::newStyleMro(Types::getBase(cls, n)), partial)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ private string canonical_name(API::Node flag) {
|
||||
*/
|
||||
private DataFlow::TypeTrackingNode re_flag_tracker(string flag_name, DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
exists(API::Node flag | flag_name = canonical_name(flag) and result = flag.getAnImmediateUse())
|
||||
exists(API::Node flag | flag_name = canonical_name(flag) and result = flag.asSource())
|
||||
or
|
||||
exists(BinaryExprNode binop, DataFlow::Node operand |
|
||||
operand.getALocalSource() = re_flag_tracker(flag_name, t.continue()) and
|
||||
|
||||
@@ -64,7 +64,7 @@ module PathInjection {
|
||||
private import semmle.python.frameworks.data.ModelsAsData
|
||||
|
||||
private class DataAsFileSink extends Sink {
|
||||
DataAsFileSink() { this = ModelOutput::getASinkNode("path-injection").getARhs() }
|
||||
DataAsFileSink() { this = ModelOutput::getASinkNode("path-injection").asSink() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -67,6 +67,6 @@ module SqlInjection {
|
||||
|
||||
/** A sink for sql-injection from model data. */
|
||||
private class DataAsSqlSink extends Sink {
|
||||
DataAsSqlSink() { this = ModelOutput::getASinkNode("sql-injection").getARhs() }
|
||||
DataAsSqlSink() { this = ModelOutput::getASinkNode("sql-injection").asSink() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for detecting
|
||||
* "tar slip"
|
||||
* 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.BarrierGuards
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for detecting
|
||||
* "tar slip"
|
||||
* vulnerabilities, as well as extension points for adding your own.
|
||||
*/
|
||||
module TarSlip {
|
||||
/**
|
||||
* A data flow source for "tar slip" vulnerabilities.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow sink for "tar slip" vulnerabilities.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A sanitizer for "tar slip" vulnerabilities.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A call to `tarfile.open`, considered as a flow source.
|
||||
*/
|
||||
class TarfileOpen extends Source {
|
||||
TarfileOpen() {
|
||||
this = API::moduleImport("tarfile").getMember("open").getACall() and
|
||||
// If argument refers to a string object, then it's a hardcoded path and
|
||||
// this tarfile is safe.
|
||||
not this.(DataFlow::CallCfgNode).getArg(0).getALocalSource().asExpr() instanceof StrConst and
|
||||
// Ignore opens within the tarfile module itself
|
||||
not this.getLocation().getFile().getBaseName() = "tarfile.py"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A sanitizer based on file name. This because we extract the standard library.
|
||||
*
|
||||
* For efficiency we don't want to track the flow of taint
|
||||
* around the tarfile module.
|
||||
*/
|
||||
class ExcludeTarFilePy extends Sanitizer {
|
||||
ExcludeTarFilePy() { this.getLocation().getFile().getBaseName() = "tarfile.py" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A sink capturing method calls to `extractall`.
|
||||
*
|
||||
* For a call to `file.extractall` without arguments, `file` is considered a sink.
|
||||
*/
|
||||
class ExtractAllSink extends Sink {
|
||||
ExtractAllSink() {
|
||||
exists(DataFlow::CallCfgNode call |
|
||||
call =
|
||||
API::moduleImport("tarfile")
|
||||
.getMember("open")
|
||||
.getReturn()
|
||||
.getMember("extractall")
|
||||
.getACall() and
|
||||
not exists(call.getArg(_)) and
|
||||
not exists(call.getArgByName(_)) and
|
||||
this = call.(DataFlow::MethodCallNode).getObject()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument to `extract` is considered a sink.
|
||||
*/
|
||||
class ExtractSink extends Sink {
|
||||
ExtractSink() {
|
||||
exists(DataFlow::CallCfgNode call |
|
||||
call =
|
||||
API::moduleImport("tarfile").getMember("open").getReturn().getMember("extract").getACall() and
|
||||
this = call.getArg(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** The `members` argument `extractall` is considered a sink. */
|
||||
class ExtractMembersSink extends Sink {
|
||||
ExtractMembersSink() {
|
||||
exists(DataFlow::CallCfgNode call |
|
||||
call =
|
||||
API::moduleImport("tarfile")
|
||||
.getMember("open")
|
||||
.getReturn()
|
||||
.getMember("extractall")
|
||||
.getACall() and
|
||||
this in [call.getArg(0), call.getArgByName("members")]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `g` clears taint for `tarInfo`.
|
||||
*
|
||||
* The test `if <check_path>(info.name)` should clear taint for `info`,
|
||||
* where `<check_path>` is any function matching `"%path"`.
|
||||
* `info` is assumed to be a `TarInfo` instance.
|
||||
*/
|
||||
predicate tarFileInfoSanitizer(DataFlow::GuardNode g, ControlFlowNode tarInfo, boolean branch) {
|
||||
exists(CallNode call, AttrNode attr |
|
||||
g = call and
|
||||
// We must test the name of the tar info object.
|
||||
attr = call.getAnArg() and
|
||||
attr.getName() = "name" and
|
||||
attr.getObject() = tarInfo
|
||||
|
|
||||
// The assumption that any test that matches %path is a sanitizer might be too broad.
|
||||
call.getAChild*().(AttrNode).getName().matches("%path")
|
||||
or
|
||||
call.getAChild*().(NameNode).getId().matches("%path")
|
||||
) and
|
||||
branch = false
|
||||
}
|
||||
|
||||
/**
|
||||
* A sanitizer guard heuristic.
|
||||
*
|
||||
* The test `if <check_path>(info.name)` should clear taint for `info`,
|
||||
* where `<check_path>` is any function matching `"%path"`.
|
||||
* `info` is assumed to be a `TarInfo` instance.
|
||||
*/
|
||||
class TarFileInfoSanitizer extends Sanitizer {
|
||||
TarFileInfoSanitizer() {
|
||||
this = DataFlow::BarrierGuard<tarFileInfoSanitizer/3>::getABarrierNode()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Provides a taint-tracking configuration for detecting "command injection" vulnerabilities.
|
||||
*
|
||||
* Note, for performance reasons: only import this file if
|
||||
* `TarSlip::Configuration` is needed, otherwise
|
||||
* `TarSlipCustomizations` should be imported instead.
|
||||
*/
|
||||
|
||||
private import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
import TarSlipCustomizations::TarSlip
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for detecting "command injection" vulnerabilities.
|
||||
*/
|
||||
class Configuration extends TaintTracking::Configuration {
|
||||
Configuration() { this = "TarSlip" }
|
||||
|
||||
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 }
|
||||
}
|
||||
@@ -1,3 +1,25 @@
|
||||
## 0.4.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* Contextual queries and the query libraries they depend on have been moved to the `codeql/python-all` package.
|
||||
|
||||
## 0.3.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* Contextual queries and the query libraries they depend on have been moved to the `codeql/python-all` package.
|
||||
|
||||
## 0.2.0
|
||||
|
||||
### Major Analysis Improvements
|
||||
|
||||
* Improved library modeling for the query "Request without certificate validation" (`py/request-without-cert-validation`), so it now also covers `httpx`, `aiohttp.client`, and `urllib3`.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The query "Use of a broken or weak cryptographic algorithm" (`py/weak-cryptographic-algorithm`) now reports if a cryptographic operation is potentially insecure due to use of a weak block mode.
|
||||
|
||||
## 0.1.4
|
||||
|
||||
## 0.1.3
|
||||
|
||||
@@ -53,7 +53,7 @@ predicate matchesBeginningOfString(RegExpTerm term) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given sequence contains top-level domain preceded by a dot, such as `.com`,
|
||||
* Holds if the given sequence `seq` contains top-level domain preceded by a dot, such as `.com`,
|
||||
* excluding cases where this is at the very beginning of the regexp.
|
||||
*
|
||||
* `i` is bound to the index of the last child in the top-level domain part.
|
||||
|
||||
@@ -13,170 +13,10 @@
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.security.Paths
|
||||
import semmle.python.dataflow.TaintTracking
|
||||
import semmle.python.security.strings.Basic
|
||||
import semmle.python.security.dataflow.TarSlipQuery
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/** A TaintKind to represent open tarfile objects. That is, the result of calling `tarfile.open(...)` */
|
||||
class OpenTarFile extends TaintKind {
|
||||
OpenTarFile() { this = "tarfile.open" }
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
name = "getmember" and result instanceof TarFileInfo
|
||||
or
|
||||
name = "getmembers" and result.(SequenceKind).getItem() instanceof TarFileInfo
|
||||
}
|
||||
|
||||
override ClassValue getType() { result = Value::named("tarfile.TarFile") }
|
||||
|
||||
override TaintKind getTaintForIteration() { result instanceof TarFileInfo }
|
||||
}
|
||||
|
||||
/** The source of open tarfile objects. That is, any call to `tarfile.open(...)` */
|
||||
class TarfileOpen extends TaintSource {
|
||||
TarfileOpen() {
|
||||
Value::named("tarfile.open").getACall() = this and
|
||||
/*
|
||||
* If argument refers to a string object, then it's a hardcoded path and
|
||||
* this tarfile is safe.
|
||||
*/
|
||||
|
||||
not this.(CallNode).getAnArg().pointsTo(any(StringValue str)) and
|
||||
/* Ignore opens within the tarfile module itself */
|
||||
not this.(ControlFlowNode).getLocation().getFile().getBaseName() = "tarfile.py"
|
||||
}
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof OpenTarFile }
|
||||
}
|
||||
|
||||
class TarFileInfo extends TaintKind {
|
||||
TarFileInfo() { this = "tarfile.entry" }
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) { name = "next" and result = this }
|
||||
|
||||
override TaintKind getTaintOfAttribute(string name) {
|
||||
name = "name" and result instanceof TarFileInfo
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* For efficiency we don't want to track the flow of taint
|
||||
* around the tarfile module.
|
||||
*/
|
||||
|
||||
class ExcludeTarFilePy extends Sanitizer {
|
||||
ExcludeTarFilePy() { this = "Tar sanitizer" }
|
||||
|
||||
override predicate sanitizingNode(TaintKind taint, ControlFlowNode node) {
|
||||
node.getLocation().getFile().getBaseName() = "tarfile.py" and
|
||||
(
|
||||
taint instanceof OpenTarFile
|
||||
or
|
||||
taint instanceof TarFileInfo
|
||||
or
|
||||
taint.(SequenceKind).getItem() instanceof TarFileInfo
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/* Any call to an extractall method */
|
||||
class ExtractAllSink extends TaintSink {
|
||||
ExtractAllSink() {
|
||||
exists(CallNode call |
|
||||
this = call.getFunction().(AttrNode).getObject("extractall") and
|
||||
not exists(call.getAnArg())
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) { kind instanceof OpenTarFile }
|
||||
}
|
||||
|
||||
/* Argument to extract method */
|
||||
class ExtractSink extends TaintSink {
|
||||
CallNode call;
|
||||
|
||||
ExtractSink() {
|
||||
call.getFunction().(AttrNode).getName() = "extract" and
|
||||
this = call.getArg(0)
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) { kind instanceof TarFileInfo }
|
||||
}
|
||||
|
||||
/* Members argument to extract method */
|
||||
class ExtractMembersSink extends TaintSink {
|
||||
CallNode call;
|
||||
|
||||
ExtractMembersSink() {
|
||||
call.getFunction().(AttrNode).getName() = "extractall" and
|
||||
(this = call.getArg(0) or this = call.getArgByName("members"))
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) {
|
||||
kind.(SequenceKind).getItem() instanceof TarFileInfo
|
||||
or
|
||||
kind instanceof OpenTarFile
|
||||
}
|
||||
}
|
||||
|
||||
class TarFileInfoSanitizer extends Sanitizer {
|
||||
TarFileInfoSanitizer() { this = "TarInfo sanitizer" }
|
||||
|
||||
/* The test `if <path_sanitizing_test>:` clears taint on its `false` edge. */
|
||||
override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) {
|
||||
taint instanceof TarFileInfo and
|
||||
clears_taint_on_false_edge(test.getTest(), test.getSense())
|
||||
}
|
||||
|
||||
private predicate clears_taint_on_false_edge(ControlFlowNode test, boolean sense) {
|
||||
path_sanitizing_test(test) and
|
||||
sense = false
|
||||
or
|
||||
// handle `not` (also nested)
|
||||
test.(UnaryExprNode).getNode().getOp() instanceof Not and
|
||||
clears_taint_on_false_edge(test.(UnaryExprNode).getOperand(), sense.booleanNot())
|
||||
}
|
||||
}
|
||||
|
||||
private predicate path_sanitizing_test(ControlFlowNode test) {
|
||||
/* Assume that any test with "path" in it is a sanitizer */
|
||||
test.getAChild+().(AttrNode).getName().matches("%path")
|
||||
or
|
||||
test.getAChild+().(NameNode).getId().matches("%path")
|
||||
}
|
||||
|
||||
class TarSlipConfiguration extends TaintTracking::Configuration {
|
||||
TarSlipConfiguration() { this = "TarSlip configuration" }
|
||||
|
||||
override predicate isSource(TaintTracking::Source source) { source instanceof TarfileOpen }
|
||||
|
||||
override predicate isSink(TaintTracking::Sink sink) {
|
||||
sink instanceof ExtractSink or
|
||||
sink instanceof ExtractAllSink or
|
||||
sink instanceof ExtractMembersSink
|
||||
}
|
||||
|
||||
override predicate isSanitizer(Sanitizer sanitizer) {
|
||||
sanitizer instanceof TarFileInfoSanitizer
|
||||
or
|
||||
sanitizer instanceof ExcludeTarFilePy
|
||||
}
|
||||
|
||||
override predicate isBarrier(DataFlow::Node node) {
|
||||
// Avoid flow into the tarfile module
|
||||
exists(ParameterDefinition def |
|
||||
node.asVariable().getDefinition() = def
|
||||
or
|
||||
node.asCfgNode() = def.getDefiningNode()
|
||||
|
|
||||
def.getScope() = Value::named("tarfile.open").(CallableValue).getScope()
|
||||
or
|
||||
def.isSelf() and def.getScope().getEnclosingModule().getName() = "tarfile"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from TarSlipConfiguration config, TaintedPathSource src, TaintedPathSink sink
|
||||
where config.hasFlowPath(src, sink)
|
||||
select sink.getSink(), src, sink, "Extraction of tarfile from $@", src.getSource(),
|
||||
from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where config.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "Extraction of tarfile from $@", source.getNode(),
|
||||
"a potentially untrusted source"
|
||||
|
||||
@@ -42,7 +42,7 @@ where
|
||||
not exists(call.getArgByName("autoescape"))
|
||||
or
|
||||
call.getKeywordParameter("autoescape")
|
||||
.getAValueReachingRhs()
|
||||
.getAValueReachingSink()
|
||||
.asExpr()
|
||||
.(ImmutableLiteral)
|
||||
.booleanValue() = false
|
||||
|
||||
@@ -18,9 +18,9 @@ import semmle.python.dataflow.new.TaintTracking
|
||||
API::Node libPam() {
|
||||
exists(API::CallNode findLibCall, API::CallNode cdllCall |
|
||||
findLibCall = API::moduleImport("ctypes").getMember("util").getMember("find_library").getACall() and
|
||||
findLibCall.getParameter(0).getAValueReachingRhs().asExpr().(StrConst).getText() = "pam" and
|
||||
findLibCall.getParameter(0).getAValueReachingSink().asExpr().(StrConst).getText() = "pam" and
|
||||
cdllCall = API::moduleImport("ctypes").getMember("CDLL").getACall() and
|
||||
cdllCall.getParameter(0).getAValueReachingRhs() = findLibCall
|
||||
cdllCall.getParameter(0).getAValueReachingSink() = findLibCall
|
||||
|
|
||||
result = cdllCall.getReturn()
|
||||
)
|
||||
|
||||
@@ -29,7 +29,7 @@ where
|
||||
call = paramikoSSHClientInstance().getMember("set_missing_host_key_policy").getACall() and
|
||||
arg in [call.getArg(0), call.getArgByName("policy")] and
|
||||
(
|
||||
arg = unsafe_paramiko_policy(name).getAUse() or
|
||||
arg = unsafe_paramiko_policy(name).getReturn().getAUse()
|
||||
arg = unsafe_paramiko_policy(name).getAValueReachableFromSource() or
|
||||
arg = unsafe_paramiko_policy(name).getReturn().getAValueReachableFromSource()
|
||||
)
|
||||
select call, "Setting missing host key policy to " + name + " may be unsafe."
|
||||
|
||||
@@ -17,7 +17,8 @@ class PyOpenSSLContextCreation extends ContextCreation, DataFlow::CallCfgNode {
|
||||
protocolArg in [this.getArg(0), this.getArgByName("method")]
|
||||
|
|
||||
protocolArg in [
|
||||
pyo.specific_version(result).getAUse(), pyo.unspecific_version(result).getAUse()
|
||||
pyo.specific_version(result).getAValueReachableFromSource(),
|
||||
pyo.unspecific_version(result).getAValueReachableFromSource()
|
||||
]
|
||||
)
|
||||
}
|
||||
@@ -43,9 +44,10 @@ class SetOptionsCall extends ProtocolRestriction, DataFlow::CallCfgNode {
|
||||
}
|
||||
|
||||
override ProtocolVersion getRestriction() {
|
||||
API::moduleImport("OpenSSL").getMember("SSL").getMember("OP_NO_" + result).getAUse() in [
|
||||
this.getArg(0), this.getArgByName("options")
|
||||
]
|
||||
API::moduleImport("OpenSSL")
|
||||
.getMember("SSL")
|
||||
.getMember("OP_NO_" + result)
|
||||
.getAValueReachableFromSource() in [this.getArg(0), this.getArgByName("options")]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,10 @@ class SSLContextCreation extends ContextCreation, DataFlow::CallCfgNode {
|
||||
protocolArg in [this.getArg(0), this.getArgByName("protocol")]
|
||||
|
|
||||
protocolArg =
|
||||
[ssl.specific_version(result).getAUse(), ssl.unspecific_version(result).getAUse()]
|
||||
[
|
||||
ssl.specific_version(result).getAValueReachableFromSource(),
|
||||
ssl.unspecific_version(result).getAValueReachableFromSource()
|
||||
]
|
||||
)
|
||||
or
|
||||
not exists(this.getArg(_)) and
|
||||
@@ -54,7 +57,11 @@ class OptionsAugOr extends ProtocolRestriction, DataFlow::CfgNode {
|
||||
aa.getTarget() = attr.getNode() and
|
||||
attr.getName() = "options" and
|
||||
attr.getObject() = node and
|
||||
flag = API::moduleImport("ssl").getMember("OP_NO_" + restriction).getAUse().asExpr() and
|
||||
flag =
|
||||
API::moduleImport("ssl")
|
||||
.getMember("OP_NO_" + restriction)
|
||||
.getAValueReachableFromSource()
|
||||
.asExpr() and
|
||||
(
|
||||
aa.getValue() = flag
|
||||
or
|
||||
@@ -79,7 +86,11 @@ class OptionsAugAndNot extends ProtocolUnrestriction, DataFlow::CfgNode {
|
||||
attr.getObject() = node and
|
||||
notFlag.getOp() instanceof Invert and
|
||||
notFlag.getOperand() = flag and
|
||||
flag = API::moduleImport("ssl").getMember("OP_NO_" + restriction).getAUse().asExpr() and
|
||||
flag =
|
||||
API::moduleImport("ssl")
|
||||
.getMember("OP_NO_" + restriction)
|
||||
.getAValueReachableFromSource()
|
||||
.asExpr() and
|
||||
(
|
||||
aa.getValue() = notFlag
|
||||
or
|
||||
@@ -134,7 +145,10 @@ class ContextSetVersion extends ProtocolRestriction, ProtocolUnrestriction, Data
|
||||
this = aw.getObject() and
|
||||
aw.getAttributeName() = "minimum_version" and
|
||||
aw.getValue() =
|
||||
API::moduleImport("ssl").getMember("TLSVersion").getMember(restriction).getAUse()
|
||||
API::moduleImport("ssl")
|
||||
.getMember("TLSVersion")
|
||||
.getMember(restriction)
|
||||
.getAValueReachableFromSource()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -188,7 +202,8 @@ class Ssl extends TlsLibrary {
|
||||
|
||||
override DataFlow::CallCfgNode insecure_connection_creation(ProtocolVersion version) {
|
||||
result = API::moduleImport("ssl").getMember("wrap_socket").getACall() and
|
||||
this.specific_version(version).getAUse() = result.getArgByName("ssl_version") and
|
||||
this.specific_version(version).getAValueReachableFromSource() =
|
||||
result.getArgByName("ssl_version") and
|
||||
version.isInsecure()
|
||||
}
|
||||
|
||||
|
||||
@@ -36,13 +36,13 @@ string permissive_permission(int p) {
|
||||
|
||||
predicate chmod_call(API::CallNode call, string name, int mode) {
|
||||
call = API::moduleImport("os").getMember("chmod").getACall() and
|
||||
mode = call.getParameter(1, "mode").getAValueReachingRhs().asExpr().(IntegerLiteral).getValue() and
|
||||
mode = call.getParameter(1, "mode").getAValueReachingSink().asExpr().(IntegerLiteral).getValue() and
|
||||
name = "chmod"
|
||||
}
|
||||
|
||||
predicate open_call(API::CallNode call, string name, int mode) {
|
||||
call = API::moduleImport("os").getMember("open").getACall() and
|
||||
mode = call.getParameter(2, "mode").getAValueReachingRhs().asExpr().(IntegerLiteral).getValue() and
|
||||
mode = call.getParameter(2, "mode").getAValueReachingSink().asExpr().(IntegerLiteral).getValue() and
|
||||
name = "open"
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import python
|
||||
import DefinitionTracking
|
||||
import analysis.DefinitionTracking
|
||||
|
||||
predicate uniqueness_error(int number, string what, string problem) {
|
||||
what in [
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
import python
|
||||
import DefinitionTracking
|
||||
import analysis.DefinitionTracking
|
||||
|
||||
from NiceLocationExpr use, Definition defn, string kind
|
||||
where defn = definitionOf(use, kind)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import python
|
||||
import DefinitionTracking
|
||||
import analysis.DefinitionTracking
|
||||
|
||||
predicate want_to_have_definition(Expr e) {
|
||||
/* not builtin object like len, tuple, etc. */
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The query "Use of a broken or weak cryptographic algorithm" (`py/weak-cryptographic-algorithm`) now report if a cryptographic operation is potentially insecure due to use of a weak block mode.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: majorAnalysis
|
||||
---
|
||||
* Improved library modeling for the query "Request without certificate validation" (`py/request-without-cert-validation`), so it now also covers `httpx`, `aiohttp.client`, and `urllib3`.
|
||||
9
python/ql/src/change-notes/released/0.2.0.md
Normal file
9
python/ql/src/change-notes/released/0.2.0.md
Normal file
@@ -0,0 +1,9 @@
|
||||
## 0.2.0
|
||||
|
||||
### Major Analysis Improvements
|
||||
|
||||
* Improved library modeling for the query "Request without certificate validation" (`py/request-without-cert-validation`), so it now also covers `httpx`, `aiohttp.client`, and `urllib3`.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The query "Use of a broken or weak cryptographic algorithm" (`py/weak-cryptographic-algorithm`) now reports if a cryptographic operation is potentially insecure due to use of a weak block mode.
|
||||
5
python/ql/src/change-notes/released/0.3.0.md
Normal file
5
python/ql/src/change-notes/released/0.3.0.md
Normal file
@@ -0,0 +1,5 @@
|
||||
## 0.3.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* Contextual queries and the query libraries they depend on have been moved to the `codeql/python-all` package.
|
||||
5
python/ql/src/change-notes/released/0.4.0.md
Normal file
5
python/ql/src/change-notes/released/0.4.0.md
Normal file
@@ -0,0 +1,5 @@
|
||||
## 0.4.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* Contextual queries and the query libraries they depend on have been moved to the `codeql/python-all` package.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.1.4
|
||||
lastReleaseVersion: 0.4.0
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
blob_client = blob_service_client.get_blob_client(container=container_name, blob=blob_name)
|
||||
blob_client.require_encryption = True
|
||||
blob_client.key_encryption_key = kek
|
||||
# GOOD: Must use `encryption_version` set to `2.0`
|
||||
blob_client.encryption_version = '2.0' # Use Version 2.0!
|
||||
with open("decryptedcontentfile.txt", "rb") as stream:
|
||||
blob_client.upload_blob(stream, overwrite=OVERWRITE_EXISTING)
|
||||
@@ -0,0 +1,29 @@
|
||||
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>Azure Storage .NET, Java, and Python SDKs support encryption on the client with a customer-managed key that is maintained in Azure Key Vault or another key store.</p>
|
||||
<p>Current release versions of the Azure Storage SDKs use cipher block chaining (CBC mode) for client-side encryption (referred to as <code>v1</code>).</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>Consider switching to <code>v2</code> client-side encryption.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<sample src="UnsafeUsageOfClientSideEncryptionVersion.py" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
<li>
|
||||
<a href="http://aka.ms/azstorageclientencryptionblog">Azure Storage Client Encryption Blog.</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-30187">CVE-2022-30187</a>
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* @name Unsafe usage of v1 version of Azure Storage client-side encryption.
|
||||
* @description Using version v1 of Azure Storage client-side encryption is insecure, and may enable an attacker to decrypt encrypted data
|
||||
* @kind problem
|
||||
* @tags security
|
||||
* cryptography
|
||||
* external/cwe/cwe-327
|
||||
* @id py/azure-storage/unsafe-client-side-encryption-in-use
|
||||
* @problem.severity error
|
||||
* @precision medium
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.ApiGraphs
|
||||
|
||||
predicate isUnsafeClientSideAzureStorageEncryptionViaAttributes(Call call, AttrNode node) {
|
||||
exists(
|
||||
API::Node n, API::Node n2, Attribute a, AssignStmt astmt, API::Node uploadBlob,
|
||||
ControlFlowNode ctrlFlowNode, string s
|
||||
|
|
||||
s in ["key_encryption_key", "key_resolver_function"] and
|
||||
n =
|
||||
API::moduleImport("azure")
|
||||
.getMember("storage")
|
||||
.getMember("blob")
|
||||
.getMember("BlobClient")
|
||||
.getReturn()
|
||||
.getMember(s) and
|
||||
n2 =
|
||||
API::moduleImport("azure")
|
||||
.getMember("storage")
|
||||
.getMember("blob")
|
||||
.getMember("BlobClient")
|
||||
.getReturn()
|
||||
.getMember("upload_blob") and
|
||||
n.getAValueReachableFromSource().asExpr() = a and
|
||||
astmt.getATarget() = a and
|
||||
a.getAFlowNode() = node and
|
||||
uploadBlob =
|
||||
API::moduleImport("azure")
|
||||
.getMember("storage")
|
||||
.getMember("blob")
|
||||
.getMember("BlobClient")
|
||||
.getReturn()
|
||||
.getMember("upload_blob") and
|
||||
uploadBlob.getACall().asExpr() = call and
|
||||
ctrlFlowNode = call.getAFlowNode() and
|
||||
node.strictlyReaches(ctrlFlowNode) and
|
||||
node != ctrlFlowNode and
|
||||
not exists(
|
||||
AssignStmt astmt2, Attribute a2, AttrNode encryptionVersionSet, StrConst uc,
|
||||
API::Node encryptionVersion
|
||||
|
|
||||
uc = astmt2.getValue() and
|
||||
uc.getText() in ["'2.0'", "2.0"] and
|
||||
encryptionVersion =
|
||||
API::moduleImport("azure")
|
||||
.getMember("storage")
|
||||
.getMember("blob")
|
||||
.getMember("BlobClient")
|
||||
.getReturn()
|
||||
.getMember("encryption_version") and
|
||||
encryptionVersion.getAValueReachableFromSource().asExpr() = a2 and
|
||||
astmt2.getATarget() = a2 and
|
||||
a2.getAFlowNode() = encryptionVersionSet and
|
||||
encryptionVersionSet.strictlyReaches(ctrlFlowNode)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isUnsafeClientSideAzureStorageEncryptionViaObjectCreation(Call call, ControlFlowNode node) {
|
||||
exists(API::Node c, string s, Keyword k | k.getAFlowNode() = node |
|
||||
c.getACall().asExpr() = call and
|
||||
c = API::moduleImport("azure").getMember("storage").getMember("blob").getMember(s) and
|
||||
s in ["ContainerClient", "BlobClient", "BlobServiceClient"] and
|
||||
k.getArg() = "key_encryption_key" and
|
||||
k = call.getANamedArg() and
|
||||
not k.getValue() instanceof None and
|
||||
not exists(Keyword k2 | k2 = call.getANamedArg() |
|
||||
k2.getArg() = "encryption_version" and
|
||||
k2.getValue().(StrConst).getText() in ["'2.0'", "2.0"]
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
from Call call, ControlFlowNode node
|
||||
where
|
||||
isUnsafeClientSideAzureStorageEncryptionViaAttributes(call, node) or
|
||||
isUnsafeClientSideAzureStorageEncryptionViaObjectCreation(call, node)
|
||||
select node, "Unsafe usage of v1 version of Azure Storage client-side encryption."
|
||||
@@ -86,11 +86,13 @@ private module ExperimentalPrivateDjango {
|
||||
t.start() and
|
||||
(
|
||||
exists(SubscriptNode subscript |
|
||||
subscript.getObject() = baseClassRef().getReturn().getAUse().asCfgNode() and
|
||||
subscript.getObject() =
|
||||
baseClassRef().getReturn().getAValueReachableFromSource().asCfgNode() and
|
||||
result.asCfgNode() = subscript
|
||||
)
|
||||
or
|
||||
result.(DataFlow::AttrRead).getObject() = baseClassRef().getReturn().getAUse()
|
||||
result.(DataFlow::AttrRead).getObject() =
|
||||
baseClassRef().getReturn().getAValueReachableFromSource()
|
||||
)
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = headerInstance(t2).track(t2, t))
|
||||
|
||||
@@ -29,7 +29,11 @@ module ExperimentalFlask {
|
||||
|
||||
/** Gets a reference to a header instance. */
|
||||
private DataFlow::LocalSourceNode headerInstance() {
|
||||
result = [Flask::Response::classRef(), flaskMakeResponse()].getReturn().getAMember().getAUse()
|
||||
result =
|
||||
[Flask::Response::classRef(), flaskMakeResponse()]
|
||||
.getReturn()
|
||||
.getAMember()
|
||||
.getAValueReachableFromSource()
|
||||
}
|
||||
|
||||
/** Gets a reference to a header instance call/subscript */
|
||||
|
||||
@@ -90,7 +90,9 @@ private module LDAP {
|
||||
|
||||
/**List of SSL-demanding options */
|
||||
private class LDAPSSLOptions extends DataFlow::Node {
|
||||
LDAPSSLOptions() { this = ldap().getMember("OPT_X_TLS_" + ["DEMAND", "HARD"]).getAUse() }
|
||||
LDAPSSLOptions() {
|
||||
this = ldap().getMember("OPT_X_TLS_" + ["DEMAND", "HARD"]).getAValueReachableFromSource()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -50,11 +50,11 @@ private module NoSql {
|
||||
t.start() and
|
||||
(
|
||||
exists(SubscriptNode subscript |
|
||||
subscript.getObject() = mongoClientInstance().getAUse().asCfgNode() and
|
||||
subscript.getObject() = mongoClientInstance().getAValueReachableFromSource().asCfgNode() and
|
||||
result.asCfgNode() = subscript
|
||||
)
|
||||
or
|
||||
result.(DataFlow::AttrRead).getObject() = mongoClientInstance().getAUse()
|
||||
result.(DataFlow::AttrRead).getObject() = mongoClientInstance().getAValueReachableFromSource()
|
||||
or
|
||||
result = mongoEngine().getMember(["get_db", "connect"]).getACall()
|
||||
or
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/python-queries
|
||||
version: 0.2.0-dev
|
||||
version: 0.4.1-dev
|
||||
groups:
|
||||
- python
|
||||
- queries
|
||||
|
||||
@@ -21,10 +21,10 @@ import semmle.python.ApiGraphs
|
||||
|
||||
private DataFlow::Node getNode(API::Node nd, string kind) {
|
||||
kind = "def" and
|
||||
result = nd.getARhs()
|
||||
result = nd.asSink()
|
||||
or
|
||||
kind = "use" and
|
||||
result = nd.getAUse()
|
||||
result = nd.getAValueReachableFromSource()
|
||||
}
|
||||
|
||||
private string getLocStr(Location loc) {
|
||||
|
||||
@@ -50,7 +50,7 @@ def test_non_eq2():
|
||||
if not ts == "safe":
|
||||
ensure_tainted(ts) # $ tainted
|
||||
else:
|
||||
ensure_not_tainted(ts) # $ SPURIOUS: tainted
|
||||
ensure_not_tainted(ts)
|
||||
|
||||
|
||||
def test_in_list():
|
||||
@@ -157,7 +157,7 @@ def test_not_in2():
|
||||
if not ts in ["safe", "also_safe"]:
|
||||
ensure_tainted(ts) # $ tainted
|
||||
else:
|
||||
ensure_not_tainted(ts) # $ SPURIOUS: tainted
|
||||
ensure_not_tainted(ts)
|
||||
|
||||
|
||||
def is_safe(x):
|
||||
|
||||
@@ -6,12 +6,20 @@ isSanitizer
|
||||
| TestTaintTrackingConfiguration | test.py:34:39:34:39 | ControlFlowNode for s |
|
||||
| TestTaintTrackingConfiguration | test.py:52:28:52:28 | ControlFlowNode for s |
|
||||
| TestTaintTrackingConfiguration | test.py:66:10:66:29 | ControlFlowNode for emulated_escaping() |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:30:28:30:28 | ControlFlowNode for s |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:45:28:45:28 | ControlFlowNode for s |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:50:28:50:28 | ControlFlowNode for s |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:89:28:89:28 | ControlFlowNode for s |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:100:28:100:28 | ControlFlowNode for s |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:145:28:145:28 | ControlFlowNode for s |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:33:28:33:28 | ControlFlowNode for s |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:40:28:40:28 | ControlFlowNode for s |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:48:28:48:28 | ControlFlowNode for s |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:53:28:53:28 | ControlFlowNode for s |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:92:28:92:28 | ControlFlowNode for s |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:103:28:103:28 | ControlFlowNode for s |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:111:28:111:28 | ControlFlowNode for s |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:130:28:130:28 | ControlFlowNode for s |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:137:28:137:28 | ControlFlowNode for s |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:148:28:148:28 | ControlFlowNode for s |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:155:28:155:28 | ControlFlowNode for s |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:151:28:151:28 | ControlFlowNode for s |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:158:28:158:28 | ControlFlowNode for s |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:167:24:167:24 | ControlFlowNode for s |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:176:24:176:24 | ControlFlowNode for s |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:185:24:185:24 | ControlFlowNode for s |
|
||||
| TestTaintTrackingConfiguration | test_logical.py:193:24:193:24 | ControlFlowNode for s |
|
||||
| TestTaintTrackingConfiguration | test_reference.py:31:28:31:28 | ControlFlowNode for s |
|
||||
|
||||
@@ -6,6 +6,12 @@ predicate isSafeCheck(DataFlow::GuardNode g, ControlFlowNode node, boolean branc
|
||||
branch = true
|
||||
}
|
||||
|
||||
predicate isUnsafeCheck(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) {
|
||||
g.(CallNode).getNode().getFunc().(Name).getId() in ["is_unsafe", "emulated_is_unsafe"] and
|
||||
node = g.(CallNode).getAnArg() and
|
||||
branch = false
|
||||
}
|
||||
|
||||
class CustomSanitizerOverrides extends TestTaintTrackingConfiguration {
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
exists(Call call |
|
||||
@@ -16,6 +22,8 @@ class CustomSanitizerOverrides extends TestTaintTrackingConfiguration {
|
||||
node.asExpr().(Call).getFunc().(Name).getId() = "emulated_escaping"
|
||||
or
|
||||
node = DataFlow::BarrierGuard<isSafeCheck/3>::getABarrierNode()
|
||||
or
|
||||
node = DataFlow::BarrierGuard<isUnsafeCheck/3>::getABarrierNode()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@ def random_choice():
|
||||
def is_safe(arg):
|
||||
return arg == "safe"
|
||||
|
||||
def is_unsafe(arg):
|
||||
return arg == TAINTED_STRING
|
||||
|
||||
|
||||
def test_basic():
|
||||
s = TAINTED_STRING
|
||||
@@ -34,7 +37,7 @@ def test_basic():
|
||||
if not is_safe(s):
|
||||
ensure_tainted(s) # $ tainted
|
||||
else:
|
||||
ensure_not_tainted(s) # $ SPURIOUS: tainted
|
||||
ensure_not_tainted(s)
|
||||
|
||||
|
||||
def test_if_in_depth():
|
||||
@@ -105,7 +108,7 @@ def test_and():
|
||||
ensure_tainted(s) # $ tainted
|
||||
else:
|
||||
# cannot be tainted
|
||||
ensure_not_tainted(s) # $ SPURIOUS: tainted
|
||||
ensure_not_tainted(s)
|
||||
|
||||
|
||||
def test_tricky():
|
||||
@@ -124,14 +127,14 @@ def test_nesting_not():
|
||||
s = TAINTED_STRING
|
||||
|
||||
if not(not(is_safe(s))):
|
||||
ensure_not_tainted(s) # $ SPURIOUS: tainted
|
||||
ensure_not_tainted(s)
|
||||
else:
|
||||
ensure_tainted(s) # $ tainted
|
||||
|
||||
if not(not(not(is_safe(s)))):
|
||||
ensure_tainted(s) # $ tainted
|
||||
else:
|
||||
ensure_not_tainted(s) # $ SPURIOUS: tainted
|
||||
ensure_not_tainted(s)
|
||||
|
||||
|
||||
# Adding `and True` makes the sanitizer trigger when it would otherwise not. See output in
|
||||
@@ -161,7 +164,16 @@ def test_with_return():
|
||||
if not is_safe(s):
|
||||
return
|
||||
|
||||
ensure_not_tainted(s) # $ SPURIOUS: tainted
|
||||
ensure_not_tainted(s)
|
||||
|
||||
|
||||
def test_with_return_neg():
|
||||
s = TAINTED_STRING
|
||||
|
||||
if is_unsafe(s):
|
||||
return
|
||||
|
||||
ensure_not_tainted(s)
|
||||
|
||||
|
||||
def test_with_exception():
|
||||
@@ -170,7 +182,15 @@ def test_with_exception():
|
||||
if not is_safe(s):
|
||||
raise Exception("unsafe")
|
||||
|
||||
ensure_not_tainted(s) # $ SPURIOUS: tainted
|
||||
ensure_not_tainted(s)
|
||||
|
||||
def test_with_exception_neg():
|
||||
s = TAINTED_STRING
|
||||
|
||||
if is_unsafe(s):
|
||||
raise Exception("unsafe")
|
||||
|
||||
ensure_not_tainted(s)
|
||||
|
||||
# Make tests runable
|
||||
|
||||
@@ -182,7 +202,12 @@ test_tricky()
|
||||
test_nesting_not()
|
||||
test_nesting_not_with_and_true()
|
||||
test_with_return()
|
||||
test_with_return_neg()
|
||||
try:
|
||||
test_with_exception()
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
test_with_exception_neg()
|
||||
except:
|
||||
pass
|
||||
|
||||
@@ -5,7 +5,7 @@ import semmle.python.ApiGraphs
|
||||
|
||||
private DataFlow::TypeTrackingNode module_tracker(TypeTracker t) {
|
||||
t.start() and
|
||||
result = API::moduleImport("module").getAnImmediateUse()
|
||||
result = API::moduleImport("module").asSource()
|
||||
or
|
||||
exists(TypeTracker t2 | result = module_tracker(t2).track(t2, t))
|
||||
}
|
||||
|
||||
@@ -65,6 +65,9 @@ def to_inner_scope():
|
||||
also_x = foo() # $ MISSING: tracked
|
||||
print(also_x) # $ MISSING: tracked
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Function decorator
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
def my_decorator(func):
|
||||
# This part doesn't make any sense in a normal decorator, but just shows how we
|
||||
@@ -135,7 +138,7 @@ class Bar(Foo):
|
||||
def track_self(self): # $ tracked_self
|
||||
self.meth1() # $ tracked_self
|
||||
super().meth2()
|
||||
super(Bar, self).foo3() # $ tracked_self
|
||||
super(Bar, self).meth3() # $ tracked_self
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Tracking of attribute lookup after "long" import chain
|
||||
|
||||
@@ -120,7 +120,7 @@ class TrackedSelfTest extends InlineExpectationsTest {
|
||||
/** Gets a reference to `foo` (fictive module). */
|
||||
private DataFlow::TypeTrackingNode foo(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = API::moduleImport("foo").getAnImmediateUse()
|
||||
result = API::moduleImport("foo").asSource()
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = foo(t2).track(t2, t))
|
||||
}
|
||||
@@ -131,7 +131,7 @@ DataFlow::Node foo() { foo(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
/** Gets a reference to `foo.bar` (fictive module). */
|
||||
private DataFlow::TypeTrackingNode foo_bar(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = API::moduleImport("foo").getMember("bar").getAnImmediateUse()
|
||||
result = API::moduleImport("foo").getMember("bar").asSource()
|
||||
or
|
||||
t.startInAttr("bar") and
|
||||
result = foo()
|
||||
@@ -145,7 +145,7 @@ DataFlow::Node foo_bar() { foo_bar(DataFlow::TypeTracker::end()).flowsTo(result)
|
||||
/** Gets a reference to `foo.bar.baz` (fictive attribute on `foo.bar` module). */
|
||||
private DataFlow::TypeTrackingNode foo_bar_baz(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = API::moduleImport("foo").getMember("bar").getMember("baz").getAnImmediateUse()
|
||||
result = API::moduleImport("foo").getMember("bar").getMember("baz").asSource()
|
||||
or
|
||||
t.startInAttr("baz") and
|
||||
result = foo_bar()
|
||||
|
||||
@@ -19,7 +19,7 @@ class MadSinkTest extends InlineExpectationsTest {
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(location.getFile().getRelativePath()) and
|
||||
exists(DataFlow::Node sink, string kind |
|
||||
sink = ModelOutput::getASinkNode(kind).getARhs() and
|
||||
sink = ModelOutput::getASinkNode(kind).asSink() and
|
||||
location = sink.getLocation() and
|
||||
element = sink.toString() and
|
||||
value = prettyNodeForInlineTest(sink) and
|
||||
@@ -38,7 +38,7 @@ class MadSourceTest extends InlineExpectationsTest {
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(location.getFile().getRelativePath()) and
|
||||
exists(DataFlow::Node source, string kind |
|
||||
source = ModelOutput::getASourceNode(kind).getAnImmediateUse() and
|
||||
source = ModelOutput::getASourceNode(kind).asSource() and
|
||||
location = source.getLocation() and
|
||||
element = source.toString() and
|
||||
value = prettyNodeForInlineTest(source) and
|
||||
|
||||
@@ -9,7 +9,7 @@ class ApiUseTest extends InlineExpectationsTest {
|
||||
override string getARelevantTag() { result = "use" }
|
||||
|
||||
private predicate relevant_node(API::Node a, DataFlow::Node n, Location l) {
|
||||
n = a.getAUse() and l = n.getLocation()
|
||||
n = a.getAValueReachableFromSource() and l = n.getLocation()
|
||||
}
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
|
||||
@@ -16,4 +16,19 @@ def internal():
|
||||
def my_internal_method(self): #$ def=moduleImport("pflask").getMember("views").getMember("View").getASubclass().getMember("my_internal_method")
|
||||
pass
|
||||
|
||||
int_instance = IntMyView() #$ use=moduleImport("pflask").getMember("views").getMember("View").getASubclass().getReturn()
|
||||
int_instance = IntMyView() #$ use=moduleImport("pflask").getMember("views").getMember("View").getASubclass().getReturn()
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Class decorator
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
def my_class_decorator(cls):
|
||||
print("dummy decorator")
|
||||
return cls
|
||||
|
||||
@my_class_decorator
|
||||
class MyViewWithDecorator(View): #$ use=moduleImport("flask").getMember("views").getMember("View").getASubclass()
|
||||
pass
|
||||
|
||||
class SubclassFromDecorated(MyViewWithDecorator): #$ use=moduleImport("flask").getMember("views").getMember("View").getASubclass().getASubclass()
|
||||
pass
|
||||
|
||||
@@ -87,11 +87,11 @@ class BasicTaintTracking extends TaintTracking::Configuration {
|
||||
BasicTaintTracking() { this = "BasicTaintTracking" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source = ModelOutput::getASourceNode("test-source").getAnImmediateUse()
|
||||
source = ModelOutput::getASourceNode("test-source").asSource()
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink = ModelOutput::getASinkNode("test-sink").getARhs()
|
||||
sink = ModelOutput::getASinkNode("test-sink").asSink()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,11 +100,11 @@ query predicate taintFlow(DataFlow::Node source, DataFlow::Node sink) {
|
||||
}
|
||||
|
||||
query predicate isSink(DataFlow::Node node, string kind) {
|
||||
node = ModelOutput::getASinkNode(kind).getARhs()
|
||||
node = ModelOutput::getASinkNode(kind).asSink()
|
||||
}
|
||||
|
||||
query predicate isSource(DataFlow::Node node, string kind) {
|
||||
node = ModelOutput::getASourceNode(kind).getAnImmediateUse()
|
||||
node = ModelOutput::getASourceNode(kind).asSource()
|
||||
}
|
||||
|
||||
class SyntaxErrorTest extends ModelInput::SinkModelCsv {
|
||||
|
||||
@@ -1,29 +1,30 @@
|
||||
edges
|
||||
| tarslip.py:12:7:12:39 | tarfile.open | tarslip.py:13:1:13:3 | tarfile.open |
|
||||
| tarslip.py:12:7:12:39 | tarfile.open | tarslip.py:13:1:13:3 | tarfile.open |
|
||||
| tarslip.py:16:7:16:39 | tarfile.open | tarslip.py:17:14:17:16 | tarfile.open |
|
||||
| tarslip.py:16:7:16:39 | tarfile.open | tarslip.py:17:14:17:16 | tarfile.open |
|
||||
| tarslip.py:17:1:17:17 | tarfile.entry | tarslip.py:18:17:18:21 | tarfile.entry |
|
||||
| tarslip.py:17:1:17:17 | tarfile.entry | tarslip.py:18:17:18:21 | tarfile.entry |
|
||||
| tarslip.py:17:14:17:16 | tarfile.open | tarslip.py:17:1:17:17 | tarfile.entry |
|
||||
| tarslip.py:17:14:17:16 | tarfile.open | tarslip.py:17:1:17:17 | tarfile.entry |
|
||||
| tarslip.py:33:7:33:39 | tarfile.open | tarslip.py:34:14:34:16 | tarfile.open |
|
||||
| tarslip.py:33:7:33:39 | tarfile.open | tarslip.py:34:14:34:16 | tarfile.open |
|
||||
| tarslip.py:34:1:34:17 | tarfile.entry | tarslip.py:37:17:37:21 | tarfile.entry |
|
||||
| tarslip.py:34:1:34:17 | tarfile.entry | tarslip.py:37:17:37:21 | tarfile.entry |
|
||||
| tarslip.py:34:14:34:16 | tarfile.open | tarslip.py:34:1:34:17 | tarfile.entry |
|
||||
| tarslip.py:34:14:34:16 | tarfile.open | tarslip.py:34:1:34:17 | tarfile.entry |
|
||||
| tarslip.py:40:7:40:39 | tarfile.open | tarslip.py:41:24:41:26 | tarfile.open |
|
||||
| tarslip.py:40:7:40:39 | tarfile.open | tarslip.py:41:24:41:26 | tarfile.open |
|
||||
| tarslip.py:56:7:56:39 | tarfile.open | tarslip.py:57:14:57:16 | tarfile.open |
|
||||
| tarslip.py:56:7:56:39 | tarfile.open | tarslip.py:57:14:57:16 | tarfile.open |
|
||||
| tarslip.py:57:1:57:17 | tarfile.entry | tarslip.py:59:21:59:25 | tarfile.entry |
|
||||
| tarslip.py:57:1:57:17 | tarfile.entry | tarslip.py:59:21:59:25 | tarfile.entry |
|
||||
| tarslip.py:57:14:57:16 | tarfile.open | tarslip.py:57:1:57:17 | tarfile.entry |
|
||||
| tarslip.py:57:14:57:16 | tarfile.open | tarslip.py:57:1:57:17 | tarfile.entry |
|
||||
| tarslip.py:12:7:12:39 | ControlFlowNode for Attribute() | tarslip.py:13:1:13:3 | ControlFlowNode for tar |
|
||||
| tarslip.py:16:7:16:39 | ControlFlowNode for Attribute() | tarslip.py:17:5:17:9 | GSSA Variable entry |
|
||||
| tarslip.py:17:5:17:9 | GSSA Variable entry | tarslip.py:18:17:18:21 | ControlFlowNode for entry |
|
||||
| tarslip.py:33:7:33:39 | ControlFlowNode for Attribute() | tarslip.py:34:5:34:9 | GSSA Variable entry |
|
||||
| tarslip.py:34:5:34:9 | GSSA Variable entry | tarslip.py:37:17:37:21 | ControlFlowNode for entry |
|
||||
| tarslip.py:40:7:40:39 | ControlFlowNode for Attribute() | tarslip.py:41:24:41:26 | ControlFlowNode for tar |
|
||||
| tarslip.py:56:7:56:39 | ControlFlowNode for Attribute() | tarslip.py:57:5:57:9 | GSSA Variable entry |
|
||||
| tarslip.py:57:5:57:9 | GSSA Variable entry | tarslip.py:59:21:59:25 | ControlFlowNode for entry |
|
||||
nodes
|
||||
| tarslip.py:12:7:12:39 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| tarslip.py:13:1:13:3 | ControlFlowNode for tar | semmle.label | ControlFlowNode for tar |
|
||||
| tarslip.py:16:7:16:39 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| tarslip.py:17:5:17:9 | GSSA Variable entry | semmle.label | GSSA Variable entry |
|
||||
| tarslip.py:18:17:18:21 | ControlFlowNode for entry | semmle.label | ControlFlowNode for entry |
|
||||
| tarslip.py:33:7:33:39 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| tarslip.py:34:5:34:9 | GSSA Variable entry | semmle.label | GSSA Variable entry |
|
||||
| tarslip.py:37:17:37:21 | ControlFlowNode for entry | semmle.label | ControlFlowNode for entry |
|
||||
| tarslip.py:40:7:40:39 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| tarslip.py:41:24:41:26 | ControlFlowNode for tar | semmle.label | ControlFlowNode for tar |
|
||||
| tarslip.py:56:7:56:39 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| tarslip.py:57:5:57:9 | GSSA Variable entry | semmle.label | GSSA Variable entry |
|
||||
| tarslip.py:59:21:59:25 | ControlFlowNode for entry | semmle.label | ControlFlowNode for entry |
|
||||
subpaths
|
||||
#select
|
||||
| tarslip.py:13:1:13:3 | tar | tarslip.py:12:7:12:39 | tarfile.open | tarslip.py:13:1:13:3 | tarfile.open | Extraction of tarfile from $@ | tarslip.py:12:7:12:39 | Attribute() | a potentially untrusted source |
|
||||
| tarslip.py:18:17:18:21 | entry | tarslip.py:16:7:16:39 | tarfile.open | tarslip.py:18:17:18:21 | tarfile.entry | Extraction of tarfile from $@ | tarslip.py:16:7:16:39 | Attribute() | a potentially untrusted source |
|
||||
| tarslip.py:37:17:37:21 | entry | tarslip.py:33:7:33:39 | tarfile.open | tarslip.py:37:17:37:21 | tarfile.entry | Extraction of tarfile from $@ | tarslip.py:33:7:33:39 | Attribute() | a potentially untrusted source |
|
||||
| tarslip.py:41:24:41:26 | tar | tarslip.py:40:7:40:39 | tarfile.open | tarslip.py:41:24:41:26 | tarfile.open | Extraction of tarfile from $@ | tarslip.py:40:7:40:39 | Attribute() | a potentially untrusted source |
|
||||
| tarslip.py:59:21:59:25 | entry | tarslip.py:56:7:56:39 | tarfile.open | tarslip.py:59:21:59:25 | tarfile.entry | Extraction of tarfile from $@ | tarslip.py:56:7:56:39 | Attribute() | a potentially untrusted source |
|
||||
| tarslip.py:13:1:13:3 | ControlFlowNode for tar | tarslip.py:12:7:12:39 | ControlFlowNode for Attribute() | tarslip.py:13:1:13:3 | ControlFlowNode for tar | Extraction of tarfile from $@ | tarslip.py:12:7:12:39 | ControlFlowNode for Attribute() | a potentially untrusted source |
|
||||
| tarslip.py:18:17:18:21 | ControlFlowNode for entry | tarslip.py:16:7:16:39 | ControlFlowNode for Attribute() | tarslip.py:18:17:18:21 | ControlFlowNode for entry | Extraction of tarfile from $@ | tarslip.py:16:7:16:39 | ControlFlowNode for Attribute() | a potentially untrusted source |
|
||||
| tarslip.py:37:17:37:21 | ControlFlowNode for entry | tarslip.py:33:7:33:39 | ControlFlowNode for Attribute() | tarslip.py:37:17:37:21 | ControlFlowNode for entry | Extraction of tarfile from $@ | tarslip.py:33:7:33:39 | ControlFlowNode for Attribute() | a potentially untrusted source |
|
||||
| tarslip.py:41:24:41:26 | ControlFlowNode for tar | tarslip.py:40:7:40:39 | ControlFlowNode for Attribute() | tarslip.py:41:24:41:26 | ControlFlowNode for tar | Extraction of tarfile from $@ | tarslip.py:40:7:40:39 | ControlFlowNode for Attribute() | a potentially untrusted source |
|
||||
| tarslip.py:59:21:59:25 | ControlFlowNode for entry | tarslip.py:56:7:56:39 | ControlFlowNode for Attribute() | tarslip.py:59:21:59:25 | ControlFlowNode for entry | Extraction of tarfile from $@ | tarslip.py:56:7:56:39 | ControlFlowNode for Attribute() | a potentially untrusted source |
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
semmle-extractor-options: -p ../lib/ --max-import-depth=3
|
||||
Reference in New Issue
Block a user