mirror of
https://github.com/github/codeql.git
synced 2025-12-24 04:36:35 +01:00
Merge pull request #9369 from asgerf/python/api-graph-api
Python: API graph renaming and documentation
This commit is contained in:
10
python/ql/lib/change-notes/2022-06-28-api-graph-api.md
Normal file
10
python/ql/lib/change-notes/2022-06-28-api-graph-api.md
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
category: deprecated
|
||||
---
|
||||
|
||||
- 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`
|
||||
@@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -152,7 +152,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 +182,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 +204,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())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user