mirror of
https://github.com/github/codeql.git
synced 2026-06-18 03:11:07 +02:00
merge in main
This commit is contained in:
@@ -136,6 +136,9 @@ module API {
|
||||
result = this.getASuccessor(Label::keywordParameter(name))
|
||||
}
|
||||
|
||||
/** Gets the node representing the self parameter */
|
||||
Node getSelfParameter() { result = this.getASuccessor(Label::selfParameter()) }
|
||||
|
||||
/**
|
||||
* Gets the number of parameters of the function represented by this node.
|
||||
*/
|
||||
@@ -321,6 +324,12 @@ module API {
|
||||
/** 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 = callee.getSelfParameter()
|
||||
}
|
||||
|
||||
/** Gets the API node for the keyword parameter `name` of this invocation. */
|
||||
Node getKeywordParameter(string name) {
|
||||
result = callee.getKeywordParameter(name) and
|
||||
@@ -345,6 +354,14 @@ module API {
|
||||
result = callee.getReturn() and
|
||||
result.getAnImmediateUse() = this
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of positional arguments of this call.
|
||||
*
|
||||
* Note: This is used for `WithArity[<n>]` in modeling-as-data, where we thought
|
||||
* including keyword arguments didn't make much sense.
|
||||
*/
|
||||
int getNumArgument() { result = count(this.getArg(_)) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -589,15 +606,24 @@ module API {
|
||||
exists(DataFlow::Node def, PY::CallableExpr fn |
|
||||
rhs(base, def) and fn = trackDefNode(def).asExpr()
|
||||
|
|
||||
exists(int i |
|
||||
lbl = Label::parameter(i) and
|
||||
exists(int i, int offset |
|
||||
if exists(PY::Parameter p | p = fn.getInnerScope().getAnArg() and p.isSelf())
|
||||
then offset = 1
|
||||
else offset = 0
|
||||
|
|
||||
lbl = Label::parameter(i - offset) and
|
||||
ref.asExpr() = fn.getInnerScope().getArg(i)
|
||||
)
|
||||
or
|
||||
exists(string name |
|
||||
exists(string name, PY::Parameter param |
|
||||
lbl = Label::keywordParameter(name) and
|
||||
ref.asExpr() = fn.getInnerScope().getArgByName(name)
|
||||
param = fn.getInnerScope().getArgByName(name) and
|
||||
not param.isSelf() and
|
||||
ref.asExpr() = param
|
||||
)
|
||||
or
|
||||
lbl = Label::selfParameter() and
|
||||
ref.asExpr() = any(PY::Parameter p | p = fn.getInnerScope().getAnArg() and p.isSelf())
|
||||
)
|
||||
or
|
||||
// Built-ins, treated as members of the module `builtins`
|
||||
@@ -664,6 +690,9 @@ module API {
|
||||
exists(string name | lbl = Label::keywordParameter(name) |
|
||||
arg = pred.getACall().getArgByName(name)
|
||||
)
|
||||
or
|
||||
lbl = Label::selfParameter() and
|
||||
arg = pred.getACall().(DataFlow::MethodCallNode).getObject()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -780,6 +809,7 @@ module API {
|
||||
or
|
||||
exists(any(PY::Function f).getArgByName(name))
|
||||
} or
|
||||
MkLabelSelfParameter() or
|
||||
MkLabelReturn() or
|
||||
MkLabelSubclass() or
|
||||
MkLabelAwait()
|
||||
@@ -837,6 +867,11 @@ module API {
|
||||
string getName() { result = name }
|
||||
}
|
||||
|
||||
/** A label for the self parameter. */
|
||||
class LabelSelfParameter extends ApiLabel, MkLabelSelfParameter {
|
||||
override string toString() { result = "getSelfParameter()" }
|
||||
}
|
||||
|
||||
/** A label that gets the return value of a function. */
|
||||
class LabelReturn extends ApiLabel, MkLabelReturn {
|
||||
override string toString() { result = "getReturn()" }
|
||||
@@ -876,6 +911,9 @@ module API {
|
||||
/** Gets the `parameter` edge label for the keyword parameter `name`. */
|
||||
LabelKeywordParameter keywordParameter(string name) { result.getName() = name }
|
||||
|
||||
/** Gets the edge label for the self parameter. */
|
||||
LabelSelfParameter selfParameter() { any() }
|
||||
|
||||
/** Gets the `return` edge label. */
|
||||
LabelReturn return() { any() }
|
||||
|
||||
|
||||
@@ -119,16 +119,21 @@ module Path {
|
||||
}
|
||||
|
||||
/** A data-flow node that checks that a path is safe to access. */
|
||||
class SafeAccessCheck extends DataFlow::BarrierGuard instanceof SafeAccessCheck::Range {
|
||||
override predicate checks(ControlFlowNode node, boolean branch) {
|
||||
SafeAccessCheck::Range.super.checks(node, branch)
|
||||
}
|
||||
class SafeAccessCheck extends DataFlow::ExprNode {
|
||||
SafeAccessCheck() { this = DataFlow::BarrierGuard<safeAccessCheck/3>::getABarrierNode() }
|
||||
}
|
||||
|
||||
private predicate safeAccessCheck(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) {
|
||||
g.(SafeAccessCheck::Range).checks(node, branch)
|
||||
}
|
||||
|
||||
/** Provides a class for modeling new path safety checks. */
|
||||
module SafeAccessCheck {
|
||||
/** A data-flow node that checks that a path is safe to access. */
|
||||
abstract class Range extends DataFlow::BarrierGuard { }
|
||||
abstract class Range extends DataFlow::GuardNode {
|
||||
/** Holds if this guard validates `node` upon evaluating to `branch`. */
|
||||
abstract predicate checks(ControlFlowNode node, boolean branch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1211,38 +1216,5 @@ module Cryptography {
|
||||
}
|
||||
}
|
||||
|
||||
import semmle.python.concepts.CryptoAlgorithms
|
||||
|
||||
/**
|
||||
* A data-flow node that is an application of a cryptographic algorithm. For example,
|
||||
* encryption, decryption, signature-validation.
|
||||
*
|
||||
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||
* extend `CryptographicOperation::Range` instead.
|
||||
*/
|
||||
class CryptographicOperation extends DataFlow::Node instanceof CryptographicOperation::Range {
|
||||
/** Gets the algorithm used, if it matches a known `CryptographicAlgorithm`. */
|
||||
CryptographicAlgorithm getAlgorithm() { result = super.getAlgorithm() }
|
||||
|
||||
/** Gets an input the algorithm is used on, for example the plain text input to be encrypted. */
|
||||
DataFlow::Node getAnInput() { result = super.getAnInput() }
|
||||
}
|
||||
|
||||
/** Provides classes for modeling new applications of a cryptographic algorithms. */
|
||||
module CryptographicOperation {
|
||||
/**
|
||||
* A data-flow node that is an application of a cryptographic algorithm. For example,
|
||||
* encryption, decryption, signature-validation.
|
||||
*
|
||||
* Extend this class to model new APIs. If you want to refine existing API models,
|
||||
* extend `CryptographicOperation` instead.
|
||||
*/
|
||||
abstract class Range extends DataFlow::Node {
|
||||
/** Gets the algorithm used, if it matches a known `CryptographicAlgorithm`. */
|
||||
abstract CryptographicAlgorithm getAlgorithm();
|
||||
|
||||
/** Gets an input the algorithm is used on, for example the plain text input to be encrypted. */
|
||||
abstract DataFlow::Node getAnInput();
|
||||
}
|
||||
}
|
||||
import semmle.python.internal.ConceptsShared::Cryptography
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ private import semmle.python.frameworks.Asyncpg
|
||||
private import semmle.python.frameworks.ClickhouseDriver
|
||||
private import semmle.python.frameworks.Cryptodome
|
||||
private import semmle.python.frameworks.Cryptography
|
||||
private import semmle.python.frameworks.data.ModelsAsData
|
||||
private import semmle.python.frameworks.Dill
|
||||
private import semmle.python.frameworks.Django
|
||||
private import semmle.python.frameworks.Fabric
|
||||
|
||||
@@ -184,7 +184,7 @@ class Import extends Import_ {
|
||||
* For example, for the import statement `import bar` which
|
||||
* is a relative import in package "foo", this would return
|
||||
* "foo.bar".
|
||||
* The import statment `from foo import bar` would return
|
||||
* The import statement `from foo import bar` would return
|
||||
* `foo` and `foo.bar`
|
||||
*/
|
||||
string getAnImportedModuleName() {
|
||||
|
||||
@@ -81,6 +81,9 @@ class EncryptionAlgorithm extends MkEncryptionAlgorithm, CryptographicAlgorithm
|
||||
override string getName() { result = name }
|
||||
|
||||
override predicate isWeak() { isWeak = true }
|
||||
|
||||
/** Holds if this algorithm is a stream cipher. */
|
||||
predicate isStreamCipher() { isStreamCipher(name) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -67,6 +67,6 @@ predicate isStrongPasswordHashingAlgorithm(string name) {
|
||||
predicate isWeakPasswordHashingAlgorithm(string name) { name = "EVPKDF" }
|
||||
|
||||
/**
|
||||
* Holds if `name` corresponds to a weak block cipher mode of operation.
|
||||
* Holds if `name` corresponds to a stream cipher.
|
||||
*/
|
||||
predicate isWeakBlockMode(string name) { name = "ECB" }
|
||||
predicate isStreamCipher(string name) { name = ["CHACHA", "RC4", "ARC4", "ARCFOUR", "RABBIT"] }
|
||||
|
||||
@@ -3,8 +3,44 @@
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
|
||||
private predicate stringConstCompare(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) {
|
||||
exists(CompareNode cn | cn = g |
|
||||
exists(StrConst str_const, Cmpop op |
|
||||
op = any(Eq eq) and branch = true
|
||||
or
|
||||
op = any(NotEq ne) and branch = false
|
||||
|
|
||||
cn.operands(str_const.getAFlowNode(), op, node)
|
||||
or
|
||||
cn.operands(node, op, str_const.getAFlowNode())
|
||||
)
|
||||
or
|
||||
exists(IterableNode str_const_iterable, Cmpop op |
|
||||
op = any(In in_) and branch = true
|
||||
or
|
||||
op = any(NotIn ni) and branch = false
|
||||
|
|
||||
forall(ControlFlowNode elem | elem = str_const_iterable.getAnElement() |
|
||||
elem.getNode() instanceof StrConst
|
||||
) and
|
||||
cn.operands(node, op, str_const_iterable)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** A validation of unknown node by comparing with a constant string value. */
|
||||
class StringConstCompare extends DataFlow::BarrierGuard, CompareNode {
|
||||
class StringConstCompareBarrier extends DataFlow::Node {
|
||||
StringConstCompareBarrier() {
|
||||
this = DataFlow::BarrierGuard<stringConstCompare/3>::getABarrierNode()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `StringConstCompareBarrier` instead.
|
||||
*
|
||||
* A validation of unknown node by comparing with a constant string value.
|
||||
*/
|
||||
deprecated class StringConstCompare extends DataFlow::BarrierGuard, CompareNode {
|
||||
ControlFlowNode checked_node;
|
||||
boolean safe_branch;
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@ module ArgumentPassing {
|
||||
/**
|
||||
* Gets the node representing the argument to `call` that is passed to the parameter at
|
||||
* (zero-based) index `paramN` in `callable`. If this is a positional argument, it must appear
|
||||
* at an index, `argN`, in `call` wich satisfies `paramN = mapping.getParamN(argN)`.
|
||||
* at an index, `argN`, in `call` which satisfies `paramN = mapping.getParamN(argN)`.
|
||||
*
|
||||
* `mapping` will be the identity for function calls, but not for method- or constructor calls,
|
||||
* where the first parameter is `self` and the first positional argument is passed to the second positional parameter.
|
||||
|
||||
@@ -90,14 +90,20 @@ abstract class Configuration extends string {
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/** Holds if data flow through nodes guarded by `guard` is prohibited. */
|
||||
predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited when
|
||||
* the flow state is `state`
|
||||
*/
|
||||
predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
@@ -335,6 +341,29 @@ private predicate outBarrier(NodeEx node, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
/** A bridge class to access the deprecated `isBarrierGuard`. */
|
||||
private class BarrierGuardGuardedNodeBridge extends Unit {
|
||||
abstract predicate guardedNode(Node n, Configuration config);
|
||||
|
||||
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
|
||||
}
|
||||
|
||||
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
|
||||
deprecated override predicate guardedNode(Node n, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
|
||||
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fullBarrier(NodeEx node, Configuration config) {
|
||||
exists(Node n | node.asNode() = n |
|
||||
@@ -348,10 +377,7 @@ private predicate fullBarrier(NodeEx node, Configuration config) {
|
||||
not config.isSink(n) and
|
||||
not config.isSink(n, _)
|
||||
or
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(n, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -360,10 +386,7 @@ private predicate stateBarrier(NodeEx node, FlowState state, Configuration confi
|
||||
exists(Node n | node.asNode() = n |
|
||||
config.isBarrier(n, state)
|
||||
or
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(n, state, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3854,16 +3877,11 @@ class PathNode extends TPathNode {
|
||||
/** Gets the associated configuration. */
|
||||
Configuration getConfiguration() { none() }
|
||||
|
||||
private PathNode getASuccessorIfHidden() {
|
||||
this.(PathNodeImpl).isHidden() and
|
||||
result = this.(PathNodeImpl).getASuccessorImpl()
|
||||
}
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() {
|
||||
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
|
||||
not this.(PathNodeImpl).isHidden() and
|
||||
not result.(PathNodeImpl).isHidden()
|
||||
result = this.(PathNodeImpl).getANonHiddenSuccessor() and
|
||||
reach(this) and
|
||||
reach(result)
|
||||
}
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
@@ -3871,7 +3889,18 @@ class PathNode extends TPathNode {
|
||||
}
|
||||
|
||||
abstract private class PathNodeImpl extends PathNode {
|
||||
abstract PathNode getASuccessorImpl();
|
||||
abstract PathNodeImpl getASuccessorImpl();
|
||||
|
||||
private PathNodeImpl getASuccessorIfHidden() {
|
||||
this.isHidden() and
|
||||
result = this.getASuccessorImpl()
|
||||
}
|
||||
|
||||
final PathNodeImpl getANonHiddenSuccessor() {
|
||||
result = this.getASuccessorImpl().getASuccessorIfHidden*() and
|
||||
not this.isHidden() and
|
||||
not result.isHidden()
|
||||
}
|
||||
|
||||
abstract NodeEx getNodeEx();
|
||||
|
||||
@@ -3914,15 +3943,17 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
}
|
||||
|
||||
/** Holds if `n` can reach a sink. */
|
||||
private predicate directReach(PathNode n) {
|
||||
n instanceof PathNodeSink or directReach(n.getASuccessor())
|
||||
private predicate directReach(PathNodeImpl n) {
|
||||
n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
|
||||
}
|
||||
|
||||
/** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */
|
||||
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
|
||||
|
||||
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
|
||||
private predicate pathSucc(PathNodeImpl n1, PathNode n2) {
|
||||
n1.getANonHiddenSuccessor() = n2 and directReach(n2)
|
||||
}
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
@@ -3931,7 +3962,7 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
|
||||
*/
|
||||
module PathGraph {
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(a) and reach(b) }
|
||||
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b }
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
@@ -4049,7 +4080,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNode getASuccessorImpl() { none() }
|
||||
override PathNodeImpl getASuccessorImpl() { none() }
|
||||
|
||||
override predicate isSource() { sourceNode(node, state, config) }
|
||||
}
|
||||
@@ -4365,8 +4396,8 @@ private module Subpaths {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate hasSuccessor(PathNode pred, PathNodeMid succ, NodeEx succNode) {
|
||||
succ = pred.getASuccessor() and
|
||||
private predicate hasSuccessor(PathNodeImpl pred, PathNodeMid succ, NodeEx succNode) {
|
||||
succ = pred.getANonHiddenSuccessor() and
|
||||
succNode = succ.getNodeEx()
|
||||
}
|
||||
|
||||
@@ -4375,9 +4406,9 @@ private module Subpaths {
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
|
||||
predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
|
||||
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
|
||||
pragma[only_bind_into](arg).getASuccessor() = pragma[only_bind_into](out0) and
|
||||
pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and
|
||||
subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
|
||||
hasSuccessor(pragma[only_bind_into](arg), par, p) and
|
||||
not ret.isHidden() and
|
||||
@@ -4390,12 +4421,12 @@ private module Subpaths {
|
||||
/**
|
||||
* Holds if `n` can reach a return node in a summarized subpath that can reach a sink.
|
||||
*/
|
||||
predicate retReach(PathNode n) {
|
||||
predicate retReach(PathNodeImpl n) {
|
||||
exists(PathNode out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
|
||||
or
|
||||
exists(PathNode mid |
|
||||
exists(PathNodeImpl mid |
|
||||
retReach(mid) and
|
||||
n.getASuccessor() = mid and
|
||||
n.getANonHiddenSuccessor() = mid and
|
||||
not subpaths(_, mid, _, _)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -90,14 +90,20 @@ abstract class Configuration extends string {
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/** Holds if data flow through nodes guarded by `guard` is prohibited. */
|
||||
predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited when
|
||||
* the flow state is `state`
|
||||
*/
|
||||
predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
@@ -335,6 +341,29 @@ private predicate outBarrier(NodeEx node, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
/** A bridge class to access the deprecated `isBarrierGuard`. */
|
||||
private class BarrierGuardGuardedNodeBridge extends Unit {
|
||||
abstract predicate guardedNode(Node n, Configuration config);
|
||||
|
||||
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
|
||||
}
|
||||
|
||||
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
|
||||
deprecated override predicate guardedNode(Node n, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
|
||||
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fullBarrier(NodeEx node, Configuration config) {
|
||||
exists(Node n | node.asNode() = n |
|
||||
@@ -348,10 +377,7 @@ private predicate fullBarrier(NodeEx node, Configuration config) {
|
||||
not config.isSink(n) and
|
||||
not config.isSink(n, _)
|
||||
or
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(n, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -360,10 +386,7 @@ private predicate stateBarrier(NodeEx node, FlowState state, Configuration confi
|
||||
exists(Node n | node.asNode() = n |
|
||||
config.isBarrier(n, state)
|
||||
or
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(n, state, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3854,16 +3877,11 @@ class PathNode extends TPathNode {
|
||||
/** Gets the associated configuration. */
|
||||
Configuration getConfiguration() { none() }
|
||||
|
||||
private PathNode getASuccessorIfHidden() {
|
||||
this.(PathNodeImpl).isHidden() and
|
||||
result = this.(PathNodeImpl).getASuccessorImpl()
|
||||
}
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() {
|
||||
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
|
||||
not this.(PathNodeImpl).isHidden() and
|
||||
not result.(PathNodeImpl).isHidden()
|
||||
result = this.(PathNodeImpl).getANonHiddenSuccessor() and
|
||||
reach(this) and
|
||||
reach(result)
|
||||
}
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
@@ -3871,7 +3889,18 @@ class PathNode extends TPathNode {
|
||||
}
|
||||
|
||||
abstract private class PathNodeImpl extends PathNode {
|
||||
abstract PathNode getASuccessorImpl();
|
||||
abstract PathNodeImpl getASuccessorImpl();
|
||||
|
||||
private PathNodeImpl getASuccessorIfHidden() {
|
||||
this.isHidden() and
|
||||
result = this.getASuccessorImpl()
|
||||
}
|
||||
|
||||
final PathNodeImpl getANonHiddenSuccessor() {
|
||||
result = this.getASuccessorImpl().getASuccessorIfHidden*() and
|
||||
not this.isHidden() and
|
||||
not result.isHidden()
|
||||
}
|
||||
|
||||
abstract NodeEx getNodeEx();
|
||||
|
||||
@@ -3914,15 +3943,17 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
}
|
||||
|
||||
/** Holds if `n` can reach a sink. */
|
||||
private predicate directReach(PathNode n) {
|
||||
n instanceof PathNodeSink or directReach(n.getASuccessor())
|
||||
private predicate directReach(PathNodeImpl n) {
|
||||
n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
|
||||
}
|
||||
|
||||
/** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */
|
||||
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
|
||||
|
||||
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
|
||||
private predicate pathSucc(PathNodeImpl n1, PathNode n2) {
|
||||
n1.getANonHiddenSuccessor() = n2 and directReach(n2)
|
||||
}
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
@@ -3931,7 +3962,7 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
|
||||
*/
|
||||
module PathGraph {
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(a) and reach(b) }
|
||||
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b }
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
@@ -4049,7 +4080,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNode getASuccessorImpl() { none() }
|
||||
override PathNodeImpl getASuccessorImpl() { none() }
|
||||
|
||||
override predicate isSource() { sourceNode(node, state, config) }
|
||||
}
|
||||
@@ -4365,8 +4396,8 @@ private module Subpaths {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate hasSuccessor(PathNode pred, PathNodeMid succ, NodeEx succNode) {
|
||||
succ = pred.getASuccessor() and
|
||||
private predicate hasSuccessor(PathNodeImpl pred, PathNodeMid succ, NodeEx succNode) {
|
||||
succ = pred.getANonHiddenSuccessor() and
|
||||
succNode = succ.getNodeEx()
|
||||
}
|
||||
|
||||
@@ -4375,9 +4406,9 @@ private module Subpaths {
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
|
||||
predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
|
||||
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
|
||||
pragma[only_bind_into](arg).getASuccessor() = pragma[only_bind_into](out0) and
|
||||
pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and
|
||||
subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
|
||||
hasSuccessor(pragma[only_bind_into](arg), par, p) and
|
||||
not ret.isHidden() and
|
||||
@@ -4390,12 +4421,12 @@ private module Subpaths {
|
||||
/**
|
||||
* Holds if `n` can reach a return node in a summarized subpath that can reach a sink.
|
||||
*/
|
||||
predicate retReach(PathNode n) {
|
||||
predicate retReach(PathNodeImpl n) {
|
||||
exists(PathNode out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
|
||||
or
|
||||
exists(PathNode mid |
|
||||
exists(PathNodeImpl mid |
|
||||
retReach(mid) and
|
||||
n.getASuccessor() = mid and
|
||||
n.getANonHiddenSuccessor() = mid and
|
||||
not subpaths(_, mid, _, _)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -90,14 +90,20 @@ abstract class Configuration extends string {
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/** Holds if data flow through nodes guarded by `guard` is prohibited. */
|
||||
predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited when
|
||||
* the flow state is `state`
|
||||
*/
|
||||
predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
@@ -335,6 +341,29 @@ private predicate outBarrier(NodeEx node, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
/** A bridge class to access the deprecated `isBarrierGuard`. */
|
||||
private class BarrierGuardGuardedNodeBridge extends Unit {
|
||||
abstract predicate guardedNode(Node n, Configuration config);
|
||||
|
||||
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
|
||||
}
|
||||
|
||||
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
|
||||
deprecated override predicate guardedNode(Node n, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
|
||||
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fullBarrier(NodeEx node, Configuration config) {
|
||||
exists(Node n | node.asNode() = n |
|
||||
@@ -348,10 +377,7 @@ private predicate fullBarrier(NodeEx node, Configuration config) {
|
||||
not config.isSink(n) and
|
||||
not config.isSink(n, _)
|
||||
or
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(n, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -360,10 +386,7 @@ private predicate stateBarrier(NodeEx node, FlowState state, Configuration confi
|
||||
exists(Node n | node.asNode() = n |
|
||||
config.isBarrier(n, state)
|
||||
or
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(n, state, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3854,16 +3877,11 @@ class PathNode extends TPathNode {
|
||||
/** Gets the associated configuration. */
|
||||
Configuration getConfiguration() { none() }
|
||||
|
||||
private PathNode getASuccessorIfHidden() {
|
||||
this.(PathNodeImpl).isHidden() and
|
||||
result = this.(PathNodeImpl).getASuccessorImpl()
|
||||
}
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() {
|
||||
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
|
||||
not this.(PathNodeImpl).isHidden() and
|
||||
not result.(PathNodeImpl).isHidden()
|
||||
result = this.(PathNodeImpl).getANonHiddenSuccessor() and
|
||||
reach(this) and
|
||||
reach(result)
|
||||
}
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
@@ -3871,7 +3889,18 @@ class PathNode extends TPathNode {
|
||||
}
|
||||
|
||||
abstract private class PathNodeImpl extends PathNode {
|
||||
abstract PathNode getASuccessorImpl();
|
||||
abstract PathNodeImpl getASuccessorImpl();
|
||||
|
||||
private PathNodeImpl getASuccessorIfHidden() {
|
||||
this.isHidden() and
|
||||
result = this.getASuccessorImpl()
|
||||
}
|
||||
|
||||
final PathNodeImpl getANonHiddenSuccessor() {
|
||||
result = this.getASuccessorImpl().getASuccessorIfHidden*() and
|
||||
not this.isHidden() and
|
||||
not result.isHidden()
|
||||
}
|
||||
|
||||
abstract NodeEx getNodeEx();
|
||||
|
||||
@@ -3914,15 +3943,17 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
}
|
||||
|
||||
/** Holds if `n` can reach a sink. */
|
||||
private predicate directReach(PathNode n) {
|
||||
n instanceof PathNodeSink or directReach(n.getASuccessor())
|
||||
private predicate directReach(PathNodeImpl n) {
|
||||
n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
|
||||
}
|
||||
|
||||
/** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */
|
||||
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
|
||||
|
||||
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
|
||||
private predicate pathSucc(PathNodeImpl n1, PathNode n2) {
|
||||
n1.getANonHiddenSuccessor() = n2 and directReach(n2)
|
||||
}
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
@@ -3931,7 +3962,7 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
|
||||
*/
|
||||
module PathGraph {
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(a) and reach(b) }
|
||||
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b }
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
@@ -4049,7 +4080,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNode getASuccessorImpl() { none() }
|
||||
override PathNodeImpl getASuccessorImpl() { none() }
|
||||
|
||||
override predicate isSource() { sourceNode(node, state, config) }
|
||||
}
|
||||
@@ -4365,8 +4396,8 @@ private module Subpaths {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate hasSuccessor(PathNode pred, PathNodeMid succ, NodeEx succNode) {
|
||||
succ = pred.getASuccessor() and
|
||||
private predicate hasSuccessor(PathNodeImpl pred, PathNodeMid succ, NodeEx succNode) {
|
||||
succ = pred.getANonHiddenSuccessor() and
|
||||
succNode = succ.getNodeEx()
|
||||
}
|
||||
|
||||
@@ -4375,9 +4406,9 @@ private module Subpaths {
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
|
||||
predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
|
||||
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
|
||||
pragma[only_bind_into](arg).getASuccessor() = pragma[only_bind_into](out0) and
|
||||
pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and
|
||||
subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
|
||||
hasSuccessor(pragma[only_bind_into](arg), par, p) and
|
||||
not ret.isHidden() and
|
||||
@@ -4390,12 +4421,12 @@ private module Subpaths {
|
||||
/**
|
||||
* Holds if `n` can reach a return node in a summarized subpath that can reach a sink.
|
||||
*/
|
||||
predicate retReach(PathNode n) {
|
||||
predicate retReach(PathNodeImpl n) {
|
||||
exists(PathNode out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
|
||||
or
|
||||
exists(PathNode mid |
|
||||
exists(PathNodeImpl mid |
|
||||
retReach(mid) and
|
||||
n.getASuccessor() = mid and
|
||||
n.getANonHiddenSuccessor() = mid and
|
||||
not subpaths(_, mid, _, _)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -90,14 +90,20 @@ abstract class Configuration extends string {
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/** Holds if data flow through nodes guarded by `guard` is prohibited. */
|
||||
predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited when
|
||||
* the flow state is `state`
|
||||
*/
|
||||
predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
@@ -335,6 +341,29 @@ private predicate outBarrier(NodeEx node, Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
/** A bridge class to access the deprecated `isBarrierGuard`. */
|
||||
private class BarrierGuardGuardedNodeBridge extends Unit {
|
||||
abstract predicate guardedNode(Node n, Configuration config);
|
||||
|
||||
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
|
||||
}
|
||||
|
||||
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
|
||||
deprecated override predicate guardedNode(Node n, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
|
||||
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fullBarrier(NodeEx node, Configuration config) {
|
||||
exists(Node n | node.asNode() = n |
|
||||
@@ -348,10 +377,7 @@ private predicate fullBarrier(NodeEx node, Configuration config) {
|
||||
not config.isSink(n) and
|
||||
not config.isSink(n, _)
|
||||
or
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(n, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -360,10 +386,7 @@ private predicate stateBarrier(NodeEx node, FlowState state, Configuration confi
|
||||
exists(Node n | node.asNode() = n |
|
||||
config.isBarrier(n, state)
|
||||
or
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(n, state, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3854,16 +3877,11 @@ class PathNode extends TPathNode {
|
||||
/** Gets the associated configuration. */
|
||||
Configuration getConfiguration() { none() }
|
||||
|
||||
private PathNode getASuccessorIfHidden() {
|
||||
this.(PathNodeImpl).isHidden() and
|
||||
result = this.(PathNodeImpl).getASuccessorImpl()
|
||||
}
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() {
|
||||
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
|
||||
not this.(PathNodeImpl).isHidden() and
|
||||
not result.(PathNodeImpl).isHidden()
|
||||
result = this.(PathNodeImpl).getANonHiddenSuccessor() and
|
||||
reach(this) and
|
||||
reach(result)
|
||||
}
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
@@ -3871,7 +3889,18 @@ class PathNode extends TPathNode {
|
||||
}
|
||||
|
||||
abstract private class PathNodeImpl extends PathNode {
|
||||
abstract PathNode getASuccessorImpl();
|
||||
abstract PathNodeImpl getASuccessorImpl();
|
||||
|
||||
private PathNodeImpl getASuccessorIfHidden() {
|
||||
this.isHidden() and
|
||||
result = this.getASuccessorImpl()
|
||||
}
|
||||
|
||||
final PathNodeImpl getANonHiddenSuccessor() {
|
||||
result = this.getASuccessorImpl().getASuccessorIfHidden*() and
|
||||
not this.isHidden() and
|
||||
not result.isHidden()
|
||||
}
|
||||
|
||||
abstract NodeEx getNodeEx();
|
||||
|
||||
@@ -3914,15 +3943,17 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
}
|
||||
|
||||
/** Holds if `n` can reach a sink. */
|
||||
private predicate directReach(PathNode n) {
|
||||
n instanceof PathNodeSink or directReach(n.getASuccessor())
|
||||
private predicate directReach(PathNodeImpl n) {
|
||||
n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
|
||||
}
|
||||
|
||||
/** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */
|
||||
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
|
||||
|
||||
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
|
||||
private predicate pathSucc(PathNodeImpl n1, PathNode n2) {
|
||||
n1.getANonHiddenSuccessor() = n2 and directReach(n2)
|
||||
}
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
@@ -3931,7 +3962,7 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
|
||||
*/
|
||||
module PathGraph {
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(a) and reach(b) }
|
||||
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b }
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
@@ -4049,7 +4080,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNode getASuccessorImpl() { none() }
|
||||
override PathNodeImpl getASuccessorImpl() { none() }
|
||||
|
||||
override predicate isSource() { sourceNode(node, state, config) }
|
||||
}
|
||||
@@ -4365,8 +4396,8 @@ private module Subpaths {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate hasSuccessor(PathNode pred, PathNodeMid succ, NodeEx succNode) {
|
||||
succ = pred.getASuccessor() and
|
||||
private predicate hasSuccessor(PathNodeImpl pred, PathNodeMid succ, NodeEx succNode) {
|
||||
succ = pred.getANonHiddenSuccessor() and
|
||||
succNode = succ.getNodeEx()
|
||||
}
|
||||
|
||||
@@ -4375,9 +4406,9 @@ private module Subpaths {
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
|
||||
predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
|
||||
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
|
||||
pragma[only_bind_into](arg).getASuccessor() = pragma[only_bind_into](out0) and
|
||||
pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and
|
||||
subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
|
||||
hasSuccessor(pragma[only_bind_into](arg), par, p) and
|
||||
not ret.isHidden() and
|
||||
@@ -4390,12 +4421,12 @@ private module Subpaths {
|
||||
/**
|
||||
* Holds if `n` can reach a return node in a summarized subpath that can reach a sink.
|
||||
*/
|
||||
predicate retReach(PathNode n) {
|
||||
predicate retReach(PathNodeImpl n) {
|
||||
exists(PathNode out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
|
||||
or
|
||||
exists(PathNode mid |
|
||||
exists(PathNodeImpl mid |
|
||||
retReach(mid) and
|
||||
n.getASuccessor() = mid and
|
||||
n.getANonHiddenSuccessor() = mid and
|
||||
not subpaths(_, mid, _, _)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -216,10 +216,9 @@ private module LambdaFlow {
|
||||
or
|
||||
// jump step
|
||||
exists(Node mid, DataFlowType t0 |
|
||||
revLambdaFlow(lambdaCall, kind, mid, t0, _, _, _) and
|
||||
revLambdaFlow(lambdaCall, kind, mid, t0, _, _, lastCall) and
|
||||
toReturn = false and
|
||||
toJump = true and
|
||||
lastCall = TDataFlowCallNone()
|
||||
toJump = true
|
||||
|
|
||||
jumpStepCached(node, mid) and
|
||||
t = t0
|
||||
@@ -305,7 +304,7 @@ cached
|
||||
private module Cached {
|
||||
/**
|
||||
* If needed, call this predicate from `DataFlowImplSpecific.qll` in order to
|
||||
* force a stage-dependency on the `DataFlowImplCommon.qll` stage and therby
|
||||
* force a stage-dependency on the `DataFlowImplCommon.qll` stage and thereby
|
||||
* collapsing the two stages.
|
||||
*/
|
||||
cached
|
||||
@@ -789,24 +788,31 @@ private module Cached {
|
||||
cached
|
||||
predicate readSet(Node node1, ContentSet c, Node node2) { readStep(node1, c, node2) }
|
||||
|
||||
cached
|
||||
predicate storeSet(
|
||||
Node node1, ContentSet c, Node node2, DataFlowType contentType, DataFlowType containerType
|
||||
) {
|
||||
storeStep(node1, c, node2) and
|
||||
contentType = getNodeDataFlowType(node1) and
|
||||
containerType = getNodeDataFlowType(node2)
|
||||
or
|
||||
exists(Node n1, Node n2 |
|
||||
n1 = node1.(PostUpdateNode).getPreUpdateNode() and
|
||||
n2 = node2.(PostUpdateNode).getPreUpdateNode()
|
||||
|
|
||||
argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1)
|
||||
or
|
||||
readSet(n2, c, n1) and
|
||||
contentType = getNodeDataFlowType(n1) and
|
||||
containerType = getNodeDataFlowType(n2)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate store(
|
||||
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
|
||||
) {
|
||||
exists(ContentSet cs | c = cs.getAStoreContent() |
|
||||
storeStep(node1, cs, node2) and
|
||||
contentType = getNodeDataFlowType(node1) and
|
||||
containerType = getNodeDataFlowType(node2)
|
||||
or
|
||||
exists(Node n1, Node n2 |
|
||||
n1 = node1.(PostUpdateNode).getPreUpdateNode() and
|
||||
n2 = node2.(PostUpdateNode).getPreUpdateNode()
|
||||
|
|
||||
argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, cs, contentType), n1)
|
||||
or
|
||||
readSet(n2, cs, n1) and
|
||||
contentType = getNodeDataFlowType(n1) and
|
||||
containerType = getNodeDataFlowType(n2)
|
||||
)
|
||||
exists(ContentSet cs |
|
||||
c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -567,6 +567,35 @@ class GuardNode extends ControlFlowNode {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the guard `g` validates `node` upon evaluating to `branch`.
|
||||
*
|
||||
* The expression `e` is expected to be a syntactic part of the guard `g`.
|
||||
* For example, the guard `g` might be a call `isSafe(x)` and the expression `e`
|
||||
* the argument `x`.
|
||||
*/
|
||||
signature predicate guardChecksSig(GuardNode g, ControlFlowNode node, boolean branch);
|
||||
|
||||
/**
|
||||
* Provides a set of barrier nodes for a guard that validates a node.
|
||||
*
|
||||
* This is expected to be used in `isBarrier`/`isSanitizer` definitions
|
||||
* in data flow and taint tracking.
|
||||
*/
|
||||
module BarrierGuard<guardChecksSig/3 guardChecks> {
|
||||
/** Gets a node that is safely guarded by the given guard check. */
|
||||
ExprNode getABarrierNode() {
|
||||
exists(GuardNode g, EssaDefinition def, ControlFlowNode node, boolean branch |
|
||||
AdjacentUses::useOfDef(def, node) and
|
||||
guardChecks(g, node, branch) and
|
||||
AdjacentUses::useOfDef(def, result.asCfgNode()) and
|
||||
g.controlsBlock(result.asCfgNode().getBasicBlock(), branch)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `BarrierGuard` module instead.
|
||||
*
|
||||
* A guard that validates some expression.
|
||||
*
|
||||
* To use this in a configuration, extend the class and provide a
|
||||
@@ -575,7 +604,7 @@ class GuardNode extends ControlFlowNode {
|
||||
*
|
||||
* It is important that all extending classes in scope are disjoint.
|
||||
*/
|
||||
class BarrierGuard extends GuardNode {
|
||||
deprecated class BarrierGuard extends GuardNode {
|
||||
/** Holds if this guard validates `node` upon evaluating to `branch`. */
|
||||
abstract predicate checks(ControlFlowNode node, boolean branch);
|
||||
|
||||
|
||||
@@ -10,12 +10,6 @@ private import semmle.python.ApiGraphs
|
||||
*/
|
||||
predicate defaultTaintSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `guard` should be a sanitizer guard in all global taint flow configurations
|
||||
* but not in local taint.
|
||||
*/
|
||||
predicate defaultTaintSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* Holds if default `TaintTracking::Configuration`s should allow implicit reads
|
||||
* of `c` at sinks and inputs to additional taint steps.
|
||||
|
||||
@@ -116,20 +116,30 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
|
||||
|
||||
/** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
|
||||
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard) or defaultTaintSanitizerGuard(guard)
|
||||
deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited
|
||||
* when the flow state is `state`.
|
||||
*/
|
||||
predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) { none() }
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
|
||||
none()
|
||||
}
|
||||
|
||||
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
|
||||
deprecated final override predicate isBarrierGuard(
|
||||
DataFlow::BarrierGuard guard, DataFlow::FlowState state
|
||||
) {
|
||||
this.isSanitizerGuard(guard, state)
|
||||
}
|
||||
|
||||
|
||||
@@ -116,20 +116,30 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
|
||||
|
||||
/** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
|
||||
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard) or defaultTaintSanitizerGuard(guard)
|
||||
deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited
|
||||
* when the flow state is `state`.
|
||||
*/
|
||||
predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) { none() }
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
|
||||
none()
|
||||
}
|
||||
|
||||
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
|
||||
deprecated final override predicate isBarrierGuard(
|
||||
DataFlow::BarrierGuard guard, DataFlow::FlowState state
|
||||
) {
|
||||
this.isSanitizerGuard(guard, state)
|
||||
}
|
||||
|
||||
|
||||
@@ -116,20 +116,30 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
|
||||
|
||||
/** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
|
||||
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard) or defaultTaintSanitizerGuard(guard)
|
||||
deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited
|
||||
* when the flow state is `state`.
|
||||
*/
|
||||
predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) { none() }
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
|
||||
none()
|
||||
}
|
||||
|
||||
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
|
||||
deprecated final override predicate isBarrierGuard(
|
||||
DataFlow::BarrierGuard guard, DataFlow::FlowState state
|
||||
) {
|
||||
this.isSanitizerGuard(guard, state)
|
||||
}
|
||||
|
||||
|
||||
@@ -116,20 +116,30 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
|
||||
|
||||
/** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
|
||||
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard) or defaultTaintSanitizerGuard(guard)
|
||||
deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited
|
||||
* when the flow state is `state`.
|
||||
*/
|
||||
predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) { none() }
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
|
||||
none()
|
||||
}
|
||||
|
||||
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
|
||||
deprecated final override predicate isBarrierGuard(
|
||||
DataFlow::BarrierGuard guard, DataFlow::FlowState state
|
||||
) {
|
||||
this.isSanitizerGuard(guard, state)
|
||||
}
|
||||
|
||||
|
||||
@@ -45,14 +45,14 @@ abstract class TrackableState extends string {
|
||||
/**
|
||||
* Holds if state starts at `f`.
|
||||
* Either this predicate or `startsAt(ControlFlowNode f, Context ctx)`
|
||||
* should be overriden by sub-classes.
|
||||
* should be overridden by sub-classes.
|
||||
*/
|
||||
predicate startsAt(ControlFlowNode f) { none() }
|
||||
|
||||
/**
|
||||
* Holds if state starts at `f` given context `ctx`.
|
||||
* Either this predicate or `startsAt(ControlFlowNode f)`
|
||||
* should be overriden by sub-classes.
|
||||
* should be overridden by sub-classes.
|
||||
*/
|
||||
pragma[noinline]
|
||||
predicate startsAt(ControlFlowNode f, Context ctx) { ctx.appliesTo(f) and this.startsAt(f) }
|
||||
@@ -60,14 +60,14 @@ abstract class TrackableState extends string {
|
||||
/**
|
||||
* Holds if state ends at `f`.
|
||||
* Either this predicate or `endsAt(ControlFlowNode f, Context ctx)`
|
||||
* may be overriden by sub-classes.
|
||||
* may be overridden by sub-classes.
|
||||
*/
|
||||
predicate endsAt(ControlFlowNode f) { none() }
|
||||
|
||||
/**
|
||||
* Holds if state ends at `f` given context `ctx`.
|
||||
* Either this predicate or `endsAt(ControlFlowNode f)`
|
||||
* may be overriden by sub-classes.
|
||||
* may be overridden by sub-classes.
|
||||
*/
|
||||
pragma[noinline]
|
||||
predicate endsAt(ControlFlowNode f, Context ctx) { ctx.appliesTo(f) and this.endsAt(f) }
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import python
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
abstract class TestScope extends Scope { }
|
||||
|
||||
// don't extend Class directly to avoid ambiguous method warnings
|
||||
class UnitTestClass extends TestScope {
|
||||
class UnitTestClass extends TestScope, Class {
|
||||
UnitTestClass() {
|
||||
exists(ClassValue cls | this = cls.getScope() |
|
||||
cls.getABaseType+() = Module::named("unittest").attr(_)
|
||||
or
|
||||
cls.getABaseType+().getName().toLowerCase() = "testcase"
|
||||
exists(API::Node testCaseClass, string testCaseString |
|
||||
testCaseString.matches("%TestCase") and
|
||||
testCaseClass = any(API::Node mod).getMember(testCaseString)
|
||||
|
|
||||
this.getParent() = testCaseClass.getASubclass*().getAnImmediateUse().asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -662,7 +662,7 @@ private module AiohttpClientModel {
|
||||
private API::Node instance() { result = classRef().getReturn() }
|
||||
|
||||
/** A method call on a ClientSession that sends off a request */
|
||||
private class OutgoingRequestCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode {
|
||||
private class OutgoingRequestCall extends HTTP::Client::Request::Range, API::CallNode {
|
||||
string methodName;
|
||||
|
||||
OutgoingRequestCall() {
|
||||
@@ -685,8 +685,14 @@ private module AiohttpClientModel {
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
// TODO: Look into disabling certificate validation
|
||||
none()
|
||||
exists(API::Node param | param = this.getKeywordParameter(["ssl", "verify_ssl"]) |
|
||||
disablingNode = param.getARhs() and
|
||||
argumentOrigin = param.getAValueReachingRhs() 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
|
||||
)
|
||||
// TODO: Handling of SSLContext passed as ssl/ssl_context arguments
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ private module Aiomysql {
|
||||
/**
|
||||
* Gets a `Connection` that is created when
|
||||
* - the result of `aiomysql.connect()` is awaited.
|
||||
* - the result of calling `aquire` on a `ConnectionPool` is awaited.
|
||||
* - the result of calling `acquire` on a `ConnectionPool` is awaited.
|
||||
* See https://aiomysql.readthedocs.io/en/stable/connection.html#connection
|
||||
*/
|
||||
API::Node connection() {
|
||||
@@ -82,7 +82,7 @@ private module Aiomysql {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an `SAConnection` that is created when the result of calling `aquire` on an `Engine` is awaited.
|
||||
* Gets an `SAConnection` that is created when the result of calling `acquire` on an `Engine` is awaited.
|
||||
* See https://aiomysql.readthedocs.io/en/stable/sa.html#connection
|
||||
*/
|
||||
API::Node saConnection() { result = engine().getMember("acquire").getReturn().getAwaited() }
|
||||
|
||||
@@ -25,7 +25,7 @@ private module Aiopg {
|
||||
/**
|
||||
* Gets a `Connection` that is created when
|
||||
* - the result of `aiopg.connect()` is awaited.
|
||||
* - the result of calling `aquire` on a `ConnectionPool` is awaited.
|
||||
* - the result of calling `acquire` on a `ConnectionPool` is awaited.
|
||||
* See https://aiopg.readthedocs.io/en/stable/core.html#connection
|
||||
*/
|
||||
API::Node connection() {
|
||||
@@ -78,7 +78,7 @@ private module Aiopg {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an `SAConnection` that is created when the result of calling `aquire` on an `Engine` is awaited.
|
||||
* Gets an `SAConnection` that is created when the result of calling `acquire` on an `Engine` is awaited.
|
||||
* See https://aiopg.readthedocs.io/en/stable/sa.html#connection
|
||||
*/
|
||||
API::Node saConnection() { result = engine().getMember("acquire").getReturn().getAwaited() }
|
||||
|
||||
@@ -7,91 +7,42 @@ private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
private import semmle.python.frameworks.data.ModelsAsData
|
||||
|
||||
/** Provides models for the `asyncpg` PyPI package. */
|
||||
private module Asyncpg {
|
||||
private import semmle.python.internal.Awaited
|
||||
|
||||
/** Gets a `ConnectionPool` that is created when the result of `asyncpg.create_pool()` is awaited. */
|
||||
API::Node connectionPool() {
|
||||
result = API::moduleImport("asyncpg").getMember("create_pool").getReturn().getAwaited()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a `Connection` that is created when
|
||||
* - the result of `asyncpg.connect()` is awaited.
|
||||
* - the result of calling `aquire` on a `ConnectionPool` is awaited.
|
||||
*/
|
||||
API::Node connection() {
|
||||
result = API::moduleImport("asyncpg").getMember("connect").getReturn().getAwaited()
|
||||
or
|
||||
result = connectionPool().getMember("acquire").getReturn().getAwaited()
|
||||
}
|
||||
|
||||
/** `Connection`s and `ConnectionPool`s provide some methods that execute SQL. */
|
||||
class SqlExecutionOnConnection extends SqlExecution::Range, DataFlow::MethodCallNode {
|
||||
string methodName;
|
||||
|
||||
SqlExecutionOnConnection() {
|
||||
this = [connectionPool(), connection()].getMember(methodName).getACall() and
|
||||
methodName in ["copy_from_query", "execute", "fetch", "fetchrow", "fetchval", "executemany"]
|
||||
}
|
||||
|
||||
override DataFlow::Node getSql() {
|
||||
methodName in ["copy_from_query", "execute", "fetch", "fetchrow", "fetchval"] and
|
||||
result in [this.getArg(0), this.getArgByName("query")]
|
||||
or
|
||||
methodName = "executemany" and
|
||||
result in [this.getArg(0), this.getArgByName("command")]
|
||||
class AsyncpgModel extends ModelInput::TypeModelCsv {
|
||||
override predicate row(string row) {
|
||||
// package1;type1;package2;type2;path
|
||||
row =
|
||||
[
|
||||
// a `ConnectionPool` that is created when the result of `asyncpg.create_pool()` is awaited.
|
||||
"asyncpg;ConnectionPool;asyncpg;;Member[create_pool].ReturnValue.Awaited",
|
||||
// a `Connection` that is created when
|
||||
// * - the result of `asyncpg.connect()` is awaited.
|
||||
// * - the result of calling `acquire` on a `ConnectionPool` is awaited.
|
||||
"asyncpg;Connection;asyncpg;;Member[connect].ReturnValue.Awaited",
|
||||
"asyncpg;Connection;asyncpg;ConnectionPool;Member[acquire].ReturnValue.Awaited",
|
||||
// Creating an internal `~Connection` type that contains both `Connection` and `ConnectionPool`.
|
||||
"asyncpg;~Connection;asyncpg;Connection;", "asyncpg;~Connection;asyncpg;ConnectionPool;"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/** A model of `Connection` and `ConnectionPool`, which provide some methods that access the file system. */
|
||||
class FileAccessOnConnection extends FileSystemAccess::Range, DataFlow::MethodCallNode {
|
||||
string methodName;
|
||||
|
||||
FileAccessOnConnection() {
|
||||
this = [connectionPool(), connection()].getMember(methodName).getACall() and
|
||||
methodName in ["copy_from_query", "copy_from_table", "copy_to_table"]
|
||||
}
|
||||
|
||||
// The path argument is keyword only.
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
methodName in ["copy_from_query", "copy_from_table"] and
|
||||
result = this.getArgByName("output")
|
||||
or
|
||||
methodName = "copy_to_table" and
|
||||
result = this.getArgByName("source")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides models of the `PreparedStatement` class in `asyncpg`.
|
||||
* `PreparedStatement`s are created when the result of calling `prepare(query)` on a connection is awaited.
|
||||
* The result of calling `prepare(query)` is a `PreparedStatementFactory` and the argument, `query` needs to
|
||||
* be tracked to the place where a `PreparedStatement` is created and then futher to any executing methods.
|
||||
* Hence the two type trackers.
|
||||
*/
|
||||
module PreparedStatement {
|
||||
class PreparedStatementConstruction extends SqlConstruction::Range, API::CallNode {
|
||||
PreparedStatementConstruction() { this = connection().getMember("prepare").getACall() }
|
||||
|
||||
override DataFlow::Node getSql() { result = this.getParameter(0, "query").getARhs() }
|
||||
}
|
||||
|
||||
class PreparedStatementExecution extends SqlExecution::Range, API::CallNode {
|
||||
PreparedStatementConstruction prepareCall;
|
||||
|
||||
PreparedStatementExecution() {
|
||||
this =
|
||||
prepareCall
|
||||
.getReturn()
|
||||
.getAwaited()
|
||||
.getMember(["executemany", "fetch", "fetchrow", "fetchval"])
|
||||
.getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getSql() { result = prepareCall.getSql() }
|
||||
class AsyncpgSink extends ModelInput::SinkModelCsv {
|
||||
// package;type;path;kind
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
// `Connection`s and `ConnectionPool`s provide some methods that execute SQL.
|
||||
"asyncpg;~Connection;Member[copy_from_query,execute,fetch,fetchrow,fetchval].Argument[0,query:];sql-injection",
|
||||
"asyncpg;~Connection;Member[executemany].Argument[0,command:];sql-injection",
|
||||
// A model of `Connection` and `ConnectionPool`, which provide some methods that access the file system.
|
||||
"asyncpg;~Connection;Member[copy_from_query,copy_from_table].Argument[output:];path-injection",
|
||||
"asyncpg;~Connection;Member[copy_to_table].Argument[source:];path-injection",
|
||||
// the `PreparedStatement` class in `asyncpg`.
|
||||
"asyncpg;Connection;Member[prepare].Argument[0,query:];sql-injection",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +57,9 @@ private module Asyncpg {
|
||||
*/
|
||||
module Cursor {
|
||||
class CursorConstruction extends SqlConstruction::Range, API::CallNode {
|
||||
CursorConstruction() { this = connection().getMember("cursor").getACall() }
|
||||
CursorConstruction() {
|
||||
this = ModelOutput::getATypeNode("asyncpg", "Connection").getMember("cursor").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getSql() { result = this.getParameter(0, "query").getARhs() }
|
||||
}
|
||||
@@ -121,8 +74,11 @@ private module Asyncpg {
|
||||
this = c.getReturn().getAwaited().getAnImmediateUse()
|
||||
)
|
||||
or
|
||||
exists(PreparedStatement::PreparedStatementConstruction prepareCall |
|
||||
sql = prepareCall.getSql() and
|
||||
exists(API::CallNode prepareCall |
|
||||
prepareCall =
|
||||
ModelOutput::getATypeNode("asyncpg", "Connection").getMember("prepare").getACall()
|
||||
|
|
||||
sql = prepareCall.getParameter(0, "query").getARhs() and
|
||||
this =
|
||||
prepareCall
|
||||
.getReturn()
|
||||
|
||||
@@ -108,20 +108,20 @@ private module CryptodomeModel {
|
||||
DataFlow::CallCfgNode {
|
||||
string methodName;
|
||||
string cipherName;
|
||||
API::CallNode newCall;
|
||||
|
||||
CryptodomeGenericCipherOperation() {
|
||||
methodName in [
|
||||
"encrypt", "decrypt", "verify", "update", "hexverify", "encrypt_and_digest",
|
||||
"decrypt_and_verify"
|
||||
] and
|
||||
this =
|
||||
newCall =
|
||||
API::moduleImport(["Crypto", "Cryptodome"])
|
||||
.getMember("Cipher")
|
||||
.getMember(cipherName)
|
||||
.getMember("new")
|
||||
.getReturn()
|
||||
.getMember(methodName)
|
||||
.getACall()
|
||||
.getACall() and
|
||||
this = newCall.getReturn().getMember(methodName).getACall()
|
||||
}
|
||||
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() { result.matchesName(cipherName) }
|
||||
@@ -155,6 +155,20 @@ private module CryptodomeModel {
|
||||
this.getArgByName("mac_tag")
|
||||
]
|
||||
}
|
||||
|
||||
override Cryptography::BlockMode getBlockMode() {
|
||||
// `modeName` is of the form "MODE_<BlockMode>"
|
||||
exists(string modeName |
|
||||
newCall.getArg(1) =
|
||||
API::moduleImport(["Crypto", "Cryptodome"])
|
||||
.getMember("Cipher")
|
||||
.getMember(cipherName)
|
||||
.getMember(modeName)
|
||||
.getAUse()
|
||||
|
|
||||
result = modeName.splitAt("_", 1)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -192,6 +206,8 @@ private module CryptodomeModel {
|
||||
result in [this.getArg(1), this.getArgByName("signature")]
|
||||
)
|
||||
}
|
||||
|
||||
override Cryptography::BlockMode getBlockMode() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -215,5 +231,7 @@ private module CryptodomeModel {
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() { result.matchesName(hashName) }
|
||||
|
||||
override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("data")] }
|
||||
|
||||
override Cryptography::BlockMode getBlockMode() { none() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,8 +170,19 @@ private module CryptographyModel {
|
||||
.getMember(algorithmName)
|
||||
}
|
||||
|
||||
/** Gets a reference to a `cryptography.hazmat.primitives.ciphers.modes` Class */
|
||||
API::Node modeClassRef(string modeName) {
|
||||
result =
|
||||
API::moduleImport("cryptography")
|
||||
.getMember("hazmat")
|
||||
.getMember("primitives")
|
||||
.getMember("ciphers")
|
||||
.getMember("modes")
|
||||
.getMember(modeName)
|
||||
}
|
||||
|
||||
/** Gets a reference to a Cipher instance using algorithm with `algorithmName`. */
|
||||
API::Node cipherInstance(string algorithmName) {
|
||||
API::Node cipherInstance(string algorithmName, string modeName) {
|
||||
exists(API::CallNode call | result = call.getReturn() |
|
||||
call =
|
||||
API::moduleImport("cryptography")
|
||||
@@ -182,7 +193,12 @@ private module CryptographyModel {
|
||||
.getACall() and
|
||||
algorithmClassRef(algorithmName).getReturn().getAUse() 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()
|
||||
else modeName = "<None or unknown>"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -192,10 +208,11 @@ private module CryptographyModel {
|
||||
class CryptographyGenericCipherOperation extends Cryptography::CryptographicOperation::Range,
|
||||
DataFlow::MethodCallNode {
|
||||
string algorithmName;
|
||||
string modeName;
|
||||
|
||||
CryptographyGenericCipherOperation() {
|
||||
this =
|
||||
cipherInstance(algorithmName)
|
||||
cipherInstance(algorithmName, modeName)
|
||||
.getMember(["decryptor", "encryptor"])
|
||||
.getReturn()
|
||||
.getMember(["update", "update_into"])
|
||||
@@ -207,6 +224,8 @@ private module CryptographyModel {
|
||||
}
|
||||
|
||||
override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("data")] }
|
||||
|
||||
override Cryptography::BlockMode getBlockMode() { result = modeName }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,6 +276,8 @@ private module CryptographyModel {
|
||||
}
|
||||
|
||||
override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("data")] }
|
||||
|
||||
override Cryptography::BlockMode getBlockMode() { none() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -464,7 +464,15 @@ module Django {
|
||||
|
||||
/** A file-like object instance that originates from a `UploadedFile`. */
|
||||
class UploadedFileFileLikeInstances extends Stdlib::FileLikeObject::InstanceSource {
|
||||
UploadedFileFileLikeInstances() { this.(DataFlow::AttrRead).accesses(instance(), "file") }
|
||||
UploadedFileFileLikeInstances() {
|
||||
// in the bottom of
|
||||
// https://docs.djangoproject.com/en/4.0/ref/files/file/#django.core.files.File
|
||||
// it's mentioned that the File object itself has proxy methods for
|
||||
// `read`/`write`/... that forwards to the underlying file object.
|
||||
this = instance()
|
||||
or
|
||||
this.(DataFlow::AttrRead).accesses(instance(), "file")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,12 @@ private import semmle.python.ApiGraphs
|
||||
* - https://www.python-httpx.org/
|
||||
*/
|
||||
private module HttpxModel {
|
||||
private class RequestCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode {
|
||||
/**
|
||||
* An outgoing HTTP request, from the `httpx` library.
|
||||
*
|
||||
* See https://www.python-httpx.org/api/
|
||||
*/
|
||||
private class RequestCall extends HTTP::Client::Request::Range, API::CallNode {
|
||||
string methodName;
|
||||
|
||||
RequestCall() {
|
||||
@@ -39,15 +44,18 @@ private module HttpxModel {
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
// TODO: Look into disabling certificate validation
|
||||
none()
|
||||
disablingNode = this.getKeywordParameter("verify").getARhs() and
|
||||
argumentOrigin = this.getKeywordParameter("verify").getAValueReachingRhs() 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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides models for the `httpx.[Async]Client` class
|
||||
*
|
||||
* See https://www.python-httpx.org/async/
|
||||
* See https://www.python-httpx.org/api/#client
|
||||
*/
|
||||
module Client {
|
||||
/** Get a reference to the `httpx.Client` or `httpx.AsyncClient` class. */
|
||||
@@ -55,16 +63,13 @@ private module HttpxModel {
|
||||
result = API::moduleImport("httpx").getMember(["Client", "AsyncClient"])
|
||||
}
|
||||
|
||||
/** Get a reference to an `httpx.Client` or `httpx.AsyncClient` instance. */
|
||||
private API::Node instance() { result = classRef().getReturn() }
|
||||
|
||||
/** A method call on a Client that sends off a request */
|
||||
private class OutgoingRequestCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode {
|
||||
string methodName;
|
||||
|
||||
OutgoingRequestCall() {
|
||||
methodName in [HTTP::httpVerbLower(), "request", "stream"] and
|
||||
this = instance().getMember(methodName).getACall()
|
||||
this = classRef().getReturn().getMember(methodName).getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAUrlPart() {
|
||||
@@ -80,8 +85,16 @@ private module HttpxModel {
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
// TODO: Look into disabling certificate validation
|
||||
none()
|
||||
exists(API::CallNode constructor |
|
||||
constructor = classRef().getACall() and
|
||||
this = constructor.getReturn().getMember(methodName).getACall()
|
||||
|
|
||||
disablingNode = constructor.getKeywordParameter("verify").getARhs() and
|
||||
argumentOrigin = constructor.getKeywordParameter("verify").getAValueReachingRhs() 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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,9 +20,14 @@ private import semmle.python.frameworks.Stdlib
|
||||
*
|
||||
* See
|
||||
* - https://pypi.org/project/requests/
|
||||
* - https://docs.python-requests.org/en/latest/
|
||||
* - https://requests.readthedocs.io/en/latest/
|
||||
*/
|
||||
private module Requests {
|
||||
/**
|
||||
* An outgoing HTTP request, from the `requests` library.
|
||||
*
|
||||
* See https://requests.readthedocs.io/en/latest/api/#requests.request
|
||||
*/
|
||||
private class OutgoingRequestCall extends HTTP::Client::Request::Range, API::CallNode {
|
||||
string methodName;
|
||||
|
||||
@@ -58,6 +63,7 @@ private module Requests {
|
||||
) {
|
||||
disablingNode = this.getKeywordParameter("verify").getARhs() and
|
||||
argumentOrigin = this.getKeywordParameter("verify").getAValueReachingRhs() 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
|
||||
}
|
||||
@@ -81,7 +87,7 @@ private module Requests {
|
||||
/**
|
||||
* Provides models for the `requests.models.Response` class
|
||||
*
|
||||
* See https://docs.python-requests.org/en/latest/api/#requests.Response.
|
||||
* See https://requests.readthedocs.io/en/latest/api/#requests.Response.
|
||||
*/
|
||||
module Response {
|
||||
/** Gets a reference to the `requests.models.Response` class. */
|
||||
|
||||
@@ -41,6 +41,8 @@ private module Rsa {
|
||||
override DataFlow::Node getAnInput() {
|
||||
result in [this.getArg(0), this.getArgByName("message")]
|
||||
}
|
||||
|
||||
override Cryptography::BlockMode getBlockMode() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,6 +56,8 @@ private module Rsa {
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() { result.getName() = "RSA" }
|
||||
|
||||
override DataFlow::Node getAnInput() { result in [this.getArg(0), this.getArgByName("crypto")] }
|
||||
|
||||
override Cryptography::BlockMode getBlockMode() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,6 +83,8 @@ private module Rsa {
|
||||
override DataFlow::Node getAnInput() {
|
||||
result in [this.getArg(0), this.getArgByName("message")]
|
||||
}
|
||||
|
||||
override Cryptography::BlockMode getBlockMode() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,6 +106,8 @@ private module Rsa {
|
||||
or
|
||||
result in [this.getArg(1), this.getArgByName("signature")]
|
||||
}
|
||||
|
||||
override Cryptography::BlockMode getBlockMode() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -122,6 +130,8 @@ private module Rsa {
|
||||
override DataFlow::Node getAnInput() {
|
||||
result in [this.getArg(0), this.getArgByName("message")]
|
||||
}
|
||||
|
||||
override Cryptography::BlockMode getBlockMode() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,5 +147,7 @@ private module Rsa {
|
||||
override DataFlow::Node getAnInput() {
|
||||
result in [this.getArg(0), this.getArgByName("hash_value")]
|
||||
}
|
||||
|
||||
override Cryptography::BlockMode getBlockMode() { none() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2671,6 +2671,8 @@ private module StdlibPrivate {
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() { result.matchesName(hashName) }
|
||||
|
||||
override DataFlow::Node getAnInput() { result = this.getParameter(1, "data").getARhs() }
|
||||
|
||||
override Cryptography::BlockMode getBlockMode() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2686,6 +2688,8 @@ private module StdlibPrivate {
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() { result.matchesName(hashName) }
|
||||
|
||||
override DataFlow::Node getAnInput() { result = this.getArg(0) }
|
||||
|
||||
override Cryptography::BlockMode getBlockMode() { none() }
|
||||
}
|
||||
|
||||
/** Helper predicate for the `HashLibGenericHashOperation` charpred, to prevent a bad join order. */
|
||||
@@ -2709,6 +2713,8 @@ private module StdlibPrivate {
|
||||
HashlibGenericHashOperation() { hashClass = hashlibMember(hashName) }
|
||||
|
||||
override Cryptography::CryptographicAlgorithm getAlgorithm() { result.matchesName(hashName) }
|
||||
|
||||
override Cryptography::BlockMode getBlockMode() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -42,7 +42,8 @@ private module Urllib {
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
// TODO: Look into disabling certificate validation
|
||||
// cannot enable/disable certificate validation on this object, only when used
|
||||
// with `urlopen`, which is modeled below
|
||||
none()
|
||||
}
|
||||
}
|
||||
@@ -63,7 +64,8 @@ private module Urllib {
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
// TODO: Look into disabling certificate validation
|
||||
// will validate certificate by default, see https://github.com/python/cpython/blob/243ed5439c32e8517aa745bc2ca9774d99c99d0f/Lib/http/client.py#L1420-L1421
|
||||
// TODO: Handling of insecure SSLContext passed to context argument
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,8 @@ private module Urllib2 {
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
// TODO: Look into disabling certificate validation
|
||||
// cannot enable/disable certificate validation on this object, only when used
|
||||
// with `urlopen`, which is modeled below
|
||||
none()
|
||||
}
|
||||
}
|
||||
@@ -49,7 +50,8 @@ private module Urllib2 {
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
// TODO: Look into disabling certificate validation
|
||||
// will validate certificate by default
|
||||
// TODO: Handling of insecure SSLContext passed to context argument
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,9 +42,6 @@ private module Urllib3 {
|
||||
.getASubclass+()
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of a `urllib3.request.RequestMethods` subclass. */
|
||||
private API::Node instance() { result = classRef().getReturn() }
|
||||
|
||||
/**
|
||||
* A call to a method making an outgoing request.
|
||||
*
|
||||
@@ -52,10 +49,11 @@ private module Urllib3 {
|
||||
* - https://urllib3.readthedocs.io/en/stable/reference/urllib3.request.html#urllib3.request.RequestMethods
|
||||
* - https://urllib3.readthedocs.io/en/stable/reference/urllib3.connectionpool.html#urllib3.HTTPConnectionPool.urlopen
|
||||
*/
|
||||
private class RequestCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode {
|
||||
private class RequestCall extends HTTP::Client::Request::Range, API::CallNode {
|
||||
RequestCall() {
|
||||
this =
|
||||
instance()
|
||||
classRef()
|
||||
.getReturn()
|
||||
.getMember(["request", "request_encode_url", "request_encode_body", "urlopen"])
|
||||
.getACall()
|
||||
}
|
||||
@@ -67,8 +65,22 @@ private module Urllib3 {
|
||||
override predicate disablesCertificateValidation(
|
||||
DataFlow::Node disablingNode, DataFlow::Node argumentOrigin
|
||||
) {
|
||||
// TODO: Look into disabling certificate validation
|
||||
none()
|
||||
exists(API::CallNode constructor |
|
||||
constructor = classRef().getACall() and
|
||||
this = constructor.getReturn().getAMember().getACall()
|
||||
|
|
||||
// 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
|
||||
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
|
||||
argumentOrigin.asExpr().(BooleanLiteral).booleanValue() = false
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
47
python/ql/lib/semmle/python/frameworks/data/ModelsAsData.qll
Normal file
47
python/ql/lib/semmle/python/frameworks/data/ModelsAsData.qll
Normal file
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Provides classes for contributing a model, or using the interpreted results
|
||||
* of a model represented as data.
|
||||
*
|
||||
* - Use the `ModelInput` module to contribute new models.
|
||||
* - Use the `ModelOutput` module to access the model results in terms of API nodes.
|
||||
*
|
||||
* The package name refers to the top-level module the import comes from, and not a PyPI package.
|
||||
* So for `from foo.bar import baz`, the package will be `foo`.
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import internal.ApiGraphModels as Shared
|
||||
private import internal.ApiGraphModelsSpecific as Specific
|
||||
import Shared::ModelInput as ModelInput
|
||||
import Shared::ModelOutput as ModelOutput
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.ApiGraphs
|
||||
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() }
|
||||
|
||||
override string getSourceType() { result = "Remote flow (from model)" }
|
||||
}
|
||||
|
||||
/**
|
||||
* Like `ModelOutput::summaryStep` but with API nodes mapped to data-flow nodes.
|
||||
*/
|
||||
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()
|
||||
)
|
||||
}
|
||||
|
||||
/** Taint steps induced by summary models of kind `taint`. */
|
||||
private class TaintStepFromSummary extends TaintTracking::AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
summaryStepNodes(pred, succ, "taint")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,522 @@
|
||||
/**
|
||||
* INTERNAL use only. This is an experimental API subject to change without notice.
|
||||
*
|
||||
* Provides classes and predicates for dealing with flow models specified in CSV format.
|
||||
*
|
||||
* The CSV specification has the following columns:
|
||||
* - Sources:
|
||||
* `package; type; path; kind`
|
||||
* - Sinks:
|
||||
* `package; type; path; kind`
|
||||
* - Summaries:
|
||||
* `package; type; path; input; output; kind`
|
||||
* - Types:
|
||||
* `package1; type1; package2; type2; path`
|
||||
*
|
||||
* The interpretation of a row is similar to API-graphs with a left-to-right
|
||||
* reading.
|
||||
* 1. The `package` column selects a package name, as it would be referenced in the source code,
|
||||
* such as an NPM package, PIP package, or Ruby gem. (See `ModelsAsData.qll` for language-specific details).
|
||||
* It may also be a synthetic package used for a type definition (see type definitions below).
|
||||
* 2. The `type` column selects all instances of a named type originating from that package,
|
||||
* or the empty string if referring to the package itself.
|
||||
* It can also be a synthetic type name defined by a type definition (see type definitions below).
|
||||
* 3. The `path` column is a `.`-separated list of "access path tokens" to resolve, starting at the node selected by `package` and `type`.
|
||||
*
|
||||
* Every language supports the following tokens:
|
||||
* - Argument[n]: the n-th argument to a call. May be a range of form `x..y` (inclusive) and/or a comma-separated list.
|
||||
* Additionally, `N-1` refers to the last argument, `N-2` refers to the second-last, and so on.
|
||||
* - Parameter[n]: the n-th parameter of a callback. May be a range of form `x..y` (inclusive) and/or a comma-separated list.
|
||||
* - ReturnValue: the value returned by a function call
|
||||
* - WithArity[n]: match a call with the given arity. May be a range of form `x..y` (inclusive) and/or a comma-separated list.
|
||||
*
|
||||
* The following tokens are common and should be implemented for languages where it makes sense:
|
||||
* - Member[x]: a member named `x`; exactly what a "member" is depends on the language. May be a comma-separated list of names.
|
||||
* - Instance: an instance of a class
|
||||
* - Subclass: a subclass of a class
|
||||
* - ArrayElement: an element of array
|
||||
* - Element: an element of a collection-like object
|
||||
* - MapKey: a key in map-like object
|
||||
* - MapValue: a value in a map-like object
|
||||
* - Awaited: the value from a resolved promise/future-like object
|
||||
*
|
||||
* For the time being, please consult `ApiGraphModelsSpecific.qll` to see which language-specific tokens are currently supported.
|
||||
*
|
||||
* 4. The `input` and `output` columns specify how data enters and leaves the element selected by the
|
||||
* first `(package, type, path)` tuple. Both strings are `.`-separated access paths
|
||||
* of the same syntax as the `path` column.
|
||||
* 5. The `kind` column is a tag that can be referenced from QL to determine to
|
||||
* which classes the interpreted elements should be added. For example, for
|
||||
* sources `"remote"` indicates a default remote flow source, and for summaries
|
||||
* `"taint"` indicates a default additional taint step and `"value"` indicates a
|
||||
* globally applicable value-preserving step.
|
||||
*
|
||||
* ### Types
|
||||
*
|
||||
* A type row of form `package1; type1; package2; type2; path` indicates that `package2; type2; path`
|
||||
* should be seen as an instance of the type `package1; type1`.
|
||||
*
|
||||
* A `(package,type)` pair may refer to a static type or a synthetic type name used internally in the model.
|
||||
* Synthetic type names can be used to reuse intermediate sub-paths, when there are multiple ways to access the same
|
||||
* element.
|
||||
* See `ModelsAsData.qll` for the language-specific interpretation of packages and static type names.
|
||||
*
|
||||
* By convention, if one wants to avoid clashes with static types from the package, the type name
|
||||
* should be prefixed with a tilde character (`~`). For example, `(foo, ~Bar)` can be used to indicate that
|
||||
* the type is related to the `foo` package but is not intended to match a static type.
|
||||
*/
|
||||
|
||||
private import ApiGraphModelsSpecific as Specific
|
||||
|
||||
private class Unit = Specific::Unit;
|
||||
|
||||
private module API = Specific::API;
|
||||
|
||||
private import Specific::AccessPathSyntax
|
||||
|
||||
/** Module containing hooks for providing input data to be interpreted as a model. */
|
||||
module ModelInput {
|
||||
/**
|
||||
* A unit class for adding additional source model rows.
|
||||
*
|
||||
* Extend this class to add additional source definitions.
|
||||
*/
|
||||
class SourceModelCsv extends Unit {
|
||||
/**
|
||||
* Holds if `row` specifies a source definition.
|
||||
*
|
||||
* A row of form
|
||||
* ```
|
||||
* package;type;path;kind
|
||||
* ```
|
||||
* indicates that the value at `(package, type, path)` should be seen as a flow
|
||||
* source of the given `kind`.
|
||||
*
|
||||
* The kind `remote` represents a general remote flow source.
|
||||
*/
|
||||
abstract predicate row(string row);
|
||||
}
|
||||
|
||||
/**
|
||||
* A unit class for adding additional sink model rows.
|
||||
*
|
||||
* Extend this class to add additional sink definitions.
|
||||
*/
|
||||
class SinkModelCsv extends Unit {
|
||||
/**
|
||||
* Holds if `row` specifies a sink definition.
|
||||
*
|
||||
* A row of form
|
||||
* ```
|
||||
* package;type;path;kind
|
||||
* ```
|
||||
* indicates that the value at `(package, type, path)` should be seen as a sink
|
||||
* of the given `kind`.
|
||||
*/
|
||||
abstract predicate row(string row);
|
||||
}
|
||||
|
||||
/**
|
||||
* A unit class for adding additional summary model rows.
|
||||
*
|
||||
* Extend this class to add additional flow summary definitions.
|
||||
*/
|
||||
class SummaryModelCsv extends Unit {
|
||||
/**
|
||||
* Holds if `row` specifies a summary definition.
|
||||
*
|
||||
* A row of form
|
||||
* ```
|
||||
* package;type;path;input;output;kind
|
||||
* ```
|
||||
* indicates that for each call to `(package, type, path)`, the value referred to by `input`
|
||||
* can flow to the value referred to by `output`.
|
||||
*
|
||||
* `kind` should be either `value` or `taint`, for value-preserving or taint-preserving steps,
|
||||
* respectively.
|
||||
*/
|
||||
abstract predicate row(string row);
|
||||
}
|
||||
|
||||
/**
|
||||
* A unit class for adding additional type model rows.
|
||||
*
|
||||
* Extend this class to add additional type definitions.
|
||||
*/
|
||||
class TypeModelCsv extends Unit {
|
||||
/**
|
||||
* Holds if `row` specifies a type definition.
|
||||
*
|
||||
* A row of form,
|
||||
* ```
|
||||
* package1;type1;package2;type2;path
|
||||
* ```
|
||||
* indicates that `(package2, type2, path)` should be seen as an instance of `(package1, type1)`.
|
||||
*/
|
||||
abstract predicate row(string row);
|
||||
}
|
||||
}
|
||||
|
||||
private import ModelInput
|
||||
|
||||
/**
|
||||
* An empty class, except in specific tests.
|
||||
*
|
||||
* If this is non-empty, all models are parsed even if the package is not
|
||||
* considered relevant for the current database.
|
||||
*/
|
||||
abstract class TestAllModels extends Unit { }
|
||||
|
||||
/**
|
||||
* Append `;dummy` to the value of `s` to work around the fact that `string.split(delim,n)`
|
||||
* does not preserve empty trailing substrings.
|
||||
*/
|
||||
bindingset[result]
|
||||
private string inversePad(string s) { s = result + ";dummy" }
|
||||
|
||||
private predicate sourceModel(string row) { any(SourceModelCsv s).row(inversePad(row)) }
|
||||
|
||||
private predicate sinkModel(string row) { any(SinkModelCsv s).row(inversePad(row)) }
|
||||
|
||||
private predicate summaryModel(string row) { any(SummaryModelCsv s).row(inversePad(row)) }
|
||||
|
||||
private predicate typeModel(string row) { any(TypeModelCsv s).row(inversePad(row)) }
|
||||
|
||||
/** Holds if a source model exists for the given parameters. */
|
||||
predicate sourceModel(string package, string type, string path, string kind) {
|
||||
exists(string row |
|
||||
sourceModel(row) and
|
||||
row.splitAt(";", 0) = package and
|
||||
row.splitAt(";", 1) = type and
|
||||
row.splitAt(";", 2) = path and
|
||||
row.splitAt(";", 3) = kind
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if a sink model exists for the given parameters. */
|
||||
private predicate sinkModel(string package, string type, string path, string kind) {
|
||||
exists(string row |
|
||||
sinkModel(row) and
|
||||
row.splitAt(";", 0) = package and
|
||||
row.splitAt(";", 1) = type and
|
||||
row.splitAt(";", 2) = path and
|
||||
row.splitAt(";", 3) = kind
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if a summary model `row` exists for the given parameters. */
|
||||
private predicate summaryModel(
|
||||
string package, string type, string path, string input, string output, string kind
|
||||
) {
|
||||
exists(string row |
|
||||
summaryModel(row) and
|
||||
row.splitAt(";", 0) = package and
|
||||
row.splitAt(";", 1) = type and
|
||||
row.splitAt(";", 2) = path and
|
||||
row.splitAt(";", 3) = input and
|
||||
row.splitAt(";", 4) = output and
|
||||
row.splitAt(";", 5) = kind
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if an type model exists for the given parameters. */
|
||||
private predicate typeModel(
|
||||
string package1, string type1, string package2, string type2, string path
|
||||
) {
|
||||
exists(string row |
|
||||
typeModel(row) and
|
||||
row.splitAt(";", 0) = package1 and
|
||||
row.splitAt(";", 1) = type1 and
|
||||
row.splitAt(";", 2) = package2 and
|
||||
row.splitAt(";", 3) = type2 and
|
||||
row.splitAt(";", 4) = path
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a package that should be seen as an alias for the given other `package`,
|
||||
* or the `package` itself.
|
||||
*/
|
||||
bindingset[package]
|
||||
bindingset[result]
|
||||
string getAPackageAlias(string package) {
|
||||
typeModel(package, "", result, "", "")
|
||||
or
|
||||
result = package
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if CSV rows involving `package` might be relevant for the analysis of this database.
|
||||
*/
|
||||
private predicate isRelevantPackage(string package) {
|
||||
(
|
||||
sourceModel(package, _, _, _) or
|
||||
sinkModel(package, _, _, _) or
|
||||
summaryModel(package, _, _, _, _, _) or
|
||||
typeModel(package, _, _, _, _)
|
||||
) and
|
||||
(
|
||||
Specific::isPackageUsed(package)
|
||||
or
|
||||
exists(TestAllModels t)
|
||||
)
|
||||
or
|
||||
exists(string other |
|
||||
isRelevantPackage(other) and
|
||||
typeModel(package, _, other, _, _)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `package,type,path` is used in some CSV row.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate isRelevantFullPath(string package, string type, string path) {
|
||||
isRelevantPackage(package) and
|
||||
(
|
||||
sourceModel(package, type, path, _) or
|
||||
sinkModel(package, type, path, _) or
|
||||
summaryModel(package, type, path, _, _, _) or
|
||||
typeModel(_, _, package, type, path)
|
||||
)
|
||||
}
|
||||
|
||||
/** A string from a CSV row that should be parsed as an access path. */
|
||||
private class AccessPathRange extends AccessPath::Range {
|
||||
AccessPathRange() {
|
||||
isRelevantFullPath(_, _, this)
|
||||
or
|
||||
exists(string package | isRelevantPackage(package) |
|
||||
summaryModel(package, _, _, this, _, _) or
|
||||
summaryModel(package, _, _, _, this, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a successor of `node` in the API graph.
|
||||
*/
|
||||
bindingset[token]
|
||||
API::Node getSuccessorFromNode(API::Node node, AccessPathToken token) {
|
||||
// API graphs use the same label for arguments and parameters. An edge originating from a
|
||||
// use-node represents an argument, and an edge originating from a def-node represents a parameter.
|
||||
// We just map both to the same thing.
|
||||
token.getName() = ["Argument", "Parameter"] and
|
||||
result = node.getParameter(AccessPath::parseIntUnbounded(token.getAnArgument()))
|
||||
or
|
||||
token.getName() = "ReturnValue" and
|
||||
result = node.getReturn()
|
||||
or
|
||||
// Language-specific tokens
|
||||
result = Specific::getExtraSuccessorFromNode(node, token)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an API-graph successor for the given invocation.
|
||||
*/
|
||||
bindingset[token]
|
||||
API::Node getSuccessorFromInvoke(Specific::InvokeNode invoke, AccessPathToken token) {
|
||||
token.getName() = "Argument" and
|
||||
result =
|
||||
invoke
|
||||
.getParameter(AccessPath::parseIntWithArity(token.getAnArgument(), invoke.getNumArgument()))
|
||||
or
|
||||
token.getName() = "ReturnValue" and
|
||||
result = invoke.getReturn()
|
||||
or
|
||||
// Language-specific tokens
|
||||
result = Specific::getExtraSuccessorFromInvoke(invoke, token)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `invoke` invokes a call-site filter given by `token`.
|
||||
*/
|
||||
pragma[inline]
|
||||
private predicate invocationMatchesCallSiteFilter(Specific::InvokeNode invoke, AccessPathToken token) {
|
||||
token.getName() = "WithArity" and
|
||||
invoke.getNumArgument() = AccessPath::parseIntUnbounded(token.getAnArgument())
|
||||
or
|
||||
Specific::invocationMatchesExtraCallSiteFilter(invoke, token)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the API node identified by the first `n` tokens of `path` in the given `(package, type, path)` tuple.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private API::Node getNodeFromPath(string package, string type, AccessPath path, int n) {
|
||||
isRelevantFullPath(package, type, path) and
|
||||
(
|
||||
n = 0 and
|
||||
exists(string package2, string type2, AccessPath path2 |
|
||||
typeModel(package, type, package2, type2, path2) and
|
||||
result = getNodeFromPath(package2, type2, path2, path2.getNumToken())
|
||||
)
|
||||
or
|
||||
// Language-specific cases, such as handling of global variables
|
||||
result = Specific::getExtraNodeFromPath(package, type, path, n)
|
||||
)
|
||||
or
|
||||
result = getSuccessorFromNode(getNodeFromPath(package, type, path, n - 1), path.getToken(n - 1))
|
||||
or
|
||||
// Similar to the other recursive case, but where the path may have stepped through one or more call-site filters
|
||||
result =
|
||||
getSuccessorFromInvoke(getInvocationFromPath(package, type, path, n - 1), path.getToken(n - 1))
|
||||
}
|
||||
|
||||
/** Gets the node identified by the given `(package, type, path)` tuple. */
|
||||
API::Node getNodeFromPath(string package, string type, AccessPath path) {
|
||||
result = getNodeFromPath(package, type, path, path.getNumToken())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an invocation identified by the given `(package, type, path)` tuple.
|
||||
*
|
||||
* Unlike `getNodeFromPath`, the `path` may end with one or more call-site filters.
|
||||
*/
|
||||
Specific::InvokeNode getInvocationFromPath(string package, string type, AccessPath path, int n) {
|
||||
result = Specific::getAnInvocationOf(getNodeFromPath(package, type, path, n))
|
||||
or
|
||||
result = getInvocationFromPath(package, type, path, n - 1) and
|
||||
invocationMatchesCallSiteFilter(result, path.getToken(n - 1))
|
||||
}
|
||||
|
||||
/** Gets an invocation identified by the given `(package, type, path)` tuple. */
|
||||
Specific::InvokeNode getInvocationFromPath(string package, string type, AccessPath path) {
|
||||
result = getInvocationFromPath(package, type, path, path.getNumToken())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `name` is a valid name for an access path token in the identifying access path.
|
||||
*/
|
||||
bindingset[name]
|
||||
predicate isValidTokenNameInIdentifyingAccessPath(string name) {
|
||||
name = ["Argument", "Parameter", "ReturnValue", "WithArity"]
|
||||
or
|
||||
Specific::isExtraValidTokenNameInIdentifyingAccessPath(name)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `name` is a valid name for an access path token with no arguments, occurring
|
||||
* in an identifying access path.
|
||||
*/
|
||||
bindingset[name]
|
||||
predicate isValidNoArgumentTokenInIdentifyingAccessPath(string name) {
|
||||
name = "ReturnValue"
|
||||
or
|
||||
Specific::isExtraValidNoArgumentTokenInIdentifyingAccessPath(name)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `argument` is a valid argument to an access path token with the given `name`, occurring
|
||||
* in an identifying access path.
|
||||
*/
|
||||
bindingset[name, argument]
|
||||
predicate isValidTokenArgumentInIdentifyingAccessPath(string name, string argument) {
|
||||
name = ["Argument", "Parameter"] and
|
||||
argument.regexpMatch("(N-|-)?\\d+(\\.\\.((N-|-)?\\d+)?)?")
|
||||
or
|
||||
name = "WithArity" and
|
||||
argument.regexpMatch("\\d+(\\.\\.(\\d+)?)?")
|
||||
or
|
||||
Specific::isExtraValidTokenArgumentInIdentifyingAccessPath(name, argument)
|
||||
}
|
||||
|
||||
/**
|
||||
* Module providing access to the imported models in terms of API graph nodes.
|
||||
*/
|
||||
module ModelOutput {
|
||||
/**
|
||||
* Holds if a CSV source model contributed `source` with the given `kind`.
|
||||
*/
|
||||
API::Node getASourceNode(string kind) {
|
||||
exists(string package, string type, string path |
|
||||
sourceModel(package, type, path, kind) and
|
||||
result = getNodeFromPath(package, type, path)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a CSV sink model contributed `sink` with the given `kind`.
|
||||
*/
|
||||
API::Node getASinkNode(string kind) {
|
||||
exists(string package, string type, string path |
|
||||
sinkModel(package, type, path, kind) and
|
||||
result = getNodeFromPath(package, type, path)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a relevant CSV summary exists for these parameters.
|
||||
*/
|
||||
predicate relevantSummaryModel(
|
||||
string package, string type, string path, string input, string output, string kind
|
||||
) {
|
||||
isRelevantPackage(package) and
|
||||
summaryModel(package, type, path, input, output, kind)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a `baseNode` is an invocation identified by the `package,type,path` part of a summary row.
|
||||
*/
|
||||
predicate resolvedSummaryBase(
|
||||
string package, string type, string path, Specific::InvokeNode baseNode
|
||||
) {
|
||||
summaryModel(package, type, path, _, _, _) and
|
||||
baseNode = getInvocationFromPath(package, type, path)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is seen as an instance of `(package,type)` due to a type definition
|
||||
* contributed by a CSV model.
|
||||
*/
|
||||
API::Node getATypeNode(string package, string type) {
|
||||
exists(string package2, string type2, AccessPath path |
|
||||
typeModel(package, type, package2, type2, path) and
|
||||
result = getNodeFromPath(package2, type2, path)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an error message relating to an invalid CSV row in a model.
|
||||
*/
|
||||
string getAWarning() {
|
||||
// Check number of columns
|
||||
exists(string row, string kind, int expectedArity, int actualArity |
|
||||
any(SourceModelCsv csv).row(row) and kind = "source" and expectedArity = 4
|
||||
or
|
||||
any(SinkModelCsv csv).row(row) and kind = "sink" and expectedArity = 4
|
||||
or
|
||||
any(SummaryModelCsv csv).row(row) and kind = "summary" and expectedArity = 6
|
||||
or
|
||||
any(TypeModelCsv csv).row(row) and kind = "type" and expectedArity = 5
|
||||
|
|
||||
actualArity = count(row.indexOf(";")) + 1 and
|
||||
actualArity != expectedArity and
|
||||
result =
|
||||
"CSV " + kind + " row should have " + expectedArity + " columns but has " + actualArity +
|
||||
": " + row
|
||||
)
|
||||
or
|
||||
// Check names and arguments of access path tokens
|
||||
exists(AccessPath path, AccessPathToken token |
|
||||
isRelevantFullPath(_, _, path) and
|
||||
token = path.getToken(_)
|
||||
|
|
||||
not isValidTokenNameInIdentifyingAccessPath(token.getName()) and
|
||||
result = "Invalid token name '" + token.getName() + "' in access path: " + path
|
||||
or
|
||||
isValidTokenNameInIdentifyingAccessPath(token.getName()) and
|
||||
exists(string argument |
|
||||
argument = token.getAnArgument() and
|
||||
not isValidTokenArgumentInIdentifyingAccessPath(token.getName(), argument) and
|
||||
result =
|
||||
"Invalid argument '" + argument + "' in token '" + token + "' in access path: " + path
|
||||
)
|
||||
or
|
||||
isValidTokenNameInIdentifyingAccessPath(token.getName()) and
|
||||
token.getNumArgument() = 0 and
|
||||
not isValidNoArgumentTokenInIdentifyingAccessPath(token.getName()) and
|
||||
result = "Invalid token '" + token + "' is missing its arguments, in access path: " + path
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
/**
|
||||
* Contains the language-specific part of the models-as-data implementation found in `ApiGraphModels.qll`.
|
||||
*
|
||||
* It must export the following members:
|
||||
* ```ql
|
||||
* class Unit // a unit type
|
||||
* module AccessPathSyntax // a re-export of the AccessPathSyntax module
|
||||
* class InvokeNode // a type representing an invocation connected to the API graph
|
||||
* module API // the API graph module
|
||||
* predicate isPackageUsed(string package)
|
||||
* API::Node getExtraNodeFromPath(string package, string type, string path, int n)
|
||||
* API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token)
|
||||
* API::Node getExtraSuccessorFromInvoke(API::InvokeNode node, AccessPathToken token)
|
||||
* predicate invocationMatchesExtraCallSiteFilter(API::InvokeNode invoke, AccessPathToken token)
|
||||
* InvokeNode getAnInvocationOf(API::Node node)
|
||||
* predicate isExtraValidTokenNameInIdentifyingAccessPath(string name)
|
||||
* predicate isExtraValidNoArgumentTokenInIdentifyingAccessPath(string name)
|
||||
* predicate isExtraValidTokenArgumentInIdentifyingAccessPath(string name, string argument)
|
||||
* ```
|
||||
*/
|
||||
|
||||
private import python as PY
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import ApiGraphModels
|
||||
import semmle.python.ApiGraphs::API as API
|
||||
|
||||
class Unit = PY::Unit;
|
||||
|
||||
// Re-export libraries needed by ApiGraphModels.qll
|
||||
import semmle.python.frameworks.data.internal.AccessPathSyntax as AccessPathSyntax
|
||||
private import AccessPathSyntax
|
||||
|
||||
/**
|
||||
* Holds if models describing `package` may be relevant for the analysis of this database.
|
||||
*/
|
||||
predicate isPackageUsed(string package) { exists(API::moduleImport(package)) }
|
||||
|
||||
/** Gets a Python-specific interpretation of the `(package, type, path)` tuple after resolving the first `n` access path tokens. */
|
||||
bindingset[package, type, path]
|
||||
API::Node getExtraNodeFromPath(string package, string type, AccessPath path, int n) {
|
||||
type = "" and
|
||||
n = 0 and
|
||||
result = API::moduleImport(package) and
|
||||
exists(path)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a Python-specific API graph successor of `node` reachable by resolving `token`.
|
||||
*/
|
||||
bindingset[token]
|
||||
API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token) {
|
||||
token.getName() = "Member" and
|
||||
result = node.getMember(token.getAnArgument())
|
||||
or
|
||||
token.getName() = "Instance" and
|
||||
result = node.getReturn() // In Python `Instance` is just an alias for `ReturnValue`
|
||||
or
|
||||
token.getName() = "Awaited" and
|
||||
result = node.getAwaited()
|
||||
or
|
||||
token.getName() = "Subclass" and
|
||||
result = node.getASubclass*()
|
||||
or
|
||||
token.getName() = "Method" and
|
||||
result = node.getMember(token.getAnArgument()).getReturn()
|
||||
or
|
||||
token.getName() = ["Argument", "Parameter"] and
|
||||
(
|
||||
token.getAnArgument() = "self" and
|
||||
result = node.getSelfParameter()
|
||||
or
|
||||
exists(string name | token.getAnArgument() = name + ":" |
|
||||
result = node.getKeywordParameter(name)
|
||||
)
|
||||
or
|
||||
token.getAnArgument() = "any" and
|
||||
result = [node.getParameter(_), node.getKeywordParameter(_)]
|
||||
or
|
||||
token.getAnArgument() = "any-named" and
|
||||
result = node.getKeywordParameter(_)
|
||||
)
|
||||
// Some features don't have MaD tokens yet, they would need to be added to API-graphs first.
|
||||
// - decorators ("DecoratedClass", "DecoratedMember", "DecoratedParameter")
|
||||
// - Array/Map elements ("ArrayElement", "Element", "MapKey", "MapValue")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a Python-specific API graph successor of `node` reachable by resolving `token`.
|
||||
*/
|
||||
bindingset[token]
|
||||
API::Node getExtraSuccessorFromInvoke(API::CallNode node, AccessPathToken token) {
|
||||
token.getName() = "Instance" and
|
||||
result = node.getReturn()
|
||||
or
|
||||
token.getName() = ["Argument", "Parameter"] and
|
||||
(
|
||||
token.getAnArgument() = "self" and
|
||||
result = node.getSelfParameter()
|
||||
or
|
||||
token.getAnArgument() = "any" and
|
||||
result = [node.getParameter(_), node.getKeywordParameter(_)]
|
||||
or
|
||||
token.getAnArgument() = "any-named" and
|
||||
result = node.getKeywordParameter(_)
|
||||
or
|
||||
exists(string arg | arg + ":" = token.getAnArgument() | result = node.getKeywordParameter(arg))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `invoke` matches the PY-specific call site filter in `token`.
|
||||
*/
|
||||
bindingset[token]
|
||||
predicate invocationMatchesExtraCallSiteFilter(API::CallNode invoke, AccessPathToken token) {
|
||||
token.getName() = "Call" and exists(invoke) // there is only one kind of call in Python.
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `path` is an input or output spec for a summary with the given `base` node.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate relevantInputOutputPath(API::CallNode base, AccessPath inputOrOutput) {
|
||||
exists(string package, string type, string input, string output, string path |
|
||||
ModelOutput::relevantSummaryModel(package, type, path, input, output, _) and
|
||||
ModelOutput::resolvedSummaryBase(package, type, path, base) and
|
||||
inputOrOutput = [input, output]
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the API node for the first `n` tokens of the given input/output path, evaluated relative to `baseNode`.
|
||||
*/
|
||||
private API::Node getNodeFromInputOutputPath(API::CallNode baseNode, AccessPath path, int n) {
|
||||
relevantInputOutputPath(baseNode, path) and
|
||||
(
|
||||
n = 1 and
|
||||
result = getSuccessorFromInvoke(baseNode, path.getToken(0))
|
||||
or
|
||||
result =
|
||||
getSuccessorFromNode(getNodeFromInputOutputPath(baseNode, path, n - 1), path.getToken(n - 1))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the API node for the given input/output path, evaluated relative to `baseNode`.
|
||||
*/
|
||||
private API::Node getNodeFromInputOutputPath(API::CallNode baseNode, AccessPath path) {
|
||||
result = getNodeFromInputOutputPath(baseNode, path, path.getNumToken())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a CSV summary contributed the step `pred -> succ` of the given `kind`.
|
||||
*/
|
||||
predicate summaryStep(API::Node pred, API::Node succ, string kind) {
|
||||
exists(
|
||||
string package, string type, string path, API::CallNode base, AccessPath input,
|
||||
AccessPath output
|
||||
|
|
||||
ModelOutput::relevantSummaryModel(package, type, path, input, output, kind) and
|
||||
ModelOutput::resolvedSummaryBase(package, type, path, base) and
|
||||
pred = getNodeFromInputOutputPath(base, input) and
|
||||
succ = getNodeFromInputOutputPath(base, output)
|
||||
)
|
||||
}
|
||||
|
||||
class InvokeNode = API::CallNode;
|
||||
|
||||
/** Gets an `InvokeNode` corresponding to an invocation of `node`. */
|
||||
InvokeNode getAnInvocationOf(API::Node node) { result = node.getACall() }
|
||||
|
||||
/**
|
||||
* Holds if `name` is a valid name for an access path token in the identifying access path.
|
||||
*/
|
||||
bindingset[name]
|
||||
predicate isExtraValidTokenNameInIdentifyingAccessPath(string name) {
|
||||
name = ["Member", "Instance", "Awaited", "Call", "Method", "Subclass"]
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `name` is a valid name for an access path token with no arguments, occurring
|
||||
* in an identifying access path.
|
||||
*/
|
||||
predicate isExtraValidNoArgumentTokenInIdentifyingAccessPath(string name) {
|
||||
name = ["Instance", "Awaited", "Call", "Subclass"]
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `argument` is a valid argument to an access path token with the given `name`, occurring
|
||||
* in an identifying access path.
|
||||
*/
|
||||
bindingset[name, argument]
|
||||
predicate isExtraValidTokenArgumentInIdentifyingAccessPath(string name, string argument) {
|
||||
name = ["Member", "Method"] and
|
||||
exists(argument)
|
||||
or
|
||||
name = ["Argument", "Parameter"] and
|
||||
(
|
||||
argument = ["self", "any", "any-named"]
|
||||
or
|
||||
argument.regexpMatch("\\w+:") // keyword argument
|
||||
)
|
||||
}
|
||||
@@ -4,3 +4,4 @@
|
||||
*/
|
||||
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.concepts.CryptoAlgorithms as CryptoAlgorithms
|
||||
|
||||
@@ -11,3 +11,79 @@
|
||||
*/
|
||||
|
||||
private import ConceptsImports
|
||||
|
||||
/**
|
||||
* Provides models for cryptographic concepts.
|
||||
*
|
||||
* Note: The `CryptographicAlgorithm` class currently doesn't take weak keys into
|
||||
* consideration for the `isWeak` member predicate. So RSA is always considered
|
||||
* secure, although using a low number of bits will actually make it insecure. We plan
|
||||
* to improve our libraries in the future to more precisely capture this aspect.
|
||||
*/
|
||||
module Cryptography {
|
||||
class CryptographicAlgorithm = CryptoAlgorithms::CryptographicAlgorithm;
|
||||
|
||||
class EncryptionAlgorithm = CryptoAlgorithms::EncryptionAlgorithm;
|
||||
|
||||
class HashingAlgorithm = CryptoAlgorithms::HashingAlgorithm;
|
||||
|
||||
class PasswordHashingAlgorithm = CryptoAlgorithms::PasswordHashingAlgorithm;
|
||||
|
||||
/**
|
||||
* A data-flow node that is an application of a cryptographic algorithm. For example,
|
||||
* encryption, decryption, signature-validation.
|
||||
*
|
||||
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||
* extend `CryptographicOperation::Range` instead.
|
||||
*/
|
||||
class CryptographicOperation extends DataFlow::Node instanceof CryptographicOperation::Range {
|
||||
/** Gets the algorithm used, if it matches a known `CryptographicAlgorithm`. */
|
||||
CryptographicAlgorithm getAlgorithm() { result = super.getAlgorithm() }
|
||||
|
||||
/** Gets an input the algorithm is used on, for example the plain text input to be encrypted. */
|
||||
DataFlow::Node getAnInput() { result = super.getAnInput() }
|
||||
|
||||
/**
|
||||
* Gets the block mode used to perform this cryptographic operation.
|
||||
* This may have no result - for example if the `CryptographicAlgorithm` used
|
||||
* is a stream cipher rather than a block cipher.
|
||||
*/
|
||||
BlockMode getBlockMode() { result = super.getBlockMode() }
|
||||
}
|
||||
|
||||
/** Provides classes for modeling new applications of a cryptographic algorithms. */
|
||||
module CryptographicOperation {
|
||||
/**
|
||||
* A data-flow node that is an application of a cryptographic algorithm. For example,
|
||||
* encryption, decryption, signature-validation.
|
||||
*
|
||||
* Extend this class to model new APIs. If you want to refine existing API models,
|
||||
* extend `CryptographicOperation` instead.
|
||||
*/
|
||||
abstract class Range extends DataFlow::Node {
|
||||
/** Gets the algorithm used, if it matches a known `CryptographicAlgorithm`. */
|
||||
abstract CryptographicAlgorithm getAlgorithm();
|
||||
|
||||
/** Gets an input the algorithm is used on, for example the plain text input to be encrypted. */
|
||||
abstract DataFlow::Node getAnInput();
|
||||
|
||||
/**
|
||||
* Gets the block mode used to perform this cryptographic operation.
|
||||
* This may have no result - for example if the `CryptographicAlgorithm` used
|
||||
* is a stream cipher rather than a block cipher.
|
||||
*/
|
||||
abstract BlockMode getBlockMode();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A cryptographic block cipher mode of operation. This can be used to encrypt
|
||||
* data of arbitrary length using a block encryption algorithm.
|
||||
*/
|
||||
class BlockMode extends string {
|
||||
BlockMode() { this = ["ECB", "CBC", "GCM", "CCM", "CFB", "OFB", "CTR", "OPENPGP"] }
|
||||
|
||||
/** Holds if this block mode is considered to be insecure. */
|
||||
predicate isWeak() { this = "ECB" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,7 +243,7 @@ predicate class_method(
|
||||
* Holds if the literal corresponding to the control flow node `n` has class `cls`.
|
||||
*
|
||||
* Helper predicate for `literal_instantiation`. Prevents a bad join with
|
||||
* `PointsToContext::appliesTo` from occuring.
|
||||
* `PointsToContext::appliesTo` from occurring.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate literal_node_class(ControlFlowNode n, ClassObjectInternal cls) {
|
||||
|
||||
@@ -28,14 +28,14 @@ private module RegexpMatching {
|
||||
* but if `ignorePrefix` is true, it will only match "foo".
|
||||
*/
|
||||
predicate test(string str, boolean ignorePrefix) {
|
||||
none() // maybe overriden in subclasses
|
||||
none() // maybe overridden in subclasses
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `test(..)`, but where the `fillsCaptureGroup` afterwards tells which capture groups were filled by the given string.
|
||||
*/
|
||||
predicate testWithGroups(string str, boolean ignorePrefix) {
|
||||
none() // maybe overriden in subclasses
|
||||
none() // maybe overridden in subclasses
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,9 +32,11 @@ module CodeInjection {
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Sanitizer` instead.
|
||||
*
|
||||
* A sanitizer guard for "code injection" vulnerabilities.
|
||||
*/
|
||||
abstract class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
abstract deprecated class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A source of remote user input, considered as a flow source.
|
||||
@@ -51,5 +53,5 @@ module CodeInjection {
|
||||
/**
|
||||
* A comparison with a constant string, considered as a sanitizer-guard.
|
||||
*/
|
||||
class StringConstCompareAsSanitizerGuard extends SanitizerGuard, StringConstCompare { }
|
||||
class StringConstCompareAsSanitizer extends Sanitizer, StringConstCompareBarrier { }
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ class Configuration extends TaintTracking::Configuration {
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,9 +32,11 @@ module CommandInjection {
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Sanitizer` instead.
|
||||
*
|
||||
* A sanitizer guard for "command injection" vulnerabilities.
|
||||
*/
|
||||
abstract class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
abstract deprecated class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A source of remote user input, considered as a flow source.
|
||||
@@ -83,5 +85,5 @@ module CommandInjection {
|
||||
/**
|
||||
* A comparison with a constant string, considered as a sanitizer-guard.
|
||||
*/
|
||||
class StringConstCompareAsSanitizerGuard extends SanitizerGuard, StringConstCompare { }
|
||||
class StringConstCompareAsSanitizerGuard extends Sanitizer, StringConstCompareBarrier { }
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ class Configuration extends TaintTracking::Configuration {
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,14 +42,18 @@ module LdapInjection {
|
||||
abstract class FilterSanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `DnSanitizer` instead.
|
||||
*
|
||||
* A sanitizer guard for "ldap injection" vulnerabilities.
|
||||
*/
|
||||
abstract class DnSanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
abstract deprecated class DnSanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `FilterSanitizer` instead.
|
||||
*
|
||||
* A sanitizer guard for "ldap injection" vulnerabilities.
|
||||
*/
|
||||
abstract class FilterSanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
abstract deprecated class FilterSanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A source of remote user input, considered as a flow source.
|
||||
@@ -73,12 +77,12 @@ module LdapInjection {
|
||||
/**
|
||||
* A comparison with a constant string, considered as a sanitizer-guard.
|
||||
*/
|
||||
class StringConstCompareAsDnSanitizerGuard extends DnSanitizerGuard, StringConstCompare { }
|
||||
class StringConstCompareAsDnSanitizerGuard extends DnSanitizer, StringConstCompareBarrier { }
|
||||
|
||||
/**
|
||||
* A comparison with a constant string, considered as a sanitizer-guard.
|
||||
*/
|
||||
class StringConstCompareAsFilterSanitizerGuard extends FilterSanitizerGuard, StringConstCompare {
|
||||
class StringConstCompareAsFilterSanitizerGuard extends FilterSanitizer, StringConstCompareBarrier {
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,7 +26,7 @@ class DnConfiguration extends TaintTracking::Configuration {
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof DnSanitizer }
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof DnSanitizerGuard
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,7 @@ class FilterConfiguration extends TaintTracking::Configuration {
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof FilterSanitizer }
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof FilterSanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,9 +32,11 @@ module LogInjection {
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Sanitizer` instead.
|
||||
*
|
||||
* A sanitizer guard for "log injection" vulnerabilities.
|
||||
*/
|
||||
abstract class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
abstract deprecated class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A source of remote user input, considered as a flow source.
|
||||
@@ -51,7 +53,7 @@ module LogInjection {
|
||||
/**
|
||||
* A comparison with a constant string, considered as a sanitizer-guard.
|
||||
*/
|
||||
class StringConstCompareAsSanitizerGuard extends SanitizerGuard, StringConstCompare { }
|
||||
class StringConstCompareAsSanitizerGuard extends Sanitizer, StringConstCompareBarrier { }
|
||||
|
||||
/**
|
||||
* A call to replace line breaks, considered as a sanitizer.
|
||||
|
||||
@@ -23,7 +23,7 @@ class Configuration extends TaintTracking::Configuration {
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,11 +90,13 @@ deprecated class NormalizedPathNotCheckedConfiguration extends TaintTracking2::C
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
node instanceof Path::SafeAccessCheck
|
||||
or
|
||||
node instanceof Sanitizer
|
||||
}
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof Path::SafeAccessCheck
|
||||
or
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,9 +43,11 @@ module PathInjection {
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Sanitizer` instead.
|
||||
*
|
||||
* A sanitizer guard for "path injection" vulnerabilities.
|
||||
*/
|
||||
abstract class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
abstract deprecated class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A source of remote user input, considered as a flow source.
|
||||
@@ -59,8 +61,14 @@ module PathInjection {
|
||||
FileSystemAccessAsSink() { this = any(FileSystemAccess e).getAPathArgument() }
|
||||
}
|
||||
|
||||
private import semmle.python.frameworks.data.ModelsAsData
|
||||
|
||||
private class DataAsFileSink extends Sink {
|
||||
DataAsFileSink() { this = ModelOutput::getASinkNode("path-injection").getARhs() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A comparison with a constant string, considered as a sanitizer-guard.
|
||||
*/
|
||||
class StringConstCompareAsSanitizerGuard extends SanitizerGuard, StringConstCompare { }
|
||||
class StringConstCompareAsSanitizerGuard extends Sanitizer, StringConstCompareBarrier { }
|
||||
}
|
||||
|
||||
@@ -47,11 +47,11 @@ class Configuration extends TaintTracking::Configuration {
|
||||
node instanceof Path::PathNormalization and
|
||||
state instanceof NotNormalized
|
||||
or
|
||||
node = any(Path::SafeAccessCheck c).getAGuardedNode() and
|
||||
node instanceof Path::SafeAccessCheck and
|
||||
state instanceof NormalizedUnchecked
|
||||
}
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
|
||||
|
||||
@@ -44,9 +44,11 @@ module PolynomialReDoS {
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Sanitizer` instead.
|
||||
*
|
||||
* A sanitizer guard for "polynomial regular expression denial of service (ReDoS)" vulnerabilities.
|
||||
*/
|
||||
abstract class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
abstract deprecated class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A source of remote user input, considered as a flow source.
|
||||
@@ -74,5 +76,5 @@ module PolynomialReDoS {
|
||||
/**
|
||||
* A comparison with a constant string, considered as a sanitizer-guard.
|
||||
*/
|
||||
class StringConstCompareAsSanitizerGuard extends SanitizerGuard, StringConstCompare { }
|
||||
class StringConstCompareAsSanitizerGuard extends Sanitizer, StringConstCompareBarrier { }
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ class Configuration extends TaintTracking::Configuration {
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,9 +32,11 @@ module ReflectedXss {
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Sanitizer` instead.
|
||||
*
|
||||
* A sanitizer guard for "reflected server-side cross-site scripting" vulnerabilities.
|
||||
*/
|
||||
abstract class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
abstract deprecated class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A source of remote user input, considered as a flow source.
|
||||
@@ -72,7 +74,7 @@ module ReflectedXss {
|
||||
/**
|
||||
* A comparison with a constant string, considered as a sanitizer-guard.
|
||||
*/
|
||||
class StringConstCompareAsSanitizerGuard extends SanitizerGuard, StringConstCompare { }
|
||||
class StringConstCompareAsSanitizerGuard extends Sanitizer, StringConstCompareBarrier { }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for ReflectedXss */
|
||||
|
||||
@@ -23,7 +23,7 @@ class Configuration extends TaintTracking::Configuration {
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,9 +40,11 @@ module RegexInjection {
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Sanitizer` instead.
|
||||
*
|
||||
* A sanitizer guard for "regular expression injection" vulnerabilities.
|
||||
*/
|
||||
abstract class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
abstract deprecated class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A source of remote user input, considered as a flow source.
|
||||
|
||||
@@ -24,7 +24,7 @@ class Configuration extends TaintTracking::Configuration {
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,9 +44,11 @@ module ServerSideRequestForgery {
|
||||
abstract class FullUrlControlSanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Sanitizer` instead.
|
||||
*
|
||||
* A sanitizer guard for "Server-side request forgery" vulnerabilities.
|
||||
*/
|
||||
abstract class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
abstract deprecated class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A source of remote user input, considered as a flow source.
|
||||
@@ -78,7 +80,7 @@ module ServerSideRequestForgery {
|
||||
/**
|
||||
* A comparison with a constant string, considered as a sanitizer-guard.
|
||||
*/
|
||||
class StringConstCompareAsSanitizerGuard extends SanitizerGuard, StringConstCompare { }
|
||||
class StringConstCompareAsSanitizerGuard extends Sanitizer, StringConstCompareBarrier { }
|
||||
|
||||
/**
|
||||
* A string construction (concat, format, f-string) where the left side is not
|
||||
|
||||
@@ -34,7 +34,7 @@ class FullServerSideRequestForgeryConfiguration extends TaintTracking::Configura
|
||||
node instanceof FullUrlControlSanitizer
|
||||
}
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
@@ -65,7 +65,7 @@ class PartialServerSideRequestForgeryConfiguration extends TaintTracking::Config
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,9 +33,11 @@ module SqlInjection {
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Sanitizer` instead.
|
||||
*
|
||||
* A sanitizer guard for "SQL injection" vulnerabilities.
|
||||
*/
|
||||
abstract class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
abstract deprecated class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A source of remote user input, considered as a flow source.
|
||||
@@ -59,5 +61,12 @@ module SqlInjection {
|
||||
/**
|
||||
* A comparison with a constant string, considered as a sanitizer-guard.
|
||||
*/
|
||||
class StringConstCompareAsSanitizerGuard extends SanitizerGuard, StringConstCompare { }
|
||||
class StringConstCompareAsSanitizerGuard extends Sanitizer, StringConstCompareBarrier { }
|
||||
|
||||
private import semmle.python.frameworks.data.ModelsAsData
|
||||
|
||||
/** A sink for sql-injection from model data. */
|
||||
private class DataAsSqlSink extends Sink {
|
||||
DataAsSqlSink() { this = ModelOutput::getASinkNode("sql-injection").getARhs() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ class Configuration extends TaintTracking::Configuration {
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,9 +32,11 @@ module StackTraceExposure {
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Sanitizer` instead.
|
||||
*
|
||||
* A sanitizer guard for "stack trace exposure" vulnerabilities.
|
||||
*/
|
||||
abstract class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
abstract deprecated class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A source of exception info, considered as a flow source.
|
||||
|
||||
@@ -23,7 +23,7 @@ class Configuration extends TaintTracking::Configuration {
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
|
||||
|
||||
@@ -32,9 +32,11 @@ module UnsafeDeserialization {
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Sanitizer` instead.
|
||||
*
|
||||
* A sanitizer guard for "code execution from deserialization" vulnerabilities.
|
||||
*/
|
||||
abstract class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
abstract deprecated class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A source of remote user input, considered as a flow source.
|
||||
@@ -56,5 +58,5 @@ module UnsafeDeserialization {
|
||||
/**
|
||||
* A comparison with a constant string, considered as a sanitizer-guard.
|
||||
*/
|
||||
class StringConstCompareAsSanitizerGuard extends SanitizerGuard, StringConstCompare { }
|
||||
class StringConstCompareAsSanitizerGuard extends Sanitizer, StringConstCompareBarrier { }
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ class Configuration extends TaintTracking::Configuration {
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,9 +32,11 @@ module UrlRedirect {
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Sanitizer` instead.
|
||||
*
|
||||
* A sanitizer guard for "URL redirection" vulnerabilities.
|
||||
*/
|
||||
abstract class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
abstract deprecated class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A source of remote user input, considered as a flow source.
|
||||
@@ -67,5 +69,5 @@ module UrlRedirect {
|
||||
/**
|
||||
* A comparison with a constant string, considered as a sanitizer-guard.
|
||||
*/
|
||||
class StringConstCompareAsSanitizerGuard extends SanitizerGuard, StringConstCompare { }
|
||||
class StringConstCompareAsSanitizerGuard extends Sanitizer, StringConstCompareBarrier { }
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ class Configuration extends TaintTracking::Configuration {
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,9 +30,11 @@ module XpathInjection {
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `Sanitizer` instead.
|
||||
*
|
||||
* A sanitizer guard for "XPath injection" vulnerabilities.
|
||||
*/
|
||||
abstract class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
abstract deprecated class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A source of remote user input, considered as a flow source.
|
||||
|
||||
@@ -23,7 +23,7 @@ class Configuration extends TaintTracking::Configuration {
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
deprecated override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
* either a single character, a set of characters represented by a
|
||||
* character class, or the set of all characters.
|
||||
* * The product automaton is constructed lazily, starting with pair states
|
||||
* `(q, q)` where `q` is a fork, and proceding along an over-approximate
|
||||
* `(q, q)` where `q` is a fork, and proceeding along an over-approximate
|
||||
* step relation.
|
||||
* * The over-approximate step relation allows transitions along pairs of
|
||||
* abstract input symbols where the symbols have overlap in the characters they accept.
|
||||
@@ -141,26 +141,28 @@ private class StatePair extends TStatePair {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds for all constructed state pairs.
|
||||
* Holds for `(fork, fork)` state pairs when `isFork(fork, _, _, _, _)` holds.
|
||||
*
|
||||
* Used in `statePairDist`
|
||||
* Used in `statePairDistToFork`
|
||||
*/
|
||||
private predicate isStatePair(StatePair p) { any() }
|
||||
private predicate isStatePairFork(StatePair p) {
|
||||
exists(State fork | p = MkStatePair(fork, fork) and isFork(fork, _, _, _, _))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there are transitions from the components of `q` to the corresponding
|
||||
* components of `r`.
|
||||
*
|
||||
* Used in `statePairDist`
|
||||
* Used in `statePairDistToFork`
|
||||
*/
|
||||
private predicate delta2(StatePair q, StatePair r) { step(q, _, _, r) }
|
||||
private predicate reverseStep(StatePair r, StatePair q) { step(q, _, _, r) }
|
||||
|
||||
/**
|
||||
* Gets the minimum length of a path from `q` to `r` in the
|
||||
* product automaton.
|
||||
*/
|
||||
private int statePairDist(StatePair q, StatePair r) =
|
||||
shortestDistances(isStatePair/1, delta2/2)(q, r, result)
|
||||
private int statePairDistToFork(StatePair q, StatePair r) =
|
||||
shortestDistances(isStatePairFork/1, reverseStep/2)(r, q, result)
|
||||
|
||||
/**
|
||||
* Holds if there are transitions from `q` to `r1` and from `q` to `r2`
|
||||
@@ -255,14 +257,7 @@ private predicate step(StatePair q, InputSymbol s1, InputSymbol s2, State r1, St
|
||||
|
||||
private newtype TTrace =
|
||||
Nil() or
|
||||
Step(InputSymbol s1, InputSymbol s2, TTrace t) {
|
||||
exists(StatePair p |
|
||||
isReachableFromFork(_, p, t, _) and
|
||||
step(p, s1, s2, _)
|
||||
)
|
||||
or
|
||||
t = Nil() and isFork(_, s1, s2, _, _)
|
||||
}
|
||||
Step(InputSymbol s1, InputSymbol s2, TTrace t) { isReachableFromFork(_, _, s1, s2, t, _) }
|
||||
|
||||
/**
|
||||
* A list of pairs of input symbols that describe a path in the product automaton
|
||||
@@ -284,20 +279,28 @@ private class Trace extends TTrace {
|
||||
* a path from `r` back to `(fork, fork)` with `rem` steps.
|
||||
*/
|
||||
private predicate isReachableFromFork(State fork, StatePair r, Trace w, int rem) {
|
||||
exists(InputSymbol s1, InputSymbol s2, Trace v |
|
||||
isReachableFromFork(fork, r, s1, s2, v, rem) and
|
||||
w = Step(s1, s2, v)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isReachableFromFork(
|
||||
State fork, StatePair r, InputSymbol s1, InputSymbol s2, Trace v, int rem
|
||||
) {
|
||||
// base case
|
||||
exists(InputSymbol s1, InputSymbol s2, State q1, State q2 |
|
||||
exists(State q1, State q2 |
|
||||
isFork(fork, s1, s2, q1, q2) and
|
||||
r = MkStatePair(q1, q2) and
|
||||
w = Step(s1, s2, Nil()) and
|
||||
rem = statePairDist(r, MkStatePair(fork, fork))
|
||||
v = Nil() and
|
||||
rem = statePairDistToFork(r, MkStatePair(fork, fork))
|
||||
)
|
||||
or
|
||||
// recursive case
|
||||
exists(StatePair p, Trace v, InputSymbol s1, InputSymbol s2 |
|
||||
exists(StatePair p |
|
||||
isReachableFromFork(fork, p, v, rem + 1) and
|
||||
step(p, s1, s2, r) and
|
||||
w = Step(s1, s2, v) and
|
||||
rem >= statePairDist(r, MkStatePair(fork, fork))
|
||||
rem = statePairDistToFork(r, MkStatePair(fork, fork))
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ deprecated class ExternalStringSequenceKind extends SequenceKind {
|
||||
}
|
||||
|
||||
/**
|
||||
* An hierachical dictionary or list where the entire structure is externally controlled
|
||||
* An hierarchical dictionary or list where the entire structure is externally controlled
|
||||
* This is typically a parsed JSON object.
|
||||
*/
|
||||
deprecated class ExternalJsonKind extends TaintKind {
|
||||
|
||||
Reference in New Issue
Block a user