mirror of
https://github.com/github/codeql.git
synced 2025-12-20 10:46:30 +01:00
Merge branch 'main' into jcogs33/shared-sink-kind-validation
This commit is contained in:
9
python/ql/lib/change-notes/2023-06-09-delete-deps.md
Normal file
9
python/ql/lib/change-notes/2023-06-09-delete-deps.md
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Deleted many deprecated predicates and classes with uppercase `API`, `HTTP`, `XSS`, `SQL`, etc. in their names. Use the PascalCased versions instead.
|
||||
* Deleted the deprecated `getName()` predicate from the `Container` class, use `getAbsolutePath()` instead.
|
||||
* Deleted many deprecated module names that started with a lowercase letter, use the versions that start with an uppercase letter instead.
|
||||
* Deleted many deprecated predicates in `PointsTo.qll`.
|
||||
* Deleted many deprecated files from the `semmle.python.security` package.
|
||||
* Deleted the deprecated `BottleRoutePointToExtension` class from `Extensions.qll`.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added modeling of taint flow through the template argument of `flask.render_template_string` and `flask.stream_template_string`.
|
||||
@@ -154,12 +154,6 @@ abstract class Container extends @container {
|
||||
*/
|
||||
string toString() { result = this.getAbsolutePath() }
|
||||
|
||||
/**
|
||||
* Gets the name of this container.
|
||||
* DEPRECATED: Use `getAbsolutePath` instead.
|
||||
*/
|
||||
deprecated string getName() { result = this.getAbsolutePath() }
|
||||
|
||||
/**
|
||||
* Gets the relative path of this file or folder from the root folder of the
|
||||
* analyzed source location. The relative path of the root folder itself is
|
||||
|
||||
@@ -38,6 +38,7 @@ private import DataFlowPrivate
|
||||
private import FlowSummaryImpl as FlowSummaryImpl
|
||||
private import FlowSummaryImplSpecific as FlowSummaryImplSpecific
|
||||
private import semmle.python.internal.CachedStages
|
||||
private import semmle.python.dataflow.new.internal.TypeTracker::CallGraphConstruction as CallGraphConstruction
|
||||
|
||||
newtype TParameterPosition =
|
||||
/** Used for `self` in methods, and `cls` in classmethods. */
|
||||
@@ -464,103 +465,105 @@ private predicate ignoreForCallGraph(File f) {
|
||||
f.getAbsolutePath().matches("%/site-packages/sympy/%")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the function `func`.
|
||||
*/
|
||||
private TypeTrackingNode functionTracker(TypeTracker t, Function func) {
|
||||
not ignoreForCallGraph(result.getLocation().getFile()) and
|
||||
t.start() and
|
||||
(
|
||||
result.asExpr() = func.getDefinition()
|
||||
private module TrackFunctionInput implements CallGraphConstruction::Simple::InputSig {
|
||||
class State = Function;
|
||||
|
||||
predicate start(Node start, Function func) {
|
||||
start.asExpr() = func.getDefinition()
|
||||
or
|
||||
// when a function is decorated, it's the result of the (last) decorator call that
|
||||
// is used
|
||||
result.asExpr() = func.getDefinition().(FunctionExpr).getADecoratorCall()
|
||||
)
|
||||
or
|
||||
not ignoreForCallGraph(result.getLocation().getFile()) and
|
||||
exists(TypeTracker t2 | result = functionTracker(t2, func).track(t2, t))
|
||||
start.asExpr() = func.getDefinition().(FunctionExpr).getADecoratorCall()
|
||||
}
|
||||
|
||||
predicate filter(Node n) { ignoreForCallGraph(n.getLocation().getFile()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the function `func`.
|
||||
*/
|
||||
Node functionTracker(Function func) { functionTracker(TypeTracker::end(), func).flowsTo(result) }
|
||||
Node functionTracker(Function func) {
|
||||
CallGraphConstruction::Simple::Make<TrackFunctionInput>::track(func)
|
||||
.(LocalSourceNode)
|
||||
.flowsTo(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the class `cls`.
|
||||
*/
|
||||
private TypeTrackingNode classTracker(TypeTracker t, Class cls) {
|
||||
not ignoreForCallGraph(result.getLocation().getFile()) and
|
||||
t.start() and
|
||||
(
|
||||
result.asExpr() = cls.getParent()
|
||||
private module TrackClassInput implements CallGraphConstruction::Simple::InputSig {
|
||||
class State = Class;
|
||||
|
||||
predicate start(Node start, Class cls) {
|
||||
start.asExpr() = cls.getParent()
|
||||
or
|
||||
// when a class is decorated, it's the result of the (last) decorator call that
|
||||
// is used
|
||||
result.asExpr() = cls.getParent().getADecoratorCall()
|
||||
start.asExpr() = cls.getParent().getADecoratorCall()
|
||||
or
|
||||
// `type(obj)`, where obj is an instance of this class
|
||||
result = getTypeCall() and
|
||||
result.(CallCfgNode).getArg(0) = classInstanceTracker(cls)
|
||||
)
|
||||
or
|
||||
not ignoreForCallGraph(result.getLocation().getFile()) and
|
||||
exists(TypeTracker t2 | result = classTracker(t2, cls).track(t2, t)) and
|
||||
not result.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
|
||||
start = getTypeCall() and
|
||||
start.(CallCfgNode).getArg(0) = classInstanceTracker(cls)
|
||||
}
|
||||
|
||||
predicate filter(Node n) {
|
||||
ignoreForCallGraph(n.getLocation().getFile())
|
||||
or
|
||||
n.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the class `cls`.
|
||||
*/
|
||||
Node classTracker(Class cls) { classTracker(TypeTracker::end(), cls).flowsTo(result) }
|
||||
Node classTracker(Class cls) {
|
||||
CallGraphConstruction::Simple::Make<TrackClassInput>::track(cls).(LocalSourceNode).flowsTo(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to an instance of the class `cls`.
|
||||
*/
|
||||
private TypeTrackingNode classInstanceTracker(TypeTracker t, Class cls) {
|
||||
not ignoreForCallGraph(result.getLocation().getFile()) and
|
||||
t.start() and
|
||||
resolveClassCall(result.(CallCfgNode).asCfgNode(), cls)
|
||||
or
|
||||
// result of `super().__new__` as used in a `__new__` method implementation
|
||||
not ignoreForCallGraph(result.getLocation().getFile()) and
|
||||
t.start() and
|
||||
exists(Class classUsedInSuper |
|
||||
fromSuperNewCall(result.(CallCfgNode).asCfgNode(), classUsedInSuper, _, _) and
|
||||
classUsedInSuper = getADirectSuperclass*(cls)
|
||||
)
|
||||
or
|
||||
not ignoreForCallGraph(result.getLocation().getFile()) and
|
||||
exists(TypeTracker t2 | result = classInstanceTracker(t2, cls).track(t2, t)) and
|
||||
not result.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
|
||||
private module TrackClassInstanceInput implements CallGraphConstruction::Simple::InputSig {
|
||||
class State = Class;
|
||||
|
||||
predicate start(Node start, Class cls) {
|
||||
resolveClassCall(start.(CallCfgNode).asCfgNode(), cls)
|
||||
or
|
||||
// result of `super().__new__` as used in a `__new__` method implementation
|
||||
exists(Class classUsedInSuper |
|
||||
fromSuperNewCall(start.(CallCfgNode).asCfgNode(), classUsedInSuper, _, _) and
|
||||
classUsedInSuper = getADirectSuperclass*(cls)
|
||||
)
|
||||
}
|
||||
|
||||
predicate filter(Node n) {
|
||||
ignoreForCallGraph(n.getLocation().getFile())
|
||||
or
|
||||
n.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to an instance of the class `cls`.
|
||||
*/
|
||||
Node classInstanceTracker(Class cls) {
|
||||
classInstanceTracker(TypeTracker::end(), cls).flowsTo(result)
|
||||
CallGraphConstruction::Simple::Make<TrackClassInstanceInput>::track(cls)
|
||||
.(LocalSourceNode)
|
||||
.flowsTo(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the `self` argument of a method on class `classWithMethod`.
|
||||
* The method cannot be a `staticmethod` or `classmethod`.
|
||||
*/
|
||||
private TypeTrackingNode selfTracker(TypeTracker t, Class classWithMethod) {
|
||||
not ignoreForCallGraph(result.getLocation().getFile()) and
|
||||
t.start() and
|
||||
exists(Function func |
|
||||
func = classWithMethod.getAMethod() and
|
||||
not isStaticmethod(func) and
|
||||
not isClassmethod(func)
|
||||
|
|
||||
result.asExpr() = func.getArg(0)
|
||||
)
|
||||
or
|
||||
not ignoreForCallGraph(result.getLocation().getFile()) and
|
||||
exists(TypeTracker t2 | result = selfTracker(t2, classWithMethod).track(t2, t)) and
|
||||
not result.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
|
||||
private module TrackSelfInput implements CallGraphConstruction::Simple::InputSig {
|
||||
class State = Class;
|
||||
|
||||
predicate start(Node start, Class classWithMethod) {
|
||||
exists(Function func |
|
||||
func = classWithMethod.getAMethod() and
|
||||
not isStaticmethod(func) and
|
||||
not isClassmethod(func)
|
||||
|
|
||||
start.asExpr() = func.getArg(0)
|
||||
)
|
||||
}
|
||||
|
||||
predicate filter(Node n) {
|
||||
ignoreForCallGraph(n.getLocation().getFile())
|
||||
or
|
||||
n.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -568,33 +571,32 @@ private TypeTrackingNode selfTracker(TypeTracker t, Class classWithMethod) {
|
||||
* The method cannot be a `staticmethod` or `classmethod`.
|
||||
*/
|
||||
Node selfTracker(Class classWithMethod) {
|
||||
selfTracker(TypeTracker::end(), classWithMethod).flowsTo(result)
|
||||
CallGraphConstruction::Simple::Make<TrackSelfInput>::track(classWithMethod)
|
||||
.(LocalSourceNode)
|
||||
.flowsTo(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the enclosing class `classWithMethod` from within one of its
|
||||
* methods, either through the `cls` argument from a `classmethod` or from `type(self)`
|
||||
* from a normal method.
|
||||
*/
|
||||
private TypeTrackingNode clsArgumentTracker(TypeTracker t, Class classWithMethod) {
|
||||
not ignoreForCallGraph(result.getLocation().getFile()) and
|
||||
t.start() and
|
||||
(
|
||||
private module TrackClsArgumentInput implements CallGraphConstruction::Simple::InputSig {
|
||||
class State = Class;
|
||||
|
||||
predicate start(Node start, Class classWithMethod) {
|
||||
exists(Function func |
|
||||
func = classWithMethod.getAMethod() and
|
||||
isClassmethod(func)
|
||||
|
|
||||
result.asExpr() = func.getArg(0)
|
||||
start.asExpr() = func.getArg(0)
|
||||
)
|
||||
or
|
||||
// type(self)
|
||||
result = getTypeCall() and
|
||||
result.(CallCfgNode).getArg(0) = selfTracker(classWithMethod)
|
||||
)
|
||||
or
|
||||
not ignoreForCallGraph(result.getLocation().getFile()) and
|
||||
exists(TypeTracker t2 | result = clsArgumentTracker(t2, classWithMethod).track(t2, t)) and
|
||||
not result.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
|
||||
start = getTypeCall() and
|
||||
start.(CallCfgNode).getArg(0) = selfTracker(classWithMethod)
|
||||
}
|
||||
|
||||
predicate filter(Node n) {
|
||||
ignoreForCallGraph(n.getLocation().getFile())
|
||||
or
|
||||
n.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -603,26 +605,28 @@ private TypeTrackingNode clsArgumentTracker(TypeTracker t, Class classWithMethod
|
||||
* from a normal method.
|
||||
*/
|
||||
Node clsArgumentTracker(Class classWithMethod) {
|
||||
clsArgumentTracker(TypeTracker::end(), classWithMethod).flowsTo(result)
|
||||
CallGraphConstruction::Simple::Make<TrackClsArgumentInput>::track(classWithMethod)
|
||||
.(LocalSourceNode)
|
||||
.flowsTo(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the result of calling `super` without any argument, where the
|
||||
* call happened in the method `func` (either a method or a classmethod).
|
||||
*/
|
||||
private TypeTrackingNode superCallNoArgumentTracker(TypeTracker t, Function func) {
|
||||
not ignoreForCallGraph(result.getLocation().getFile()) and
|
||||
t.start() and
|
||||
not isStaticmethod(func) and
|
||||
exists(CallCfgNode call | result = call |
|
||||
call = getSuperCall() and
|
||||
not exists(call.getArg(_)) and
|
||||
call.getScope() = func
|
||||
)
|
||||
or
|
||||
not ignoreForCallGraph(result.getLocation().getFile()) and
|
||||
exists(TypeTracker t2 | result = superCallNoArgumentTracker(t2, func).track(t2, t)) and
|
||||
not result.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
|
||||
private module TrackSuperCallNoArgumentInput implements CallGraphConstruction::Simple::InputSig {
|
||||
class State = Function;
|
||||
|
||||
predicate start(Node start, Function func) {
|
||||
not isStaticmethod(func) and
|
||||
exists(CallCfgNode call | start = call |
|
||||
call = getSuperCall() and
|
||||
not exists(call.getArg(_)) and
|
||||
call.getScope() = func
|
||||
)
|
||||
}
|
||||
|
||||
predicate filter(Node n) {
|
||||
ignoreForCallGraph(n.getLocation().getFile())
|
||||
or
|
||||
n.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -630,25 +634,30 @@ private TypeTrackingNode superCallNoArgumentTracker(TypeTracker t, Function func
|
||||
* call happened in the method `func` (either a method or a classmethod).
|
||||
*/
|
||||
Node superCallNoArgumentTracker(Function func) {
|
||||
superCallNoArgumentTracker(TypeTracker::end(), func).flowsTo(result)
|
||||
CallGraphConstruction::Simple::Make<TrackSuperCallNoArgumentInput>::track(func)
|
||||
.(LocalSourceNode)
|
||||
.flowsTo(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to the result of calling `super` with 2 arguments, where the
|
||||
* first is a reference to the class `cls`, and the second argument is `obj`.
|
||||
*/
|
||||
private TypeTrackingNode superCallTwoArgumentTracker(TypeTracker t, Class cls, Node obj) {
|
||||
not ignoreForCallGraph(result.getLocation().getFile()) and
|
||||
t.start() and
|
||||
exists(CallCfgNode call | result = call |
|
||||
private module TrackSuperCallTwoArgumentInput implements CallGraphConstruction::Simple::InputSig {
|
||||
additional predicate superCall(CallCfgNode call, Class cls, Node obj) {
|
||||
call = getSuperCall() and
|
||||
call.getArg(0) = classTracker(cls) and
|
||||
call.getArg(1) = obj
|
||||
)
|
||||
or
|
||||
not ignoreForCallGraph(result.getLocation().getFile()) and
|
||||
exists(TypeTracker t2 | result = superCallTwoArgumentTracker(t2, cls, obj).track(t2, t)) and
|
||||
not result.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
|
||||
}
|
||||
|
||||
class State = CallCfgNode;
|
||||
|
||||
predicate start(Node start, CallCfgNode call) {
|
||||
superCall(call, _, _) and
|
||||
start = call
|
||||
}
|
||||
|
||||
predicate filter(Node n) {
|
||||
ignoreForCallGraph(n.getLocation().getFile())
|
||||
or
|
||||
n.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -656,7 +665,12 @@ private TypeTrackingNode superCallTwoArgumentTracker(TypeTracker t, Class cls, N
|
||||
* first is a reference to the class `cls`, and the second argument is `obj`.
|
||||
*/
|
||||
Node superCallTwoArgumentTracker(Class cls, Node obj) {
|
||||
superCallTwoArgumentTracker(TypeTracker::end(), cls, obj).flowsTo(result)
|
||||
exists(CallCfgNode call |
|
||||
TrackSuperCallTwoArgumentInput::superCall(call, cls, obj) and
|
||||
CallGraphConstruction::Simple::Make<TrackSuperCallTwoArgumentInput>::track(call)
|
||||
.(LocalSourceNode)
|
||||
.flowsTo(result)
|
||||
)
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
@@ -800,20 +814,30 @@ Function findFunctionAccordingToMroKnownStartingClass(Class startingClass, strin
|
||||
// =============================================================================
|
||||
// attribute trackers
|
||||
// =============================================================================
|
||||
/** Gets a reference to the attribute read `attr` */
|
||||
private TypeTrackingNode attrReadTracker(TypeTracker t, AttrRead attr) {
|
||||
t.start() and
|
||||
result = attr and
|
||||
attr.getObject() in [
|
||||
classTracker(_), classInstanceTracker(_), selfTracker(_), clsArgumentTracker(_),
|
||||
superCallNoArgumentTracker(_), superCallTwoArgumentTracker(_, _)
|
||||
]
|
||||
or
|
||||
exists(TypeTracker t2 | result = attrReadTracker(t2, attr).track(t2, t))
|
||||
private module TrackAttrReadInput implements CallGraphConstruction::Simple::InputSig {
|
||||
class State = AttrRead;
|
||||
|
||||
predicate start(Node start, AttrRead attr) {
|
||||
start = attr and
|
||||
attr.getObject() in [
|
||||
classTracker(_), classInstanceTracker(_), selfTracker(_), clsArgumentTracker(_),
|
||||
superCallNoArgumentTracker(_), superCallTwoArgumentTracker(_, _)
|
||||
]
|
||||
}
|
||||
|
||||
predicate filter(Node n) {
|
||||
ignoreForCallGraph(n.getLocation().getFile())
|
||||
or
|
||||
n.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets a reference to the attribute read `attr` */
|
||||
Node attrReadTracker(AttrRead attr) { attrReadTracker(TypeTracker::end(), attr).flowsTo(result) }
|
||||
Node attrReadTracker(AttrRead attr) {
|
||||
CallGraphConstruction::Simple::Make<TrackAttrReadInput>::track(attr)
|
||||
.(LocalSourceNode)
|
||||
.flowsTo(result)
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// call and argument resolution
|
||||
@@ -1292,7 +1316,9 @@ newtype TDataFlowCall =
|
||||
TNormalCall(CallNode call, Function target, CallType type) { resolveCall(call, target, type) } or
|
||||
TPotentialLibraryCall(CallNode call) or
|
||||
/** A synthesized call inside a summarized callable */
|
||||
TSummaryCall(FlowSummaryImpl::Public::SummarizedCallable c, Node receiver) {
|
||||
TSummaryCall(
|
||||
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
|
||||
) {
|
||||
FlowSummaryImpl::Private::summaryCallbackRange(c, receiver)
|
||||
}
|
||||
|
||||
@@ -1424,12 +1450,12 @@ class PotentialLibraryCall extends ExtractedDataFlowCall, TPotentialLibraryCall
|
||||
*/
|
||||
class SummaryCall extends DataFlowCall, TSummaryCall {
|
||||
private FlowSummaryImpl::Public::SummarizedCallable c;
|
||||
private Node receiver;
|
||||
private FlowSummaryImpl::Private::SummaryNode receiver;
|
||||
|
||||
SummaryCall() { this = TSummaryCall(c, receiver) }
|
||||
|
||||
/** Gets the data flow node that this call targets. */
|
||||
Node getReceiver() { result = receiver }
|
||||
FlowSummaryImpl::Private::SummaryNode getReceiver() { result = receiver }
|
||||
|
||||
override DataFlowCallable getEnclosingCallable() { result.asLibraryCallable() = c }
|
||||
|
||||
@@ -1462,44 +1488,35 @@ abstract class ParameterNodeImpl extends Node {
|
||||
}
|
||||
|
||||
/** A parameter for a library callable with a flow summary. */
|
||||
class SummaryParameterNode extends ParameterNodeImpl, TSummaryParameterNode {
|
||||
private FlowSummaryImpl::Public::SummarizedCallable sc;
|
||||
private ParameterPosition pos;
|
||||
class SummaryParameterNode extends ParameterNodeImpl, FlowSummaryNode {
|
||||
SummaryParameterNode() {
|
||||
FlowSummaryImpl::Private::summaryParameterNode(this.getSummaryNode(), _)
|
||||
}
|
||||
|
||||
SummaryParameterNode() { this = TSummaryParameterNode(sc, pos) }
|
||||
private ParameterPosition getPosition() {
|
||||
FlowSummaryImpl::Private::summaryParameterNode(this.getSummaryNode(), result)
|
||||
}
|
||||
|
||||
override Parameter getParameter() { none() }
|
||||
|
||||
override predicate isParameterOf(DataFlowCallable c, ParameterPosition ppos) {
|
||||
sc = c.asLibraryCallable() and ppos = pos
|
||||
}
|
||||
|
||||
override DataFlowCallable getEnclosingCallable() { result.asLibraryCallable() = sc }
|
||||
|
||||
override string toString() { result = "parameter " + pos + " of " + sc }
|
||||
|
||||
// Hack to return "empty location"
|
||||
override predicate hasLocationInfo(
|
||||
string file, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
file = "" and
|
||||
startline = 0 and
|
||||
startcolumn = 0 and
|
||||
endline = 0 and
|
||||
endcolumn = 0
|
||||
this.getSummarizedCallable() = c.asLibraryCallable() and ppos = this.getPosition()
|
||||
}
|
||||
}
|
||||
|
||||
/** A data-flow node used to model flow summaries. */
|
||||
class SummaryNode extends Node, TSummaryNode {
|
||||
private FlowSummaryImpl::Public::SummarizedCallable c;
|
||||
private FlowSummaryImpl::Private::SummaryNodeState state;
|
||||
class FlowSummaryNode extends Node, TFlowSummaryNode {
|
||||
FlowSummaryImpl::Private::SummaryNode getSummaryNode() { this = TFlowSummaryNode(result) }
|
||||
|
||||
SummaryNode() { this = TSummaryNode(c, state) }
|
||||
FlowSummaryImpl::Public::SummarizedCallable getSummarizedCallable() {
|
||||
result = this.getSummaryNode().getSummarizedCallable()
|
||||
}
|
||||
|
||||
override DataFlowCallable getEnclosingCallable() { result.asLibraryCallable() = c }
|
||||
override DataFlowCallable getEnclosingCallable() {
|
||||
result.asLibraryCallable() = this.getSummarizedCallable()
|
||||
}
|
||||
|
||||
override string toString() { result = "[summary] " + state + " in " + c }
|
||||
override string toString() { result = this.getSummaryNode().toString() }
|
||||
|
||||
// Hack to return "empty location"
|
||||
override predicate hasLocationInfo(
|
||||
@@ -1513,26 +1530,30 @@ class SummaryNode extends Node, TSummaryNode {
|
||||
}
|
||||
}
|
||||
|
||||
private class SummaryReturnNode extends SummaryNode, ReturnNode {
|
||||
private class SummaryReturnNode extends FlowSummaryNode, ReturnNode {
|
||||
private ReturnKind rk;
|
||||
|
||||
SummaryReturnNode() { FlowSummaryImpl::Private::summaryReturnNode(this, rk) }
|
||||
SummaryReturnNode() { FlowSummaryImpl::Private::summaryReturnNode(this.getSummaryNode(), rk) }
|
||||
|
||||
override ReturnKind getKind() { result = rk }
|
||||
}
|
||||
|
||||
private class SummaryArgumentNode extends SummaryNode, ArgumentNode {
|
||||
SummaryArgumentNode() { FlowSummaryImpl::Private::summaryArgumentNode(_, this, _) }
|
||||
private class SummaryArgumentNode extends FlowSummaryNode, ArgumentNode {
|
||||
SummaryArgumentNode() {
|
||||
FlowSummaryImpl::Private::summaryArgumentNode(_, this.getSummaryNode(), _)
|
||||
}
|
||||
|
||||
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
|
||||
FlowSummaryImpl::Private::summaryArgumentNode(call, this, pos)
|
||||
FlowSummaryImpl::Private::summaryArgumentNode(call, this.getSummaryNode(), pos)
|
||||
}
|
||||
}
|
||||
|
||||
private class SummaryPostUpdateNode extends SummaryNode, PostUpdateNodeImpl {
|
||||
private Node pre;
|
||||
private class SummaryPostUpdateNode extends FlowSummaryNode, PostUpdateNodeImpl {
|
||||
private FlowSummaryNode pre;
|
||||
|
||||
SummaryPostUpdateNode() { FlowSummaryImpl::Private::summaryPostUpdateNode(this, pre) }
|
||||
SummaryPostUpdateNode() {
|
||||
FlowSummaryImpl::Private::summaryPostUpdateNode(this.getSummaryNode(), pre.getSummaryNode())
|
||||
}
|
||||
|
||||
override Node getPreUpdateNode() { result = pre }
|
||||
}
|
||||
@@ -1601,11 +1622,11 @@ private module OutNodes {
|
||||
}
|
||||
}
|
||||
|
||||
private class SummaryOutNode extends SummaryNode, OutNode {
|
||||
SummaryOutNode() { FlowSummaryImpl::Private::summaryOutNode(_, this, _) }
|
||||
private class SummaryOutNode extends FlowSummaryNode, OutNode {
|
||||
SummaryOutNode() { FlowSummaryImpl::Private::summaryOutNode(_, this.getSummaryNode(), _) }
|
||||
|
||||
override DataFlowCall getCall(ReturnKind kind) {
|
||||
FlowSummaryImpl::Private::summaryOutNode(result, this, kind)
|
||||
FlowSummaryImpl::Private::summaryOutNode(result, this.getSummaryNode(), kind)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1135,8 +1135,8 @@ module Impl<FullStateConfigSig Config> {
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow
|
||||
);
|
||||
|
||||
bindingset[node, state, t, ap]
|
||||
predicate filter(NodeEx node, FlowState state, Typ t, Ap ap);
|
||||
bindingset[node, state, t0, ap]
|
||||
predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t);
|
||||
|
||||
bindingset[typ, contentType]
|
||||
predicate typecheckStore(Typ typ, DataFlowType contentType);
|
||||
@@ -1199,17 +1199,21 @@ module Impl<FullStateConfigSig Config> {
|
||||
NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT,
|
||||
ApOption argAp, Typ t, Ap ap, ApApprox apa
|
||||
) {
|
||||
fwdFlow0(node, state, cc, summaryCtx, argT, argAp, t, ap, apa) and
|
||||
PrevStage::revFlow(node, state, apa) and
|
||||
filter(node, state, t, ap)
|
||||
fwdFlow1(node, state, cc, summaryCtx, argT, argAp, _, t, ap, apa)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
additional predicate fwdFlow(
|
||||
private predicate fwdFlow1(
|
||||
NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT,
|
||||
ApOption argAp, Typ t, Ap ap
|
||||
ApOption argAp, Typ t0, Typ t, Ap ap, ApApprox apa
|
||||
) {
|
||||
fwdFlow(node, state, cc, summaryCtx, argT, argAp, t, ap, _)
|
||||
fwdFlow0(node, state, cc, summaryCtx, argT, argAp, t0, ap, apa) and
|
||||
PrevStage::revFlow(node, state, apa) and
|
||||
filter(node, state, t0, ap, t)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate typeStrengthen(Typ t0, Ap ap, Typ t) {
|
||||
fwdFlow1(_, _, _, _, _, _, t0, t, ap, _) and t0 != t
|
||||
}
|
||||
|
||||
pragma[assume_small_delta]
|
||||
@@ -1339,6 +1343,11 @@ module Impl<FullStateConfigSig Config> {
|
||||
private predicate fwdFlowConsCand(Typ t2, Ap cons, Content c, Typ t1, Ap tail) {
|
||||
fwdFlowStore(_, t1, tail, c, t2, _, _, _, _, _, _) and
|
||||
cons = apCons(c, t1, tail)
|
||||
or
|
||||
exists(Typ t0 |
|
||||
typeStrengthen(t0, cons, t2) and
|
||||
fwdFlowConsCand(t0, cons, c, t1, tail)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1359,7 +1368,7 @@ module Impl<FullStateConfigSig Config> {
|
||||
ParamNodeOption summaryCtx, TypOption argT, ApOption argAp
|
||||
) {
|
||||
exists(ApHeadContent apc |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argT, argAp, t, ap) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argT, argAp, t, ap, _) and
|
||||
apc = getHeadContent(ap) and
|
||||
readStepCand0(node1, apc, c, node2)
|
||||
)
|
||||
@@ -1520,14 +1529,14 @@ module Impl<FullStateConfigSig Config> {
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
) {
|
||||
revFlow0(node, state, returnCtx, returnAp, ap) and
|
||||
fwdFlow(node, state, _, _, _, _, _, ap)
|
||||
fwdFlow(node, state, _, _, _, _, _, ap, _)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlow0(
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
) {
|
||||
fwdFlow(node, state, _, _, _, _, _, ap) and
|
||||
fwdFlow(node, state, _, _, _, _, _, ap, _) and
|
||||
sinkNode(node, state) and
|
||||
(
|
||||
if hasSinkCallCtx()
|
||||
@@ -1780,13 +1789,13 @@ module Impl<FullStateConfigSig Config> {
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples
|
||||
) {
|
||||
fwd = true and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, _, _)) and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, _, _, _)) and
|
||||
fields = count(Content f0 | fwdConsCand(f0, _, _)) and
|
||||
conscand = count(Content f0, Typ t, Ap ap | fwdConsCand(f0, t, ap)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, _, _)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, _, _, _)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT,
|
||||
ApOption argAp, Typ t, Ap ap | fwdFlow(n, state, cc, summaryCtx, argT, argAp, t, ap))
|
||||
ApOption argAp, Typ t, Ap ap | fwdFlow(n, state, cc, summaryCtx, argT, argAp, t, ap, _))
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx node | revFlow(node, _, _, _, _)) and
|
||||
@@ -1963,10 +1972,10 @@ module Impl<FullStateConfigSig Config> {
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[node, state, t, ap]
|
||||
predicate filter(NodeEx node, FlowState state, Typ t, Ap ap) {
|
||||
bindingset[node, state, t0, ap]
|
||||
predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) {
|
||||
PrevStage::revFlowState(state) and
|
||||
exists(t) and
|
||||
t0 = t and
|
||||
exists(ap) and
|
||||
not stateBarrier(node, state) and
|
||||
(
|
||||
@@ -2197,8 +2206,8 @@ module Impl<FullStateConfigSig Config> {
|
||||
import BooleanCallContext
|
||||
|
||||
predicate localStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
DataFlowType t, LocalCc lcc
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t,
|
||||
LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, t, _) and
|
||||
exists(lcc)
|
||||
@@ -2218,10 +2227,16 @@ module Impl<FullStateConfigSig Config> {
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[node, state, t, ap]
|
||||
predicate filter(NodeEx node, FlowState state, Typ t, Ap ap) {
|
||||
bindingset[node, state, t0, ap]
|
||||
predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) {
|
||||
exists(state) and
|
||||
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), t) else any()) and
|
||||
// We can get away with not using type strengthening here, since we aren't
|
||||
// going to use the tracked types in the construction of Stage 4 access
|
||||
// paths. For Stage 4 and onwards, the tracked types must be consistent as
|
||||
// the cons candidates including types are used to construct subsequent
|
||||
// access path approximations.
|
||||
t0 = t and
|
||||
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), t0) else any()) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
@@ -2241,6 +2256,16 @@ module Impl<FullStateConfigSig Config> {
|
||||
import MkStage<Stage2>::Stage<Stage3Param>
|
||||
}
|
||||
|
||||
bindingset[node, t0]
|
||||
private predicate strengthenType(NodeEx node, DataFlowType t0, DataFlowType t) {
|
||||
if castingNodeEx(node)
|
||||
then
|
||||
exists(DataFlowType nt | nt = node.getDataFlowType() |
|
||||
if typeStrongerThan(nt, t0) then t = nt else (compatibleTypes(nt, t0) and t = t0)
|
||||
)
|
||||
else t = t0
|
||||
}
|
||||
|
||||
private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
private module PrevStage = Stage3;
|
||||
|
||||
@@ -2274,8 +2299,8 @@ module Impl<FullStateConfigSig Config> {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate localStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
DataFlowType t, LocalCc lcc
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t,
|
||||
LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, t, _) and
|
||||
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _) and
|
||||
@@ -2333,11 +2358,11 @@ module Impl<FullStateConfigSig Config> {
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[node, state, t, ap]
|
||||
predicate filter(NodeEx node, FlowState state, Typ t, Ap ap) {
|
||||
bindingset[node, state, t0, ap]
|
||||
predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) {
|
||||
exists(state) and
|
||||
not clear(node, ap) and
|
||||
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), t) else any()) and
|
||||
strengthenType(node, t0, t) and
|
||||
(
|
||||
notExpectsContent(node)
|
||||
or
|
||||
@@ -2365,7 +2390,7 @@ module Impl<FullStateConfigSig Config> {
|
||||
exists(AccessPathFront apf |
|
||||
Stage4::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf) and
|
||||
Stage4::fwdFlow(node, state, any(Stage4::CcCall ccc), _, _, TAccessPathFrontSome(argApf), _,
|
||||
apf)
|
||||
apf, _)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2579,8 +2604,8 @@ module Impl<FullStateConfigSig Config> {
|
||||
import LocalCallContext
|
||||
|
||||
predicate localStep(
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
|
||||
DataFlowType t, LocalCc lcc
|
||||
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, Typ t,
|
||||
LocalCc lcc
|
||||
) {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, t, lcc) and
|
||||
PrevStage::revFlow(node1, pragma[only_bind_into](state1), _) and
|
||||
@@ -2609,9 +2634,9 @@ module Impl<FullStateConfigSig Config> {
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[node, state, t, ap]
|
||||
predicate filter(NodeEx node, FlowState state, Typ t, Ap ap) {
|
||||
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), t) else any()) and
|
||||
bindingset[node, state, t0, ap]
|
||||
predicate filter(NodeEx node, FlowState state, Typ t0, Ap ap, Typ t) {
|
||||
strengthenType(node, t0, t) and
|
||||
exists(state) and
|
||||
exists(ap)
|
||||
}
|
||||
@@ -2632,7 +2657,7 @@ module Impl<FullStateConfigSig Config> {
|
||||
Stage5::parameterMayFlowThrough(p, _) and
|
||||
Stage5::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0) and
|
||||
Stage5::fwdFlow(n, state, any(CallContextCall ccc), TParamNodeSome(p.asNode()), _,
|
||||
TAccessPathApproxSome(apa), _, apa0)
|
||||
TAccessPathApproxSome(apa), _, apa0, _)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2649,7 +2674,7 @@ module Impl<FullStateConfigSig Config> {
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, DataFlowType t, AccessPath ap) {
|
||||
exists(AccessPathApprox apa | ap.getApprox() = apa |
|
||||
Stage5::parameterMayFlowThrough(p, apa) and
|
||||
Stage5::fwdFlow(p, state, _, _, _, _, t, apa) and
|
||||
Stage5::fwdFlow(p, state, _, _, Option<DataFlowType>::some(t), _, _, apa, _) and
|
||||
Stage5::revFlow(p, state, _)
|
||||
)
|
||||
}
|
||||
@@ -2820,9 +2845,7 @@ module Impl<FullStateConfigSig Config> {
|
||||
ap = TAccessPathNil()
|
||||
or
|
||||
// ... or a step from an existing PathNode to another node.
|
||||
pathStep(_, node, state, cc, sc, t, ap) and
|
||||
Stage5::revFlow(node, state, ap.getApprox()) and
|
||||
(if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), t) else any())
|
||||
pathStep(_, node, state, cc, sc, t, ap)
|
||||
} or
|
||||
TPathNodeSink(NodeEx node, FlowState state) {
|
||||
exists(PathNodeMid sink |
|
||||
@@ -3340,13 +3363,24 @@ module Impl<FullStateConfigSig Config> {
|
||||
ap = mid.getAp()
|
||||
}
|
||||
|
||||
private predicate pathStep(
|
||||
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t,
|
||||
AccessPath ap
|
||||
) {
|
||||
exists(DataFlowType t0 |
|
||||
pathStep0(mid, node, state, cc, sc, t0, ap) and
|
||||
Stage5::revFlow(node, state, ap.getApprox()) and
|
||||
strengthenType(node, t0, t)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `mid` to `node`. The last step in or out of
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
pragma[assume_small_delta]
|
||||
pragma[nomagic]
|
||||
private predicate pathStep(
|
||||
private predicate pathStep0(
|
||||
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t,
|
||||
AccessPath ap
|
||||
) {
|
||||
@@ -3964,7 +3998,7 @@ module Impl<FullStateConfigSig Config> {
|
||||
ap = TPartialNil() and
|
||||
exists(explorationLimit())
|
||||
or
|
||||
partialPathNodeMk0(node, state, cc, sc1, sc2, sc3, sc4, t, ap) and
|
||||
partialPathStep(_, node, state, cc, sc1, sc2, sc3, sc4, t, ap) and
|
||||
distSrc(node.getEnclosingCallable()) <= explorationLimit()
|
||||
} or
|
||||
TPartialPathNodeRev(
|
||||
@@ -3990,11 +4024,20 @@ module Impl<FullStateConfigSig Config> {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathNodeMk0(
|
||||
NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap
|
||||
private predicate partialPathStep(
|
||||
PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1,
|
||||
TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap
|
||||
) {
|
||||
partialPathStep(_, node, state, cc, sc1, sc2, sc3, sc4, t, ap) and
|
||||
partialPathStep1(mid, node, state, cc, sc1, sc2, sc3, sc4, _, t, ap)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathStep1(
|
||||
PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1,
|
||||
TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t0, DataFlowType t,
|
||||
PartialAccessPath ap
|
||||
) {
|
||||
partialPathStep0(mid, node, state, cc, sc1, sc2, sc3, sc4, t0, ap) and
|
||||
not fullBarrier(node) and
|
||||
not stateBarrier(node, state) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
@@ -4002,9 +4045,14 @@ module Impl<FullStateConfigSig Config> {
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), t)
|
||||
else any()
|
||||
strengthenType(node, t0, t)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate partialPathTypeStrengthen(
|
||||
DataFlowType t0, PartialAccessPath ap, DataFlowType t
|
||||
) {
|
||||
partialPathStep1(_, _, _, _, _, _, _, _, t0, t, ap) and t0 != t
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4183,7 +4231,8 @@ module Impl<FullStateConfigSig Config> {
|
||||
}
|
||||
}
|
||||
|
||||
private predicate partialPathStep(
|
||||
pragma[nomagic]
|
||||
private predicate partialPathStep0(
|
||||
PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1,
|
||||
TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap
|
||||
) {
|
||||
@@ -4309,6 +4358,11 @@ module Impl<FullStateConfigSig Config> {
|
||||
DataFlowType t1, PartialAccessPath ap1, Content c, DataFlowType t2, PartialAccessPath ap2
|
||||
) {
|
||||
partialPathStoreStep(_, t1, ap1, c, _, t2, ap2)
|
||||
or
|
||||
exists(DataFlowType t0 |
|
||||
partialPathTypeStrengthen(t0, ap2, t2) and
|
||||
apConsFwd(t1, ap1, c, t0, ap2)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
|
||||
@@ -441,14 +441,16 @@ predicate importTimeSummaryFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
// This will miss statements inside functions called from the top level.
|
||||
isTopLevel(nodeFrom) and
|
||||
isTopLevel(nodeTo) and
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo, true)
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
|
||||
nodeTo.(FlowSummaryNode).getSummaryNode(), true)
|
||||
}
|
||||
|
||||
predicate runtimeSummaryFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
// Anything not at the top level can be executed at runtime.
|
||||
not isTopLevel(nodeFrom) and
|
||||
not isTopLevel(nodeTo) and
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo, true)
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
|
||||
nodeTo.(FlowSummaryNode).getSummaryNode(), true)
|
||||
}
|
||||
|
||||
/** `ModuleVariable`s are accessed via jump steps at runtime. */
|
||||
@@ -502,6 +504,8 @@ class CastNode extends Node {
|
||||
pragma[inline]
|
||||
predicate compatibleTypes(DataFlowType t1, DataFlowType t2) { any() }
|
||||
|
||||
predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) { none() }
|
||||
|
||||
/**
|
||||
* Gets the type of `node`.
|
||||
*/
|
||||
@@ -527,7 +531,8 @@ predicate jumpStep(Node nodeFrom, Node nodeTo) {
|
||||
or
|
||||
jumpStepNotSharedWithTypeTracker(nodeFrom, nodeTo)
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryJumpStep(nodeFrom, nodeTo)
|
||||
FlowSummaryImpl::Private::Steps::summaryJumpStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
|
||||
nodeTo.(FlowSummaryNode).getSummaryNode())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -600,7 +605,8 @@ predicate storeStep(Node nodeFrom, Content c, Node nodeTo) {
|
||||
or
|
||||
any(Orm::AdditionalOrmSteps es).storeStep(nodeFrom, c, nodeTo)
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryStoreStep(nodeFrom, c, nodeTo)
|
||||
FlowSummaryImpl::Private::Steps::summaryStoreStep(nodeFrom.(FlowSummaryNode).getSummaryNode(), c,
|
||||
nodeTo.(FlowSummaryNode).getSummaryNode())
|
||||
or
|
||||
synthStarArgsElementParameterNodeStoreStep(nodeFrom, c, nodeTo)
|
||||
or
|
||||
@@ -792,19 +798,16 @@ predicate defaultValueFlowStep(CfgNode nodeFrom, CfgNode nodeTo) {
|
||||
predicate readStep(Node nodeFrom, Content c, Node nodeTo) {
|
||||
subscriptReadStep(nodeFrom, c, nodeTo)
|
||||
or
|
||||
dictReadStep(nodeFrom, c, nodeTo)
|
||||
or
|
||||
iterableUnpackingReadStep(nodeFrom, c, nodeTo)
|
||||
or
|
||||
matchReadStep(nodeFrom, c, nodeTo)
|
||||
or
|
||||
popReadStep(nodeFrom, c, nodeTo)
|
||||
or
|
||||
forReadStep(nodeFrom, c, nodeTo)
|
||||
or
|
||||
attributeReadStep(nodeFrom, c, nodeTo)
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryReadStep(nodeFrom, c, nodeTo)
|
||||
FlowSummaryImpl::Private::Steps::summaryReadStep(nodeFrom.(FlowSummaryNode).getSummaryNode(), c,
|
||||
nodeTo.(FlowSummaryNode).getSummaryNode())
|
||||
or
|
||||
synthDictSplatParameterNodeReadStep(nodeFrom, c, nodeTo)
|
||||
}
|
||||
@@ -832,51 +835,6 @@ predicate subscriptReadStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
|
||||
)
|
||||
}
|
||||
|
||||
predicate dictReadStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
|
||||
// see
|
||||
// - https://docs.python.org/3.10/library/stdtypes.html#dict.get
|
||||
// - https://docs.python.org/3.10/library/stdtypes.html#dict.setdefault
|
||||
exists(MethodCallNode call |
|
||||
call.calls(nodeFrom, ["get", "setdefault"]) and
|
||||
call.getArg(0).asExpr().(StrConst).getText() = c.(DictionaryElementContent).getKey() and
|
||||
nodeTo = call
|
||||
)
|
||||
}
|
||||
|
||||
/** Data flows from a sequence to a call to `pop` on the sequence. */
|
||||
predicate popReadStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
|
||||
// set.pop or list.pop
|
||||
// `s.pop()`
|
||||
// nodeFrom is `s`, cfg node
|
||||
// nodeTo is `s.pop()`, cfg node
|
||||
// c denotes element of list or set
|
||||
exists(CallNode call, AttrNode a |
|
||||
call.getFunction() = a and
|
||||
a.getName() = "pop" and // Should match appropriate call since we tracked a sequence here.
|
||||
not exists(call.getAnArg()) and
|
||||
nodeFrom.getNode() = a.getObject() and
|
||||
nodeTo.getNode() = call and
|
||||
(
|
||||
c instanceof ListElementContent
|
||||
or
|
||||
c instanceof SetElementContent
|
||||
)
|
||||
)
|
||||
or
|
||||
// dict.pop
|
||||
// `d.pop("key")`
|
||||
// nodeFrom is `d`, cfg node
|
||||
// nodeTo is `d.pop("key")`, cfg node
|
||||
// c denotes the key `"key"`
|
||||
exists(CallNode call, AttrNode a |
|
||||
call.getFunction() = a and
|
||||
a.getName() = "pop" and // Should match appropriate call since we tracked a dictionary here.
|
||||
nodeFrom.getNode() = a.getObject() and
|
||||
nodeTo.getNode() = call and
|
||||
c.(DictionaryElementContent).getKey() = call.getArg(0).getNode().(StrConst).getS()
|
||||
)
|
||||
}
|
||||
|
||||
predicate forReadStep(CfgNode nodeFrom, Content c, Node nodeTo) {
|
||||
exists(ForTarget target |
|
||||
nodeFrom.asExpr() = target.getSource() and
|
||||
@@ -919,7 +877,7 @@ predicate clearsContent(Node n, Content c) {
|
||||
or
|
||||
dictClearStep(n, c)
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryClearsContent(n, c)
|
||||
FlowSummaryImpl::Private::Steps::summaryClearsContent(n.(FlowSummaryNode).getSummaryNode(), c)
|
||||
or
|
||||
dictSplatParameterNodeClearStep(n, c)
|
||||
}
|
||||
@@ -976,9 +934,7 @@ predicate forceHighPrecision(Content c) { none() }
|
||||
predicate nodeIsHidden(Node n) {
|
||||
n instanceof ModuleVariableNode
|
||||
or
|
||||
n instanceof SummaryNode
|
||||
or
|
||||
n instanceof SummaryParameterNode
|
||||
n instanceof FlowSummaryNode
|
||||
or
|
||||
n instanceof SynthStarArgsElementParameterNode
|
||||
or
|
||||
@@ -1005,7 +961,7 @@ predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c)
|
||||
|
||||
/** Holds if `call` is a lambda call of kind `kind` where `receiver` is the lambda expression. */
|
||||
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
|
||||
receiver = call.(SummaryCall).getReceiver() and
|
||||
receiver.(FlowSummaryNode).getSummaryNode() = call.(SummaryCall).getReceiver() and
|
||||
exists(kind)
|
||||
}
|
||||
|
||||
|
||||
@@ -105,14 +105,7 @@ newtype TNode =
|
||||
// So for now we live with having these synthetic ORM nodes for _all_ classes, which
|
||||
// is a bit wasteful, but we don't think it will hurt too much.
|
||||
TSyntheticOrmModelNode(Class cls) or
|
||||
TSummaryNode(
|
||||
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNodeState state
|
||||
) {
|
||||
FlowSummaryImpl::Private::summaryNodeRange(c, state)
|
||||
} or
|
||||
TSummaryParameterNode(FlowSummaryImpl::Public::SummarizedCallable c, ParameterPosition pos) {
|
||||
FlowSummaryImpl::Private::summaryParameterNodeRange(c, pos)
|
||||
} or
|
||||
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) or
|
||||
/** A synthetic node to capture positional arguments that are passed to a `*args` parameter. */
|
||||
TSynthStarArgsElementParameterNode(DataFlowCallable callable) {
|
||||
exists(ParameterPosition ppos | ppos.isStarArgs(_) | exists(callable.getParameter(ppos)))
|
||||
|
||||
@@ -180,6 +180,11 @@ module Public {
|
||||
result = "Argument[" + getParameterPosition(pos) + "]"
|
||||
)
|
||||
or
|
||||
exists(string synthetic |
|
||||
sc = TSyntheticGlobalSummaryComponent(synthetic) and
|
||||
result = "SyntheticGlobal[" + synthetic + "]"
|
||||
)
|
||||
or
|
||||
sc = TReturnSummaryComponent(getReturnValueKind()) and result = "ReturnValue"
|
||||
}
|
||||
|
||||
@@ -505,6 +510,9 @@ module Private {
|
||||
or
|
||||
// Add the post-update node corresponding to the requested argument node
|
||||
outputState(c, s) and isCallbackParameter(s)
|
||||
or
|
||||
// Add the parameter node for parameter side-effects
|
||||
outputState(c, s) and s = SummaryComponentStack::argument(_)
|
||||
}
|
||||
|
||||
private newtype TSummaryNodeState =
|
||||
@@ -530,7 +538,7 @@ module Private {
|
||||
* this state represents that the components in `s` _remain to be written_ to
|
||||
* the output.
|
||||
*/
|
||||
class SummaryNodeState extends TSummaryNodeState {
|
||||
private class SummaryNodeState extends TSummaryNodeState {
|
||||
/** Holds if this state is a valid input state for `c`. */
|
||||
pragma[nomagic]
|
||||
predicate isInputState(SummarizedCallable c, SummaryComponentStack s) {
|
||||
@@ -559,6 +567,42 @@ module Private {
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TSummaryNode =
|
||||
TSummaryInternalNode(SummarizedCallable c, SummaryNodeState state) {
|
||||
summaryNodeRange(c, state)
|
||||
} or
|
||||
TSummaryParameterNode(SummarizedCallable c, ParameterPosition pos) {
|
||||
summaryParameterNodeRange(c, pos)
|
||||
}
|
||||
|
||||
abstract class SummaryNode extends TSummaryNode {
|
||||
abstract string toString();
|
||||
|
||||
abstract SummarizedCallable getSummarizedCallable();
|
||||
}
|
||||
|
||||
private class SummaryInternalNode extends SummaryNode, TSummaryInternalNode {
|
||||
private SummarizedCallable c;
|
||||
private SummaryNodeState state;
|
||||
|
||||
SummaryInternalNode() { this = TSummaryInternalNode(c, state) }
|
||||
|
||||
override string toString() { result = "[summary] " + state + " in " + c }
|
||||
|
||||
override SummarizedCallable getSummarizedCallable() { result = c }
|
||||
}
|
||||
|
||||
private class SummaryParamNode extends SummaryNode, TSummaryParameterNode {
|
||||
private SummarizedCallable c;
|
||||
private ParameterPosition pos;
|
||||
|
||||
SummaryParamNode() { this = TSummaryParameterNode(c, pos) }
|
||||
|
||||
override string toString() { result = "[summary param] " + pos + " in " + c }
|
||||
|
||||
override SummarizedCallable getSummarizedCallable() { result = c }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `state` represents having read from a parameter at position
|
||||
* `pos` in `c`. In this case we are not synthesizing a data-flow node,
|
||||
@@ -574,7 +618,7 @@ module Private {
|
||||
* Holds if a synthesized summary node is needed for the state `state` in summarized
|
||||
* callable `c`.
|
||||
*/
|
||||
predicate summaryNodeRange(SummarizedCallable c, SummaryNodeState state) {
|
||||
private predicate summaryNodeRange(SummarizedCallable c, SummaryNodeState state) {
|
||||
state.isInputState(c, _) and
|
||||
not parameterReadState(c, state, _)
|
||||
or
|
||||
@@ -582,22 +626,22 @@ module Private {
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private Node summaryNodeInputState(SummarizedCallable c, SummaryComponentStack s) {
|
||||
private SummaryNode summaryNodeInputState(SummarizedCallable c, SummaryComponentStack s) {
|
||||
exists(SummaryNodeState state | state.isInputState(c, s) |
|
||||
result = summaryNode(c, state)
|
||||
result = TSummaryInternalNode(c, state)
|
||||
or
|
||||
exists(ParameterPosition pos |
|
||||
parameterReadState(c, state, pos) and
|
||||
result.(ParamNode).isParameterOf(inject(c), pos)
|
||||
result = TSummaryParameterNode(c, pos)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private Node summaryNodeOutputState(SummarizedCallable c, SummaryComponentStack s) {
|
||||
private SummaryNode summaryNodeOutputState(SummarizedCallable c, SummaryComponentStack s) {
|
||||
exists(SummaryNodeState state |
|
||||
state.isOutputState(c, s) and
|
||||
result = summaryNode(c, state)
|
||||
result = TSummaryInternalNode(c, state)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -605,12 +649,14 @@ module Private {
|
||||
* Holds if a write targets `post`, which is a post-update node for a
|
||||
* parameter at position `pos` in `c`.
|
||||
*/
|
||||
private predicate isParameterPostUpdate(Node post, SummarizedCallable c, ParameterPosition pos) {
|
||||
private predicate isParameterPostUpdate(
|
||||
SummaryNode post, SummarizedCallable c, ParameterPosition pos
|
||||
) {
|
||||
post = summaryNodeOutputState(c, SummaryComponentStack::argument(pos))
|
||||
}
|
||||
|
||||
/** Holds if a parameter node at position `pos` is required for `c`. */
|
||||
predicate summaryParameterNodeRange(SummarizedCallable c, ParameterPosition pos) {
|
||||
private predicate summaryParameterNodeRange(SummarizedCallable c, ParameterPosition pos) {
|
||||
parameterReadState(c, _, pos)
|
||||
or
|
||||
// Same as `isParameterPostUpdate(_, c, pos)`, but can be used in a negative context
|
||||
@@ -618,7 +664,7 @@ module Private {
|
||||
}
|
||||
|
||||
private predicate callbackOutput(
|
||||
SummarizedCallable c, SummaryComponentStack s, Node receiver, ReturnKind rk
|
||||
SummarizedCallable c, SummaryComponentStack s, SummaryNode receiver, ReturnKind rk
|
||||
) {
|
||||
any(SummaryNodeState state).isInputState(c, s) and
|
||||
s.head() = TReturnSummaryComponent(rk) and
|
||||
@@ -626,7 +672,7 @@ module Private {
|
||||
}
|
||||
|
||||
private predicate callbackInput(
|
||||
SummarizedCallable c, SummaryComponentStack s, Node receiver, ArgumentPosition pos
|
||||
SummarizedCallable c, SummaryComponentStack s, SummaryNode receiver, ArgumentPosition pos
|
||||
) {
|
||||
any(SummaryNodeState state).isOutputState(c, s) and
|
||||
s.head() = TParameterSummaryComponent(pos) and
|
||||
@@ -634,7 +680,7 @@ module Private {
|
||||
}
|
||||
|
||||
/** Holds if a call targeting `receiver` should be synthesized inside `c`. */
|
||||
predicate summaryCallbackRange(SummarizedCallable c, Node receiver) {
|
||||
predicate summaryCallbackRange(SummarizedCallable c, SummaryNode receiver) {
|
||||
callbackOutput(c, _, receiver, _)
|
||||
or
|
||||
callbackInput(c, _, receiver, _)
|
||||
@@ -647,10 +693,10 @@ module Private {
|
||||
* `getContentType()`, `getReturnType()`, `getCallbackParameterType()`, and
|
||||
* `getCallbackReturnType()`.
|
||||
*/
|
||||
DataFlowType summaryNodeType(Node n) {
|
||||
exists(Node pre |
|
||||
DataFlowType summaryNodeType(SummaryNode n) {
|
||||
exists(SummaryNode pre |
|
||||
summaryPostUpdateNode(n, pre) and
|
||||
result = getNodeType(pre)
|
||||
result = summaryNodeType(pre)
|
||||
)
|
||||
or
|
||||
exists(SummarizedCallable c, SummaryComponentStack s, SummaryComponent head | head = s.head() |
|
||||
@@ -662,12 +708,12 @@ module Private {
|
||||
)
|
||||
or
|
||||
head = TWithoutContentSummaryComponent(_) and
|
||||
result = getNodeType(summaryNodeInputState(c, s.tail()))
|
||||
result = summaryNodeType(summaryNodeInputState(c, s.tail()))
|
||||
or
|
||||
exists(ReturnKind rk |
|
||||
head = TReturnSummaryComponent(rk) and
|
||||
result =
|
||||
getCallbackReturnType(getNodeType(summaryNodeInputState(pragma[only_bind_out](c),
|
||||
getCallbackReturnType(summaryNodeType(summaryNodeInputState(pragma[only_bind_out](c),
|
||||
s.tail())), rk)
|
||||
)
|
||||
or
|
||||
@@ -675,6 +721,11 @@ module Private {
|
||||
head = TSyntheticGlobalSummaryComponent(sg) and
|
||||
result = getSyntheticGlobalType(sg)
|
||||
)
|
||||
or
|
||||
exists(ParameterPosition pos |
|
||||
head = TArgumentSummaryComponent(pos) and
|
||||
result = getParameterType(c, pos)
|
||||
)
|
||||
)
|
||||
or
|
||||
n = summaryNodeOutputState(c, s) and
|
||||
@@ -691,7 +742,7 @@ module Private {
|
||||
or
|
||||
exists(ArgumentPosition pos | head = TParameterSummaryComponent(pos) |
|
||||
result =
|
||||
getCallbackParameterType(getNodeType(summaryNodeInputState(pragma[only_bind_out](c),
|
||||
getCallbackParameterType(summaryNodeType(summaryNodeInputState(pragma[only_bind_out](c),
|
||||
s.tail())), pos)
|
||||
)
|
||||
or
|
||||
@@ -703,9 +754,14 @@ module Private {
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if summary node `p` is a parameter with position `pos`. */
|
||||
predicate summaryParameterNode(SummaryNode p, ParameterPosition pos) {
|
||||
p = TSummaryParameterNode(_, pos)
|
||||
}
|
||||
|
||||
/** Holds if summary node `out` contains output of kind `rk` from call `c`. */
|
||||
predicate summaryOutNode(DataFlowCall c, Node out, ReturnKind rk) {
|
||||
exists(SummarizedCallable callable, SummaryComponentStack s, Node receiver |
|
||||
predicate summaryOutNode(DataFlowCall c, SummaryNode out, ReturnKind rk) {
|
||||
exists(SummarizedCallable callable, SummaryComponentStack s, SummaryNode receiver |
|
||||
callbackOutput(callable, s, receiver, rk) and
|
||||
out = summaryNodeInputState(callable, s) and
|
||||
c = summaryDataFlowCall(receiver)
|
||||
@@ -713,8 +769,8 @@ module Private {
|
||||
}
|
||||
|
||||
/** Holds if summary node `arg` is at position `pos` in the call `c`. */
|
||||
predicate summaryArgumentNode(DataFlowCall c, Node arg, ArgumentPosition pos) {
|
||||
exists(SummarizedCallable callable, SummaryComponentStack s, Node receiver |
|
||||
predicate summaryArgumentNode(DataFlowCall c, SummaryNode arg, ArgumentPosition pos) {
|
||||
exists(SummarizedCallable callable, SummaryComponentStack s, SummaryNode receiver |
|
||||
callbackInput(callable, s, receiver, pos) and
|
||||
arg = summaryNodeOutputState(callable, s) and
|
||||
c = summaryDataFlowCall(receiver)
|
||||
@@ -722,10 +778,10 @@ module Private {
|
||||
}
|
||||
|
||||
/** Holds if summary node `post` is a post-update node with pre-update node `pre`. */
|
||||
predicate summaryPostUpdateNode(Node post, Node pre) {
|
||||
predicate summaryPostUpdateNode(SummaryNode post, SummaryNode pre) {
|
||||
exists(SummarizedCallable c, ParameterPosition pos |
|
||||
isParameterPostUpdate(post, c, pos) and
|
||||
pre.(ParamNode).isParameterOf(inject(c), pos)
|
||||
pre = TSummaryParameterNode(c, pos)
|
||||
)
|
||||
or
|
||||
exists(SummarizedCallable callable, SummaryComponentStack s |
|
||||
@@ -736,7 +792,7 @@ module Private {
|
||||
}
|
||||
|
||||
/** Holds if summary node `ret` is a return node of kind `rk`. */
|
||||
predicate summaryReturnNode(Node ret, ReturnKind rk) {
|
||||
predicate summaryReturnNode(SummaryNode ret, ReturnKind rk) {
|
||||
exists(SummaryComponentStack s |
|
||||
ret = summaryNodeOutputState(_, s) and
|
||||
s = TSingletonSummaryComponentStack(TReturnSummaryComponent(rk))
|
||||
@@ -748,7 +804,9 @@ module Private {
|
||||
* node, and back out to `p`.
|
||||
*/
|
||||
predicate summaryAllowParameterReturnInSelf(ParamNode p) {
|
||||
exists(SummarizedCallable c, ParameterPosition ppos | p.isParameterOf(inject(c), ppos) |
|
||||
exists(SummarizedCallable c, ParameterPosition ppos |
|
||||
p.isParameterOf(inject(c), pragma[only_bind_into](ppos))
|
||||
|
|
||||
exists(SummaryComponentStack inputContents, SummaryComponentStack outputContents |
|
||||
summary(c, inputContents, outputContents, _) and
|
||||
inputContents.bottom() = pragma[only_bind_into](TArgumentSummaryComponent(ppos)) and
|
||||
@@ -763,7 +821,7 @@ module Private {
|
||||
* Holds if there is a local step from `pred` to `succ`, which is synthesized
|
||||
* from a flow summary.
|
||||
*/
|
||||
predicate summaryLocalStep(Node pred, Node succ, boolean preservesValue) {
|
||||
predicate summaryLocalStep(SummaryNode pred, SummaryNode succ, boolean preservesValue) {
|
||||
exists(
|
||||
SummarizedCallable c, SummaryComponentStack inputContents,
|
||||
SummaryComponentStack outputContents
|
||||
@@ -789,7 +847,7 @@ module Private {
|
||||
* Holds if there is a read step of content `c` from `pred` to `succ`, which
|
||||
* is synthesized from a flow summary.
|
||||
*/
|
||||
predicate summaryReadStep(Node pred, ContentSet c, Node succ) {
|
||||
predicate summaryReadStep(SummaryNode pred, ContentSet c, SummaryNode succ) {
|
||||
exists(SummarizedCallable sc, SummaryComponentStack s |
|
||||
pred = summaryNodeInputState(sc, s.tail()) and
|
||||
succ = summaryNodeInputState(sc, s) and
|
||||
@@ -801,7 +859,7 @@ module Private {
|
||||
* Holds if there is a store step of content `c` from `pred` to `succ`, which
|
||||
* is synthesized from a flow summary.
|
||||
*/
|
||||
predicate summaryStoreStep(Node pred, ContentSet c, Node succ) {
|
||||
predicate summaryStoreStep(SummaryNode pred, ContentSet c, SummaryNode succ) {
|
||||
exists(SummarizedCallable sc, SummaryComponentStack s |
|
||||
pred = summaryNodeOutputState(sc, s) and
|
||||
succ = summaryNodeOutputState(sc, s.tail()) and
|
||||
@@ -813,7 +871,7 @@ module Private {
|
||||
* Holds if there is a jump step from `pred` to `succ`, which is synthesized
|
||||
* from a flow summary.
|
||||
*/
|
||||
predicate summaryJumpStep(Node pred, Node succ) {
|
||||
predicate summaryJumpStep(SummaryNode pred, SummaryNode succ) {
|
||||
exists(SummaryComponentStack s |
|
||||
s = SummaryComponentStack::singleton(SummaryComponent::syntheticGlobal(_)) and
|
||||
pred = summaryNodeOutputState(_, s) and
|
||||
@@ -840,9 +898,9 @@ module Private {
|
||||
* `a` on line 2 to the post-update node for `a` on that line (via an intermediate
|
||||
* node where field `b` is cleared).
|
||||
*/
|
||||
predicate summaryClearsContent(Node n, ContentSet c) {
|
||||
predicate summaryClearsContent(SummaryNode n, ContentSet c) {
|
||||
exists(SummarizedCallable sc, SummaryNodeState state, SummaryComponentStack stack |
|
||||
n = summaryNode(sc, state) and
|
||||
n = TSummaryInternalNode(sc, state) and
|
||||
state.isInputState(sc, stack) and
|
||||
stack.head() = SummaryComponent::withoutContent(c)
|
||||
)
|
||||
@@ -852,9 +910,9 @@ module Private {
|
||||
* Holds if the value that is being tracked is expected to be stored inside
|
||||
* content `c` at `n`.
|
||||
*/
|
||||
predicate summaryExpectsContent(Node n, ContentSet c) {
|
||||
predicate summaryExpectsContent(SummaryNode n, ContentSet c) {
|
||||
exists(SummarizedCallable sc, SummaryNodeState state, SummaryComponentStack stack |
|
||||
n = summaryNode(sc, state) and
|
||||
n = TSummaryInternalNode(sc, state) and
|
||||
state.isInputState(sc, stack) and
|
||||
stack.head() = SummaryComponent::withContent(c)
|
||||
)
|
||||
@@ -862,17 +920,17 @@ module Private {
|
||||
|
||||
pragma[noinline]
|
||||
private predicate viableParam(
|
||||
DataFlowCall call, SummarizedCallable sc, ParameterPosition ppos, ParamNode p
|
||||
DataFlowCall call, SummarizedCallable sc, ParameterPosition ppos, SummaryParamNode p
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
c = inject(sc) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
p = TSummaryParameterNode(sc, ppos) and
|
||||
c = viableCallable(call)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private ParamNode summaryArgParam0(DataFlowCall call, ArgNode arg, SummarizedCallable sc) {
|
||||
private SummaryParamNode summaryArgParam(DataFlowCall call, ArgNode arg, SummarizedCallable sc) {
|
||||
exists(ParameterPosition ppos |
|
||||
argumentPositionMatch(call, arg, ppos) and
|
||||
viableParam(call, sc, ppos, result)
|
||||
@@ -884,12 +942,12 @@ module Private {
|
||||
* local steps. `clearsOrExpects` records whether any node on the path from `p` to
|
||||
* `n` either clears or expects contents.
|
||||
*/
|
||||
private predicate paramReachesLocal(ParamNode p, Node n, boolean clearsOrExpects) {
|
||||
private predicate paramReachesLocal(SummaryParamNode p, SummaryNode n, boolean clearsOrExpects) {
|
||||
viableParam(_, _, _, p) and
|
||||
n = p and
|
||||
clearsOrExpects = false
|
||||
or
|
||||
exists(Node mid, boolean clearsOrExpectsMid |
|
||||
exists(SummaryNode mid, boolean clearsOrExpectsMid |
|
||||
paramReachesLocal(p, mid, clearsOrExpectsMid) and
|
||||
summaryLocalStep(mid, n, true) and
|
||||
if
|
||||
@@ -909,21 +967,33 @@ module Private {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate prohibitsUseUseFlow(ArgNode arg, SummarizedCallable sc) {
|
||||
exists(ParamNode p, ParameterPosition ppos, Node ret |
|
||||
exists(SummaryParamNode p, ParameterPosition ppos, SummaryNode ret |
|
||||
paramReachesLocal(p, ret, true) and
|
||||
p = summaryArgParam0(_, arg, sc) and
|
||||
p.isParameterOf(_, pragma[only_bind_into](ppos)) and
|
||||
p = summaryArgParam(_, arg, sc) and
|
||||
p = TSummaryParameterNode(_, pragma[only_bind_into](ppos)) and
|
||||
isParameterPostUpdate(ret, _, pragma[only_bind_into](ppos))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate summaryReturnNodeExt(SummaryNode ret, ReturnKindExt rk) {
|
||||
summaryReturnNode(ret, rk.(ValueReturnKind).getKind())
|
||||
or
|
||||
exists(SummaryParamNode p, SummaryNode pre, ParameterPosition pos |
|
||||
paramReachesLocal(p, pre, _) and
|
||||
summaryPostUpdateNode(ret, pre) and
|
||||
p = TSummaryParameterNode(_, pos) and
|
||||
rk.(ParamUpdateReturnKind).getPosition() = pos
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[ret]
|
||||
private ParamNode summaryArgParam(
|
||||
ArgNode arg, ReturnNodeExt ret, OutNodeExt out, SummarizedCallable sc
|
||||
private SummaryParamNode summaryArgParamRetOut(
|
||||
ArgNode arg, SummaryNode ret, OutNodeExt out, SummarizedCallable sc
|
||||
) {
|
||||
exists(DataFlowCall call, ReturnKindExt rk |
|
||||
result = summaryArgParam0(call, arg, sc) and
|
||||
ret.getKind() = pragma[only_bind_into](rk) and
|
||||
result = summaryArgParam(call, arg, sc) and
|
||||
summaryReturnNodeExt(ret, pragma[only_bind_into](rk)) and
|
||||
out = pragma[only_bind_into](rk).getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
@@ -936,9 +1006,9 @@ module Private {
|
||||
* be useful to include in the exposed local data-flow/taint-tracking relations.
|
||||
*/
|
||||
predicate summaryThroughStepValue(ArgNode arg, Node out, SummarizedCallable sc) {
|
||||
exists(ReturnKind rk, ReturnNode ret, DataFlowCall call |
|
||||
summaryLocalStep(summaryArgParam0(call, arg, sc), ret, true) and
|
||||
ret.getKind() = pragma[only_bind_into](rk) and
|
||||
exists(ReturnKind rk, SummaryNode ret, DataFlowCall call |
|
||||
summaryLocalStep(summaryArgParam(call, arg, sc), ret, true) and
|
||||
summaryReturnNode(ret, pragma[only_bind_into](rk)) and
|
||||
out = getAnOutNode(call, pragma[only_bind_into](rk))
|
||||
)
|
||||
}
|
||||
@@ -951,7 +1021,9 @@ module Private {
|
||||
* be useful to include in the exposed local data-flow/taint-tracking relations.
|
||||
*/
|
||||
predicate summaryThroughStepTaint(ArgNode arg, Node out, SummarizedCallable sc) {
|
||||
exists(ReturnNodeExt ret | summaryLocalStep(summaryArgParam(arg, ret, out, sc), ret, false))
|
||||
exists(SummaryNode ret |
|
||||
summaryLocalStep(summaryArgParamRetOut(arg, ret, out, sc), ret, false)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -962,8 +1034,8 @@ module Private {
|
||||
* be useful to include in the exposed local data-flow/taint-tracking relations.
|
||||
*/
|
||||
predicate summaryGetterStep(ArgNode arg, ContentSet c, Node out, SummarizedCallable sc) {
|
||||
exists(Node mid, ReturnNodeExt ret |
|
||||
summaryReadStep(summaryArgParam(arg, ret, out, sc), c, mid) and
|
||||
exists(SummaryNode mid, SummaryNode ret |
|
||||
summaryReadStep(summaryArgParamRetOut(arg, ret, out, sc), c, mid) and
|
||||
summaryLocalStep(mid, ret, _)
|
||||
)
|
||||
}
|
||||
@@ -976,8 +1048,8 @@ module Private {
|
||||
* be useful to include in the exposed local data-flow/taint-tracking relations.
|
||||
*/
|
||||
predicate summarySetterStep(ArgNode arg, ContentSet c, Node out, SummarizedCallable sc) {
|
||||
exists(Node mid, ReturnNodeExt ret |
|
||||
summaryLocalStep(summaryArgParam(arg, ret, out, sc), mid, _) and
|
||||
exists(SummaryNode mid, SummaryNode ret |
|
||||
summaryLocalStep(summaryArgParamRetOut(arg, ret, out, sc), mid, _) and
|
||||
summaryStoreStep(mid, c, ret)
|
||||
)
|
||||
}
|
||||
@@ -1344,11 +1416,11 @@ module Private {
|
||||
}
|
||||
|
||||
private newtype TNodeOrCall =
|
||||
MkNode(Node n) {
|
||||
MkNode(SummaryNode n) {
|
||||
exists(RelevantSummarizedCallable c |
|
||||
n = summaryNode(c, _)
|
||||
n = TSummaryInternalNode(c, _)
|
||||
or
|
||||
n.(ParamNode).isParameterOf(inject(c), _)
|
||||
n = TSummaryParameterNode(c, _)
|
||||
)
|
||||
} or
|
||||
MkCall(DataFlowCall call) {
|
||||
@@ -1357,7 +1429,7 @@ module Private {
|
||||
}
|
||||
|
||||
private class NodeOrCall extends TNodeOrCall {
|
||||
Node asNode() { this = MkNode(result) }
|
||||
SummaryNode asNode() { this = MkNode(result) }
|
||||
|
||||
DataFlowCall asCall() { this = MkCall(result) }
|
||||
|
||||
@@ -1377,9 +1449,11 @@ module Private {
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this.asNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
or
|
||||
this.asCall().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
filepath = "" and
|
||||
startline = 0 and
|
||||
startcolumn = 0 and
|
||||
endline = 0 and
|
||||
endcolumn = 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,15 +47,15 @@ DataFlowCallable inject(SummarizedCallable c) { result.asLibraryCallable() = c }
|
||||
/** Gets the parameter position of the instance parameter. */
|
||||
ArgumentPosition callbackSelfParameterPosition() { none() } // disables implicit summary flow to `this` for callbacks
|
||||
|
||||
/** Gets the synthesized summary data-flow node for the given values. */
|
||||
Node summaryNode(SummarizedCallable c, SummaryNodeState state) { result = TSummaryNode(c, state) }
|
||||
|
||||
/** Gets the synthesized data-flow call for `receiver`. */
|
||||
SummaryCall summaryDataFlowCall(Node receiver) { receiver = result.getReceiver() }
|
||||
SummaryCall summaryDataFlowCall(SummaryNode receiver) { receiver = result.getReceiver() }
|
||||
|
||||
/** Gets the type of content `c`. */
|
||||
DataFlowType getContentType(Content c) { any() }
|
||||
|
||||
/** Gets the type of the parameter at the given position. */
|
||||
DataFlowType getParameterType(SummarizedCallable c, ParameterPosition pos) { any() }
|
||||
|
||||
/** Gets the return type of kind `rk` for callable `c`. */
|
||||
bindingset[c, rk]
|
||||
DataFlowType getReturnType(SummarizedCallable c, ReturnKind rk) { any() }
|
||||
|
||||
@@ -57,7 +57,9 @@ private module Cached {
|
||||
or
|
||||
asyncWithStep(nodeFrom, nodeTo)
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo, false)
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom
|
||||
.(DataFlowPrivate::FlowSummaryNode)
|
||||
.getSummaryNode(), nodeTo.(DataFlowPrivate::FlowSummaryNode).getSummaryNode(), false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,14 +192,9 @@ predicate containerStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
call.getArg(0) = nodeFrom
|
||||
)
|
||||
or
|
||||
// methods
|
||||
// dict methods
|
||||
exists(DataFlow::MethodCallNode call, string methodName | call = nodeTo |
|
||||
methodName in [
|
||||
// general
|
||||
"copy", "pop",
|
||||
// dict
|
||||
"values", "items", "get", "popitem"
|
||||
] and
|
||||
methodName in ["values", "items"] and
|
||||
call.calls(nodeFrom, methodName)
|
||||
)
|
||||
or
|
||||
|
||||
@@ -224,6 +224,50 @@ private module Cached {
|
||||
|
||||
private import Cached
|
||||
|
||||
private predicate step(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) {
|
||||
stepNoCall(nodeFrom, nodeTo, summary)
|
||||
or
|
||||
stepCall(nodeFrom, nodeTo, summary)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate stepProj(TypeTrackingNode nodeFrom, StepSummary summary) {
|
||||
step(nodeFrom, _, summary)
|
||||
}
|
||||
|
||||
bindingset[nodeFrom, t]
|
||||
pragma[inline_late]
|
||||
pragma[noopt]
|
||||
private TypeTracker stepInlineLate(TypeTracker t, TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo) {
|
||||
exists(StepSummary summary |
|
||||
stepProj(nodeFrom, summary) and
|
||||
result = t.append(summary) and
|
||||
step(nodeFrom, nodeTo, summary)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate smallstep(Node nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) {
|
||||
smallstepNoCall(nodeFrom, nodeTo, summary)
|
||||
or
|
||||
smallstepCall(nodeFrom, nodeTo, summary)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate smallstepProj(Node nodeFrom, StepSummary summary) {
|
||||
smallstep(nodeFrom, _, summary)
|
||||
}
|
||||
|
||||
bindingset[nodeFrom, t]
|
||||
pragma[inline_late]
|
||||
pragma[noopt]
|
||||
private TypeTracker smallstepInlineLate(TypeTracker t, Node nodeFrom, Node nodeTo) {
|
||||
exists(StepSummary summary |
|
||||
smallstepProj(nodeFrom, summary) and
|
||||
result = t.append(summary) and
|
||||
smallstep(nodeFrom, nodeTo, summary)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `nodeFrom` is being written to the `content` of the object in `nodeTo`.
|
||||
*
|
||||
@@ -298,21 +342,50 @@ class StepSummary extends TStepSummary {
|
||||
module StepSummary {
|
||||
/**
|
||||
* Gets the summary that corresponds to having taken a forwards
|
||||
* heap and/or inter-procedural step from `nodeFrom` to `nodeTo`.
|
||||
* inter-procedural step from `nodeFrom` to `nodeTo`.
|
||||
*
|
||||
* This predicate is inlined, which enables better join-orders when
|
||||
* the call graph construction and type tracking are mutually recursive.
|
||||
* In such cases, non-linear recursion involving `step` will be limited
|
||||
* to non-linear recursion for the parts of `step` that involve the
|
||||
* call graph.
|
||||
* This predicate should normally not be used; consider using `step`
|
||||
* instead.
|
||||
*/
|
||||
predicate stepCall = Cached::stepCall/3;
|
||||
|
||||
/**
|
||||
* Gets the summary that corresponds to having taken a forwards
|
||||
* intra-procedural step from `nodeFrom` to `nodeTo`.
|
||||
*
|
||||
* This predicate should normally not be used; consider using `step`
|
||||
* instead.
|
||||
*/
|
||||
predicate stepNoCall = Cached::stepNoCall/3;
|
||||
|
||||
/**
|
||||
* Gets the summary that corresponds to having taken a forwards
|
||||
* heap and/or inter-procedural step from `nodeFrom` to `nodeTo`.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate step(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) {
|
||||
stepNoCall(nodeFrom, nodeTo, summary)
|
||||
or
|
||||
stepCall(nodeFrom, nodeTo, summary)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the summary that corresponds to having taken a forwards
|
||||
* inter-procedural step from `nodeFrom` to `nodeTo`.
|
||||
*
|
||||
* This predicate should normally not be used; consider using `step`
|
||||
* instead.
|
||||
*/
|
||||
predicate smallstepNoCall = Cached::smallstepNoCall/3;
|
||||
|
||||
/**
|
||||
* Gets the summary that corresponds to having taken a forwards
|
||||
* intra-procedural step from `nodeFrom` to `nodeTo`.
|
||||
*
|
||||
* This predicate should normally not be used; consider using `step`
|
||||
* instead.
|
||||
*/
|
||||
predicate smallstepCall = Cached::smallstepCall/3;
|
||||
|
||||
/**
|
||||
* Gets the summary that corresponds to having taken a forwards
|
||||
* local, heap and/or inter-procedural step from `nodeFrom` to `nodeTo`.
|
||||
@@ -320,7 +393,6 @@ module StepSummary {
|
||||
* Unlike `StepSummary::step`, this predicate does not compress
|
||||
* type-preserving steps.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate smallstep(Node nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) {
|
||||
smallstepNoCall(nodeFrom, nodeTo, summary)
|
||||
or
|
||||
@@ -431,10 +503,7 @@ class TypeTracker extends TTypeTracker {
|
||||
*/
|
||||
pragma[inline]
|
||||
TypeTracker step(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo) {
|
||||
exists(StepSummary summary |
|
||||
StepSummary::step(nodeFrom, pragma[only_bind_out](nodeTo), pragma[only_bind_into](summary)) and
|
||||
result = this.append(pragma[only_bind_into](summary))
|
||||
)
|
||||
result = stepInlineLate(this, nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -463,10 +532,7 @@ class TypeTracker extends TTypeTracker {
|
||||
*/
|
||||
pragma[inline]
|
||||
TypeTracker smallstep(Node nodeFrom, Node nodeTo) {
|
||||
exists(StepSummary summary |
|
||||
StepSummary::smallstep(nodeFrom, nodeTo, summary) and
|
||||
result = this.append(summary)
|
||||
)
|
||||
result = smallstepInlineLate(this, nodeFrom, nodeTo)
|
||||
or
|
||||
simpleLocalFlowStep(nodeFrom, nodeTo) and
|
||||
result = this
|
||||
@@ -481,6 +547,39 @@ module TypeTracker {
|
||||
TypeTracker end() { result.end() }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate backStepProj(TypeTrackingNode nodeTo, StepSummary summary) {
|
||||
step(_, nodeTo, summary)
|
||||
}
|
||||
|
||||
bindingset[nodeTo, t]
|
||||
pragma[inline_late]
|
||||
pragma[noopt]
|
||||
private TypeBackTracker backStepInlineLate(
|
||||
TypeBackTracker t, TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo
|
||||
) {
|
||||
exists(StepSummary summary |
|
||||
backStepProj(nodeTo, summary) and
|
||||
result = t.prepend(summary) and
|
||||
step(nodeFrom, nodeTo, summary)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate backSmallstepProj(TypeTrackingNode nodeTo, StepSummary summary) {
|
||||
smallstep(_, nodeTo, summary)
|
||||
}
|
||||
|
||||
bindingset[nodeTo, t]
|
||||
pragma[inline_late]
|
||||
pragma[noopt]
|
||||
private TypeBackTracker backSmallstepInlineLate(TypeBackTracker t, Node nodeFrom, Node nodeTo) {
|
||||
exists(StepSummary summary |
|
||||
backSmallstepProj(nodeTo, summary) and
|
||||
result = t.prepend(summary) and
|
||||
smallstep(nodeFrom, nodeTo, summary)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A summary of the steps needed to back-track a use of a value to a given dataflow node.
|
||||
*
|
||||
@@ -564,10 +663,7 @@ class TypeBackTracker extends TTypeBackTracker {
|
||||
*/
|
||||
pragma[inline]
|
||||
TypeBackTracker step(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo) {
|
||||
exists(StepSummary summary |
|
||||
StepSummary::step(pragma[only_bind_out](nodeFrom), nodeTo, pragma[only_bind_into](summary)) and
|
||||
this = result.prepend(pragma[only_bind_into](summary))
|
||||
)
|
||||
this = backStepInlineLate(result, nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -596,10 +692,7 @@ class TypeBackTracker extends TTypeBackTracker {
|
||||
*/
|
||||
pragma[inline]
|
||||
TypeBackTracker smallstep(Node nodeFrom, Node nodeTo) {
|
||||
exists(StepSummary summary |
|
||||
StepSummary::smallstep(nodeFrom, nodeTo, summary) and
|
||||
this = result.prepend(summary)
|
||||
)
|
||||
this = backSmallstepInlineLate(result, nodeFrom, nodeTo)
|
||||
or
|
||||
simpleLocalFlowStep(nodeFrom, nodeTo) and
|
||||
this = result
|
||||
@@ -635,3 +728,169 @@ module TypeBackTracker {
|
||||
*/
|
||||
TypeBackTracker end() { result.end() }
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Provides logic for constructing a call graph in mutual recursion with type tracking.
|
||||
*
|
||||
* When type tracking is used to construct a call graph, we cannot use the join-order
|
||||
* from `stepInlineLate`, because `step` becomes a recursive call, which means that we
|
||||
* will have a conjunct with 3 recursive calls: the call to `step`, the call to `stepProj`,
|
||||
* and the recursive type tracking call itself. The solution is to split the three-way
|
||||
* non-linear recursion into two non-linear predicates: one that first joins with the
|
||||
* projected `stepCall` relation, followed by a predicate that joins with the full
|
||||
* `stepCall` relation (`stepNoCall` not being recursive, can be join-ordered in the
|
||||
* same way as in `stepInlineLate`).
|
||||
*/
|
||||
module CallGraphConstruction {
|
||||
/** The input to call graph construction. */
|
||||
signature module InputSig {
|
||||
/** A state to track during type tracking. */
|
||||
class State;
|
||||
|
||||
/** Holds if type tracking should start at `start` in state `state`. */
|
||||
predicate start(Node start, State state);
|
||||
|
||||
/**
|
||||
* Holds if type tracking should use the step from `nodeFrom` to `nodeTo`,
|
||||
* which _does not_ depend on the call graph.
|
||||
*
|
||||
* Implementing this predicate using `StepSummary::[small]stepNoCall` yields
|
||||
* standard type tracking.
|
||||
*/
|
||||
predicate stepNoCall(Node nodeFrom, Node nodeTo, StepSummary summary);
|
||||
|
||||
/**
|
||||
* Holds if type tracking should use the step from `nodeFrom` to `nodeTo`,
|
||||
* which _does_ depend on the call graph.
|
||||
*
|
||||
* Implementing this predicate using `StepSummary::[small]stepCall` yields
|
||||
* standard type tracking.
|
||||
*/
|
||||
predicate stepCall(Node nodeFrom, Node nodeTo, StepSummary summary);
|
||||
|
||||
/** A projection of an element from the state space. */
|
||||
class StateProj;
|
||||
|
||||
/** Gets the projection of `state`. */
|
||||
StateProj stateProj(State state);
|
||||
|
||||
/** Holds if type tracking should stop at `n` when we are tracking projected state `stateProj`. */
|
||||
predicate filter(Node n, StateProj stateProj);
|
||||
}
|
||||
|
||||
/** Provides the `track` predicate for use in call graph construction. */
|
||||
module Make<InputSig Input> {
|
||||
pragma[nomagic]
|
||||
private predicate stepNoCallProj(Node nodeFrom, StepSummary summary) {
|
||||
Input::stepNoCall(nodeFrom, _, summary)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate stepCallProj(Node nodeFrom, StepSummary summary) {
|
||||
Input::stepCall(nodeFrom, _, summary)
|
||||
}
|
||||
|
||||
bindingset[nodeFrom, t]
|
||||
pragma[inline_late]
|
||||
pragma[noopt]
|
||||
private TypeTracker stepNoCallInlineLate(
|
||||
TypeTracker t, TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo
|
||||
) {
|
||||
exists(StepSummary summary |
|
||||
stepNoCallProj(nodeFrom, summary) and
|
||||
result = t.append(summary) and
|
||||
Input::stepNoCall(nodeFrom, nodeTo, summary)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[state]
|
||||
pragma[inline_late]
|
||||
private Input::StateProj stateProjInlineLate(Input::State state) {
|
||||
result = Input::stateProj(state)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Node track(Input::State state, TypeTracker t) {
|
||||
t.start() and Input::start(result, state)
|
||||
or
|
||||
exists(Input::StateProj stateProj |
|
||||
stateProj = stateProjInlineLate(state) and
|
||||
not Input::filter(result, stateProj)
|
||||
|
|
||||
exists(TypeTracker t2 | t = stepNoCallInlineLate(t2, track(state, t2), result))
|
||||
or
|
||||
exists(StepSummary summary |
|
||||
// non-linear recursion
|
||||
Input::stepCall(trackCall(state, t, summary), result, summary)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[t, summary]
|
||||
pragma[inline_late]
|
||||
private TypeTracker appendInlineLate(TypeTracker t, StepSummary summary) {
|
||||
result = t.append(summary)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Node trackCall(Input::State state, TypeTracker t, StepSummary summary) {
|
||||
exists(TypeTracker t2 |
|
||||
// non-linear recursion
|
||||
result = track(state, t2) and
|
||||
stepCallProj(result, summary) and
|
||||
t = appendInlineLate(t2, summary)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a node that can be reached from _some_ start node in state `state`. */
|
||||
pragma[nomagic]
|
||||
Node track(Input::State state) { result = track(state, TypeTracker::end()) }
|
||||
}
|
||||
|
||||
/** A simple version of `CallGraphConstruction` that uses standard type tracking. */
|
||||
module Simple {
|
||||
/** The input to call graph construction. */
|
||||
signature module InputSig {
|
||||
/** A state to track during type tracking. */
|
||||
class State;
|
||||
|
||||
/** Holds if type tracking should start at `start` in state `state`. */
|
||||
predicate start(Node start, State state);
|
||||
|
||||
/** Holds if type tracking should stop at `n`. */
|
||||
predicate filter(Node n);
|
||||
}
|
||||
|
||||
/** Provides the `track` predicate for use in call graph construction. */
|
||||
module Make<InputSig Input> {
|
||||
private module I implements CallGraphConstruction::InputSig {
|
||||
private import codeql.util.Unit
|
||||
|
||||
class State = Input::State;
|
||||
|
||||
predicate start(Node start, State state) { Input::start(start, state) }
|
||||
|
||||
predicate stepNoCall(Node nodeFrom, Node nodeTo, StepSummary summary) {
|
||||
StepSummary::stepNoCall(nodeFrom, nodeTo, summary)
|
||||
}
|
||||
|
||||
predicate stepCall(Node nodeFrom, Node nodeTo, StepSummary summary) {
|
||||
StepSummary::stepCall(nodeFrom, nodeTo, summary)
|
||||
}
|
||||
|
||||
class StateProj = Unit;
|
||||
|
||||
Unit stateProj(State state) { exists(state) and exists(result) }
|
||||
|
||||
predicate filter(Node n, Unit u) {
|
||||
Input::filter(n) and
|
||||
exists(u)
|
||||
}
|
||||
}
|
||||
|
||||
import CallGraphConstruction::Make<I>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -664,14 +664,6 @@ module DataFlow {
|
||||
}
|
||||
}
|
||||
|
||||
deprecated private class DataFlowType extends TaintKind {
|
||||
// this only exists to avoid an empty recursion error in the type checker
|
||||
DataFlowType() {
|
||||
this = "Data flow" and
|
||||
1 = 2
|
||||
}
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate dict_construct(ControlFlowNode itemnode, ControlFlowNode dictnode) {
|
||||
dictnode.(DictNode).getAValue() = itemnode
|
||||
|
||||
@@ -534,9 +534,6 @@ module PrivateDjango {
|
||||
/** Gets a reference to the `django` module. */
|
||||
API::Node django() { result = API::moduleImport("django") }
|
||||
|
||||
/** DEPRECATED: Alias for `DjangoImpl` */
|
||||
deprecated module django = DjangoImpl;
|
||||
|
||||
/** Provides models for the `django` module. */
|
||||
module DjangoImpl {
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -552,9 +549,6 @@ module PrivateDjango {
|
||||
DjangoDb() { this = API::moduleImport("django").getMember("db") }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for `DB` */
|
||||
deprecated module db = DB;
|
||||
|
||||
/** Provides models for the `django.db` module. */
|
||||
module DB {
|
||||
/** Gets a reference to the `django.db.connection` object. */
|
||||
@@ -571,9 +565,6 @@ module PrivateDjango {
|
||||
/** Gets a reference to the `django.db.models` module. */
|
||||
API::Node models() { result = db().getMember("models") }
|
||||
|
||||
/** DEPRECATED: Alias for `Models` */
|
||||
deprecated module models = Models;
|
||||
|
||||
/** Provides models for the `django.db.models` module. */
|
||||
module Models {
|
||||
/**
|
||||
@@ -819,9 +810,6 @@ module PrivateDjango {
|
||||
/** Gets a reference to the `django.db.models.expressions` module. */
|
||||
API::Node expressions() { result = models().getMember("expressions") }
|
||||
|
||||
/** DEPRECATED: Alias for `Expressions` */
|
||||
deprecated module expressions = Expressions;
|
||||
|
||||
/** Provides models for the `django.db.models.expressions` module. */
|
||||
module Expressions {
|
||||
/** Provides models for the `django.db.models.expressions.RawSql` class. */
|
||||
@@ -858,9 +846,6 @@ module PrivateDjango {
|
||||
instance(DataFlow::TypeTracker::end(), sql).flowsTo(result)
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for RawSql */
|
||||
deprecated module RawSQL = RawSql;
|
||||
}
|
||||
|
||||
/** This internal module provides data-flow modeling of Django ORM. */
|
||||
@@ -1099,9 +1084,6 @@ module PrivateDjango {
|
||||
/** Gets a reference to the `django.urls` module. */
|
||||
API::Node urls() { result = django().getMember("urls") }
|
||||
|
||||
/** DEPRECATED: Alias for `Urls` */
|
||||
deprecated module urls = Urls;
|
||||
|
||||
/** Provides models for the `django.urls` module */
|
||||
module Urls {
|
||||
/**
|
||||
@@ -1123,14 +1105,8 @@ module PrivateDjango {
|
||||
/** Gets a reference to the `django.conf` module. */
|
||||
API::Node conf() { result = django().getMember("conf") }
|
||||
|
||||
/** DEPRECATED: Alias for `Conf` */
|
||||
deprecated module conf = Conf;
|
||||
|
||||
/** Provides models for the `django.conf` module */
|
||||
module Conf {
|
||||
/** DEPRECATED: Alias for `ConfUrls` */
|
||||
deprecated module conf_urls = ConfUrls;
|
||||
|
||||
/** Provides models for the `django.conf.urls` module */
|
||||
module ConfUrls {
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -1166,9 +1142,6 @@ module PrivateDjango {
|
||||
/** Gets a reference to the `django.http.request` module. */
|
||||
API::Node request() { result = http().getMember("request") }
|
||||
|
||||
/** DEPRECATED: Alias for `Request` */
|
||||
deprecated module request = Request;
|
||||
|
||||
/** Provides models for the `django.http.request` module. */
|
||||
module Request {
|
||||
/**
|
||||
@@ -1331,9 +1304,6 @@ module PrivateDjango {
|
||||
/** Gets a reference to the `django.http.response` module. */
|
||||
API::Node response() { result = http().getMember("response") }
|
||||
|
||||
/** DEPRECATED: Alias for `Response` */
|
||||
deprecated module response = Response;
|
||||
|
||||
/** Provides models for the `django.http.response` module */
|
||||
module Response {
|
||||
/**
|
||||
@@ -2189,9 +2159,6 @@ module PrivateDjango {
|
||||
/** Gets a reference to the `django.shortcuts` module. */
|
||||
API::Node shortcuts() { result = django().getMember("shortcuts") }
|
||||
|
||||
/** DEPRECATED: Alias for `Shortcuts` */
|
||||
deprecated module shortcuts = Shortcuts;
|
||||
|
||||
/** Provides models for the `django.shortcuts` module */
|
||||
module Shortcuts {
|
||||
/**
|
||||
|
||||
@@ -37,9 +37,6 @@ private module FastApi {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for ApiRouter */
|
||||
deprecated module APIRouter = ApiRouter;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// routing modeling
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -13,6 +13,7 @@ private import semmle.python.frameworks.Stdlib
|
||||
private import semmle.python.ApiGraphs
|
||||
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
|
||||
private import semmle.python.security.dataflow.PathInjectionCustomizations
|
||||
private import semmle.python.dataflow.new.FlowSummary
|
||||
|
||||
/**
|
||||
* Provides models for the `flask` PyPI package.
|
||||
@@ -587,4 +588,57 @@ module Flask {
|
||||
private class FlaskLogger extends Stdlib::Logger::InstanceSource {
|
||||
FlaskLogger() { this = FlaskApp::instance().getMember("logger").asSource() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow summary for `flask.render_template_string`.
|
||||
*
|
||||
* see https://flask.palletsprojects.com/en/2.3.x/api/#flask.render_template_string
|
||||
*/
|
||||
private class RenderTemplateStringSummary extends SummarizedCallable {
|
||||
RenderTemplateStringSummary() { this = "flask.render_template_string" }
|
||||
|
||||
override DataFlow::CallCfgNode getACall() {
|
||||
result = API::moduleImport("flask").getMember("render_template_string").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::ArgumentNode getACallback() {
|
||||
result =
|
||||
API::moduleImport("flask")
|
||||
.getMember("render_template_string")
|
||||
.getAValueReachableFromSource()
|
||||
}
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
input = "Argument[0]" and
|
||||
output = "ReturnValue" and
|
||||
preservesValue = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow summary for `flask.stream_template_string`.
|
||||
*
|
||||
* see https://flask.palletsprojects.com/en/2.3.x/api/#flask.stream_template_string
|
||||
*/
|
||||
private class StreamTemplateStringSummary extends SummarizedCallable {
|
||||
StreamTemplateStringSummary() { this = "flask.stream_template_string" }
|
||||
|
||||
override DataFlow::CallCfgNode getACall() {
|
||||
result = API::moduleImport("flask").getMember("stream_template_string").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::ArgumentNode getACallback() {
|
||||
result =
|
||||
API::moduleImport("flask")
|
||||
.getMember("stream_template_string")
|
||||
.getAValueReachableFromSource()
|
||||
}
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
input = "Argument[0]" and
|
||||
// Technically it's `Iterator[str]`, but list will do :)
|
||||
output = "ReturnValue.ListElement" and
|
||||
preservesValue = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,7 +359,4 @@ private module RestFramework {
|
||||
override string getMimetypeDefault() { none() }
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for ApiException */
|
||||
deprecated module APIException = ApiException;
|
||||
}
|
||||
|
||||
@@ -169,9 +169,6 @@ module SqlAlchemy {
|
||||
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for DBApiConnection */
|
||||
deprecated module DBAPIConnection = DBApiConnection;
|
||||
|
||||
/**
|
||||
* Provides models for the `sqlalchemy.orm.Session` class
|
||||
*
|
||||
|
||||
@@ -130,9 +130,6 @@ module Stdlib {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for HttpMessage */
|
||||
deprecated module HTTPMessage = HttpMessage;
|
||||
|
||||
/**
|
||||
* Provides models for the `http.cookies.Morsel` class
|
||||
*
|
||||
@@ -1821,9 +1818,6 @@ private module StdlibPrivate {
|
||||
/** Gets a reference to the `BaseHttpServer` module. */
|
||||
API::Node baseHttpServer() { result = API::moduleImport("BaseHTTPServer") }
|
||||
|
||||
/** DEPRECATED: Alias for baseHttpServer */
|
||||
deprecated API::Node baseHTTPServer() { result = baseHttpServer() }
|
||||
|
||||
/** Provides models for the `BaseHttpServer` module. */
|
||||
module BaseHttpServer {
|
||||
/**
|
||||
@@ -1833,23 +1827,14 @@ private module StdlibPrivate {
|
||||
/** Gets a reference to the `BaseHttpServer.BaseHttpRequestHandler` class. */
|
||||
API::Node classRef() { result = baseHttpServer().getMember("BaseHTTPRequestHandler") }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for BaseHttpRequestHandler */
|
||||
deprecated module BaseHTTPRequestHandler = BaseHttpRequestHandler;
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for BaseHttpServer */
|
||||
deprecated module BaseHTTPServer = BaseHttpServer;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// SimpleHTTPServer (Python 2 only)
|
||||
// ---------------------------------------------------------------------------
|
||||
/** Gets a reference to the `SimpleHttpServer` module. */
|
||||
API::Node simpleHttpServer() { result = API::moduleImport("SimpleHTTPServer") }
|
||||
|
||||
/** DEPRECATED: Alias for simpleHttpServer */
|
||||
deprecated API::Node simpleHTTPServer() { result = simpleHttpServer() }
|
||||
|
||||
/** Provides models for the `SimpleHttpServer` module. */
|
||||
module SimpleHttpServer {
|
||||
/**
|
||||
@@ -1859,23 +1844,14 @@ private module StdlibPrivate {
|
||||
/** Gets a reference to the `SimpleHttpServer.SimpleHttpRequestHandler` class. */
|
||||
API::Node classRef() { result = simpleHttpServer().getMember("SimpleHTTPRequestHandler") }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for SimpleHttpRequestHandler */
|
||||
deprecated module SimpleHTTPRequestHandler = SimpleHttpRequestHandler;
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for SimpleHttpServer */
|
||||
deprecated module SimpleHTTPServer = SimpleHttpServer;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// CGIHTTPServer (Python 2 only)
|
||||
// ---------------------------------------------------------------------------
|
||||
/** Gets a reference to the `CGIHTTPServer` module. */
|
||||
API::Node cgiHttpServer() { result = API::moduleImport("CGIHTTPServer") }
|
||||
|
||||
/** DEPRECATED: Alias for cgiHttpServer */
|
||||
deprecated API::Node cgiHTTPServer() { result = cgiHttpServer() }
|
||||
|
||||
/** Provides models for the `CGIHTTPServer` module. */
|
||||
module CgiHttpServer {
|
||||
/**
|
||||
@@ -1919,9 +1895,6 @@ private module StdlibPrivate {
|
||||
API::Node classRef() { result = server().getMember("BaseHTTPRequestHandler") }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for BaseHttpRequestHandler */
|
||||
deprecated module BaseHTTPRequestHandler = BaseHttpRequestHandler;
|
||||
|
||||
/**
|
||||
* Provides models for the `http.server.SimpleHTTPRequestHandler` class (Python 3 only).
|
||||
*
|
||||
@@ -1932,9 +1905,6 @@ private module StdlibPrivate {
|
||||
API::Node classRef() { result = server().getMember("SimpleHTTPRequestHandler") }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for SimpleHttpRequestHandler */
|
||||
deprecated module SimpleHTTPRequestHandler = SimpleHttpRequestHandler;
|
||||
|
||||
/**
|
||||
* Provides models for the `http.server.CGIHTTPRequestHandler` class (Python 3 only).
|
||||
*
|
||||
@@ -1978,9 +1948,6 @@ private module StdlibPrivate {
|
||||
HttpRequestHandlerClassDef() { this.getParent() = subclassRef().asSource().asExpr() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for HttpRequestHandlerClassDef */
|
||||
deprecated class HTTPRequestHandlerClassDef = HttpRequestHandlerClassDef;
|
||||
|
||||
/**
|
||||
* A source of instances of the `BaseHTTPRequestHandler` class or any subclass, extend this class to model new instances.
|
||||
*
|
||||
@@ -2352,9 +2319,6 @@ private module StdlibPrivate {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for HttpConnection */
|
||||
deprecated module HTTPConnection = HttpConnection;
|
||||
|
||||
/**
|
||||
* Provides models for the `http.client.HTTPResponse` class
|
||||
*
|
||||
@@ -2424,9 +2388,6 @@ private module StdlibPrivate {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for HttpResponse */
|
||||
deprecated module HTTPResponse = HttpResponse;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// sqlite3
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -3939,6 +3900,176 @@ private module StdlibPrivate {
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Flow summaries for container methods
|
||||
// ---------------------------------------------------------------------------
|
||||
/** A flow summary for `copy`. */
|
||||
class CopySummary extends SummarizedCallable {
|
||||
CopySummary() { this = "collection.copy" }
|
||||
|
||||
override DataFlow::CallCfgNode getACall() {
|
||||
result.(DataFlow::MethodCallNode).getMethodName() = "copy"
|
||||
}
|
||||
|
||||
override DataFlow::ArgumentNode getACallback() { none() }
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
exists(string content |
|
||||
content = "ListElement"
|
||||
or
|
||||
content = "SetElement"
|
||||
or
|
||||
exists(DataFlow::TupleElementContent tc, int i | i = tc.getIndex() |
|
||||
content = "TupleElement[" + i.toString() + "]"
|
||||
)
|
||||
or
|
||||
exists(DataFlow::DictionaryElementContent dc, string key | key = dc.getKey() |
|
||||
content = "DictionaryElement[" + key + "]"
|
||||
)
|
||||
|
|
||||
input = "Argument[self]." + content and
|
||||
output = "ReturnValue." + content and
|
||||
preservesValue = true
|
||||
)
|
||||
or
|
||||
input = "Argument[self]" and
|
||||
output = "ReturnValue" and
|
||||
preservesValue = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow summary for `pop` either for list or set.
|
||||
* This ignores the index if given, since content is
|
||||
* imprecise anyway.
|
||||
*
|
||||
* I also handles the default value when `pop` is called
|
||||
* on a dictionary, since that also does not depend on the key.
|
||||
*/
|
||||
class PopSummary extends SummarizedCallable {
|
||||
PopSummary() { this = "collection.pop" }
|
||||
|
||||
override DataFlow::CallCfgNode getACall() {
|
||||
result.(DataFlow::MethodCallNode).getMethodName() = "pop"
|
||||
}
|
||||
|
||||
override DataFlow::ArgumentNode getACallback() { none() }
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
input = "Argument[self].ListElement" and
|
||||
output = "ReturnValue" and
|
||||
preservesValue = true
|
||||
or
|
||||
input = "Argument[self].SetElement" and
|
||||
output = "ReturnValue" and
|
||||
preservesValue = true
|
||||
or
|
||||
// default value for dictionary
|
||||
input = "Argument[1]" and
|
||||
output = "ReturnValue" and
|
||||
preservesValue = true
|
||||
or
|
||||
// transfer taint on self to return value
|
||||
input = "Argument[self]" and
|
||||
output = "ReturnValue" and
|
||||
preservesValue = false
|
||||
}
|
||||
}
|
||||
|
||||
/** A flow summary for `dict.pop` */
|
||||
class DictPopSummary extends SummarizedCallable {
|
||||
string key;
|
||||
|
||||
DictPopSummary() {
|
||||
this = "dict.pop(" + key + ")" and
|
||||
exists(DataFlow::DictionaryElementContent dc | key = dc.getKey())
|
||||
}
|
||||
|
||||
override DataFlow::CallCfgNode getACall() {
|
||||
result.(DataFlow::MethodCallNode).getMethodName() = "pop" and
|
||||
result.getArg(0).getALocalSource().asExpr().(StrConst).getText() = key
|
||||
}
|
||||
|
||||
override DataFlow::ArgumentNode getACallback() { none() }
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
input = "Argument[self].DictionaryElement[" + key + "]" and
|
||||
output = "ReturnValue" and
|
||||
preservesValue = true
|
||||
}
|
||||
}
|
||||
|
||||
/** A flow summary for `dict.get` at specific content. */
|
||||
class DictGetSummary extends SummarizedCallable {
|
||||
string key;
|
||||
|
||||
DictGetSummary() {
|
||||
this = "dict.get(" + key + ")" and
|
||||
exists(DataFlow::DictionaryElementContent dc | key = dc.getKey())
|
||||
}
|
||||
|
||||
override DataFlow::CallCfgNode getACall() {
|
||||
result.(DataFlow::MethodCallNode).getMethodName() = "get" and
|
||||
result.getArg(0).getALocalSource().asExpr().(StrConst).getText() = key
|
||||
}
|
||||
|
||||
override DataFlow::ArgumentNode getACallback() { none() }
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
input = "Argument[self].DictionaryElement[" + key + "]" and
|
||||
output = "ReturnValue" and
|
||||
preservesValue = true
|
||||
or
|
||||
// optional default value
|
||||
input = "Argument[1]" and
|
||||
output = "ReturnValue" and
|
||||
preservesValue = true
|
||||
}
|
||||
}
|
||||
|
||||
/** A flow summary for `dict.get` disregarding content. */
|
||||
class DictGetAnySummary extends SummarizedCallable {
|
||||
DictGetAnySummary() { this = "dict.get" }
|
||||
|
||||
override DataFlow::CallCfgNode getACall() {
|
||||
result.(DataFlow::MethodCallNode).getMethodName() = "get"
|
||||
}
|
||||
|
||||
override DataFlow::ArgumentNode getACallback() { none() }
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
// default value
|
||||
input = "Argument[1]" and
|
||||
output = "ReturnValue" and
|
||||
preservesValue = true
|
||||
or
|
||||
// transfer taint from self to return value
|
||||
input = "Argument[self]" and
|
||||
output = "ReturnValue" and
|
||||
preservesValue = false
|
||||
}
|
||||
}
|
||||
|
||||
/** A flow summary for `dict.popitem` */
|
||||
class DictPopitemSummary extends SummarizedCallable {
|
||||
DictPopitemSummary() { this = "dict.popitem" }
|
||||
|
||||
override DataFlow::CallCfgNode getACall() {
|
||||
result.(DataFlow::MethodCallNode).getMethodName() = "popitem"
|
||||
}
|
||||
|
||||
override DataFlow::ArgumentNode getACallback() { none() }
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
exists(DataFlow::DictionaryElementContent dc, string key | key = dc.getKey() |
|
||||
input = "Argument[self].DictionaryElement[" + key + "]" and
|
||||
output = "ReturnValue.TupleElement[1]" and
|
||||
preservesValue = true
|
||||
// TODO: put `key` into "ReturnValue.TupleElement[0]"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow summary for `dict.setdefault`.
|
||||
*
|
||||
@@ -3962,6 +4093,40 @@ private module StdlibPrivate {
|
||||
preservesValue = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow summary for `dict.setdefault` at specific content.
|
||||
* See https://docs.python.org/3.10/library/stdtypes.html#dict.setdefault
|
||||
* This summary handles read and store steps. See `DictSetdefaultSummary`
|
||||
* for the dataflow steps.
|
||||
*/
|
||||
class DictSetdefaultKeySummary extends SummarizedCallable {
|
||||
string key;
|
||||
|
||||
DictSetdefaultKeySummary() {
|
||||
this = "dict.setdefault(" + key + ")" and
|
||||
exists(DataFlow::DictionaryElementContent dc | key = dc.getKey())
|
||||
}
|
||||
|
||||
override DataFlow::CallCfgNode getACall() {
|
||||
result.(DataFlow::MethodCallNode).getMethodName() = "setdefault" and
|
||||
result.getArg(0).getALocalSource().asExpr().(StrConst).getText() = key
|
||||
}
|
||||
|
||||
override DataFlow::ArgumentNode getACallback() { none() }
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
// If key is in the dictionary, return its value.
|
||||
input = "Argument[self].DictionaryElement[" + key + "]" and
|
||||
output = "ReturnValue" and
|
||||
preservesValue = true
|
||||
or
|
||||
// If not, insert key with a value of default.
|
||||
input = "Argument[1]" and
|
||||
output = "ReturnValue.DictionaryElement[" + key + "]" and
|
||||
preservesValue = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -64,9 +64,6 @@ module Tornado {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for HttpHeaders */
|
||||
deprecated module HTTPHeaders = HttpHeaders;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// tornado
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -1445,14 +1445,6 @@ module Expressions {
|
||||
)
|
||||
}
|
||||
|
||||
deprecated predicate subscriptPointsTo(
|
||||
SubscriptNode subscr, PointsToContext context, ObjectInternal value, ControlFlowNode origin,
|
||||
ControlFlowNode obj, ObjectInternal objvalue
|
||||
) {
|
||||
subscriptPointsTo(subscr, context, value, obj, objvalue) and
|
||||
origin = subscr
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate subscriptPointsTo(
|
||||
SubscriptNode subscr, PointsToContext context, ObjectInternal value, ControlFlowNode obj,
|
||||
@@ -1489,14 +1481,6 @@ module Expressions {
|
||||
index = subscr.getIndex()
|
||||
}
|
||||
|
||||
deprecated predicate binaryPointsTo(
|
||||
BinaryExprNode b, PointsToContext context, ObjectInternal value, ControlFlowNode origin,
|
||||
ControlFlowNode operand, ObjectInternal opvalue
|
||||
) {
|
||||
binaryPointsTo(b, context, value, operand, opvalue) and
|
||||
origin = b
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracking too many binary expressions is likely to kill performance, so just say anything other than addition or bitwise or is 'unknown'.
|
||||
*/
|
||||
@@ -1521,14 +1505,6 @@ module Expressions {
|
||||
)
|
||||
}
|
||||
|
||||
deprecated predicate addPointsTo(
|
||||
BinaryExprNode b, PointsToContext context, ObjectInternal value, ControlFlowNode origin,
|
||||
ControlFlowNode operand, ObjectInternal opvalue
|
||||
) {
|
||||
addPointsTo(b, context, value, operand, opvalue) and
|
||||
origin = b
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate addPointsTo(
|
||||
BinaryExprNode b, PointsToContext context, ObjectInternal value, ControlFlowNode operand,
|
||||
@@ -1545,14 +1521,6 @@ module Expressions {
|
||||
)
|
||||
}
|
||||
|
||||
deprecated predicate bitOrPointsTo(
|
||||
BinaryExprNode b, PointsToContext context, ObjectInternal value, ControlFlowNode origin,
|
||||
ControlFlowNode operand, ObjectInternal opvalue
|
||||
) {
|
||||
bitOrPointsTo(b, context, value, operand, opvalue) and
|
||||
origin = b
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate bitOrPointsTo(
|
||||
BinaryExprNode b, PointsToContext context, ObjectInternal value, ControlFlowNode operand,
|
||||
@@ -1577,14 +1545,6 @@ module Expressions {
|
||||
value = obj.intValue()
|
||||
}
|
||||
|
||||
deprecated predicate unaryPointsTo(
|
||||
UnaryExprNode u, PointsToContext context, ObjectInternal value, ControlFlowNode origin,
|
||||
ControlFlowNode operand, ObjectInternal opvalue
|
||||
) {
|
||||
unaryPointsTo(u, context, value, operand, opvalue) and
|
||||
origin = u
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate unaryPointsTo(
|
||||
UnaryExprNode u, PointsToContext context, ObjectInternal value, ControlFlowNode operand,
|
||||
@@ -1603,14 +1563,6 @@ module Expressions {
|
||||
)
|
||||
}
|
||||
|
||||
deprecated predicate builtinCallPointsTo(
|
||||
CallNode call, PointsToContext context, ObjectInternal value, ControlFlowNode origin,
|
||||
ControlFlowNode arg, ObjectInternal argvalue
|
||||
) {
|
||||
builtinCallPointsTo(call, context, value, arg, argvalue) and
|
||||
origin = call
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate builtinCallPointsTo(
|
||||
CallNode call, PointsToContext context, ObjectInternal value, ControlFlowNode arg,
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
import python
|
||||
import semmle.python.dataflow.TaintTracking
|
||||
import semmle.python.security.SensitiveData
|
||||
import semmle.python.dataflow.Files
|
||||
import semmle.python.web.Http
|
||||
|
||||
deprecated module ClearTextStorage {
|
||||
abstract class Sink extends TaintSink {
|
||||
override predicate sinks(TaintKind kind) { kind instanceof SensitiveData }
|
||||
}
|
||||
|
||||
class CookieStorageSink extends Sink {
|
||||
CookieStorageSink() { any(CookieSet cookie).getValue() = this }
|
||||
}
|
||||
|
||||
class FileStorageSink extends Sink {
|
||||
FileStorageSink() {
|
||||
exists(CallNode call, AttrNode meth, string name |
|
||||
any(OpenFile fd).taints(meth.getObject(name)) and
|
||||
call.getFunction() = meth and
|
||||
call.getAnArg() = this
|
||||
|
|
||||
name = "write"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deprecated module ClearTextLogging {
|
||||
abstract class Sink extends TaintSink {
|
||||
override predicate sinks(TaintKind kind) { kind instanceof SensitiveData }
|
||||
}
|
||||
|
||||
class PrintSink extends Sink {
|
||||
PrintSink() {
|
||||
exists(CallNode call |
|
||||
call.getAnArg() = this and
|
||||
call = Value::named("print").getACall()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class LoggingSink extends Sink {
|
||||
LoggingSink() {
|
||||
exists(CallNode call, AttrNode meth, string name |
|
||||
call.getFunction() = meth and
|
||||
meth.getObject(name).(NameNode).getId().matches("logg%") and
|
||||
call.getAnArg() = this
|
||||
|
|
||||
name = ["error", "warn", "warning", "debug", "info"]
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
import python
|
||||
import semmle.python.dataflow.TaintTracking
|
||||
private import semmle.python.security.SensitiveData
|
||||
private import semmle.crypto.Crypto as CryptoLib
|
||||
|
||||
abstract deprecated class WeakCryptoSink extends TaintSink {
|
||||
override predicate sinks(TaintKind taint) { taint instanceof SensitiveData }
|
||||
}
|
||||
|
||||
/** Modeling the 'pycrypto' package https://github.com/dlitz/pycrypto (latest release 2013) */
|
||||
deprecated module Pycrypto {
|
||||
ModuleValue cipher(string name) { result = Module::named("Crypto.Cipher").attr(name) }
|
||||
|
||||
class CipherInstance extends TaintKind {
|
||||
string name;
|
||||
|
||||
CipherInstance() {
|
||||
this = "Crypto.Cipher." + name and
|
||||
exists(cipher(name))
|
||||
}
|
||||
|
||||
string getName() { result = name }
|
||||
|
||||
CryptoLib::CryptographicAlgorithm getAlgorithm() { result.getName() = name }
|
||||
|
||||
predicate isWeak() { this.getAlgorithm().isWeak() }
|
||||
}
|
||||
|
||||
class CipherInstanceSource extends TaintSource {
|
||||
CipherInstance instance;
|
||||
|
||||
CipherInstanceSource() {
|
||||
exists(AttrNode attr |
|
||||
this.(CallNode).getFunction() = attr and
|
||||
attr.getObject("new").pointsTo(cipher(instance.getName()))
|
||||
)
|
||||
}
|
||||
|
||||
override string toString() { result = "Source of " + instance }
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind = instance }
|
||||
}
|
||||
|
||||
class PycryptoWeakCryptoSink extends WeakCryptoSink {
|
||||
string name;
|
||||
|
||||
PycryptoWeakCryptoSink() {
|
||||
exists(CallNode call, AttrNode method, CipherInstance cipher |
|
||||
call.getAnArg() = this and
|
||||
call.getFunction() = method and
|
||||
cipher.taints(method.getObject("encrypt")) and
|
||||
cipher.isWeak() and
|
||||
cipher.getName() = name
|
||||
)
|
||||
}
|
||||
|
||||
override string toString() { result = "Use of weak crypto algorithm " + name }
|
||||
}
|
||||
}
|
||||
|
||||
deprecated module Cryptography {
|
||||
ModuleValue ciphers() {
|
||||
result = Module::named("cryptography.hazmat.primitives.ciphers") and
|
||||
result.isPackage()
|
||||
}
|
||||
|
||||
class CipherClass extends ClassValue {
|
||||
CipherClass() { ciphers().attr("Cipher") = this }
|
||||
}
|
||||
|
||||
class AlgorithmClass extends ClassValue {
|
||||
AlgorithmClass() { ciphers().attr("algorithms").attr(_) = this }
|
||||
|
||||
string getAlgorithmName() { result = this.declaredAttribute("name").(StringValue).getText() }
|
||||
|
||||
predicate isWeak() {
|
||||
exists(CryptoLib::CryptographicAlgorithm algo |
|
||||
algo.getName() = this.getAlgorithmName() and
|
||||
algo.isWeak()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class CipherInstance extends TaintKind {
|
||||
AlgorithmClass cls;
|
||||
|
||||
CipherInstance() { this = "cryptography.Cipher." + cls.getAlgorithmName() }
|
||||
|
||||
AlgorithmClass getAlgorithm() { result = cls }
|
||||
|
||||
predicate isWeak() { cls.isWeak() }
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
name = "encryptor" and
|
||||
result.(Encryptor).getAlgorithm() = this.getAlgorithm()
|
||||
}
|
||||
}
|
||||
|
||||
class CipherSource extends TaintSource {
|
||||
CipherSource() { this.(CallNode).getFunction().pointsTo(any(CipherClass cls)) }
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) {
|
||||
this.(CallNode).getArg(0).pointsTo().getClass() = kind.(CipherInstance).getAlgorithm()
|
||||
}
|
||||
|
||||
override string toString() { result = "cryptography.Cipher.source" }
|
||||
}
|
||||
|
||||
class Encryptor extends TaintKind {
|
||||
AlgorithmClass cls;
|
||||
|
||||
Encryptor() { this = "cryptography.encryptor." + cls.getAlgorithmName() }
|
||||
|
||||
AlgorithmClass getAlgorithm() { result = cls }
|
||||
}
|
||||
|
||||
class CryptographyWeakCryptoSink extends WeakCryptoSink {
|
||||
CryptographyWeakCryptoSink() {
|
||||
exists(CallNode call, AttrNode method, Encryptor encryptor |
|
||||
call.getAnArg() = this and
|
||||
call.getFunction() = method and
|
||||
encryptor.taints(method.getObject("update")) and
|
||||
encryptor.getAlgorithm().isWeak()
|
||||
)
|
||||
}
|
||||
|
||||
override string toString() { result = "Use of weak crypto algorithm" }
|
||||
}
|
||||
}
|
||||
|
||||
deprecated private class CipherConfig extends TaintTracking::Configuration {
|
||||
CipherConfig() { this = "Crypto cipher config" }
|
||||
|
||||
override predicate isSource(TaintTracking::Source source) {
|
||||
source instanceof Pycrypto::CipherInstanceSource
|
||||
or
|
||||
source instanceof Cryptography::CipherSource
|
||||
}
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
/**
|
||||
* Provides classes and predicates for identifying sensitive data and methods for security.
|
||||
*
|
||||
* 'Sensitive' data in general is anything that should not be sent around in unencrypted form. This
|
||||
* library tries to guess where sensitive data may either be stored in a variable or produced by a
|
||||
* method.
|
||||
*
|
||||
* In addition, there are methods that ought not to be executed or not in a fashion that the user
|
||||
* can control. This includes authorization methods such as logins, and sending of data, etc.
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.dataflow.TaintTracking
|
||||
import semmle.python.web.HttpRequest
|
||||
import semmle.python.security.internal.SensitiveDataHeuristics
|
||||
private import HeuristicNames
|
||||
|
||||
abstract deprecated class SensitiveData extends TaintKind {
|
||||
bindingset[this]
|
||||
SensitiveData() { this = this }
|
||||
|
||||
/** Gets the classification of this sensitive data taint kind. */
|
||||
abstract SensitiveDataClassification getClassification();
|
||||
}
|
||||
|
||||
deprecated module SensitiveData {
|
||||
class Secret extends SensitiveData {
|
||||
Secret() { this = "sensitive.data.secret" }
|
||||
|
||||
override string repr() { result = "a secret" }
|
||||
|
||||
override SensitiveDataClassification getClassification() {
|
||||
result = SensitiveDataClassification::secret()
|
||||
}
|
||||
}
|
||||
|
||||
class Id extends SensitiveData {
|
||||
Id() { this = "sensitive.data.id" }
|
||||
|
||||
override string repr() { result = "an ID" }
|
||||
|
||||
override SensitiveDataClassification getClassification() {
|
||||
result = SensitiveDataClassification::id()
|
||||
}
|
||||
}
|
||||
|
||||
class Password extends SensitiveData {
|
||||
Password() { this = "sensitive.data.password" }
|
||||
|
||||
override string repr() { result = "a password" }
|
||||
|
||||
override SensitiveDataClassification getClassification() {
|
||||
result = SensitiveDataClassification::password()
|
||||
}
|
||||
}
|
||||
|
||||
class Certificate extends SensitiveData {
|
||||
Certificate() { this = "sensitive.data.certificate" }
|
||||
|
||||
override string repr() { result = "a certificate or key" }
|
||||
|
||||
override SensitiveDataClassification getClassification() {
|
||||
result = SensitiveDataClassification::certificate()
|
||||
}
|
||||
}
|
||||
|
||||
private SensitiveData fromFunction(Value func) {
|
||||
nameIndicatesSensitiveData(func.getName(), result.getClassification())
|
||||
}
|
||||
|
||||
abstract class Source extends TaintSource {
|
||||
abstract string repr();
|
||||
}
|
||||
|
||||
private class SensitiveCallSource extends Source {
|
||||
SensitiveData data;
|
||||
|
||||
SensitiveCallSource() {
|
||||
exists(Value callee | callee.getACall() = this | data = fromFunction(callee))
|
||||
}
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind = data }
|
||||
|
||||
override string repr() { result = "a call returning " + data.repr() }
|
||||
}
|
||||
|
||||
/** An access to a variable or property that might contain sensitive data. */
|
||||
private class SensitiveVariableAccess extends SensitiveData::Source {
|
||||
SensitiveData data;
|
||||
|
||||
SensitiveVariableAccess() {
|
||||
nameIndicatesSensitiveData(this.(AttrNode).getName(), data.getClassification())
|
||||
}
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind = data }
|
||||
|
||||
override string repr() { result = "an attribute or property containing " + data.repr() }
|
||||
}
|
||||
|
||||
private class SensitiveRequestParameter extends SensitiveData::Source {
|
||||
SensitiveData data;
|
||||
|
||||
SensitiveRequestParameter() {
|
||||
this.(CallNode).getFunction().(AttrNode).getName() = "get" and
|
||||
exists(StringValue sensitive |
|
||||
this.(CallNode).getAnArg().pointsTo(sensitive) and
|
||||
nameIndicatesSensitiveData(sensitive.getText(), data.getClassification())
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind = data }
|
||||
|
||||
override string repr() { result = "a request parameter containing " + data.repr() }
|
||||
}
|
||||
}
|
||||
|
||||
//Backwards compatibility
|
||||
deprecated class SensitiveDataSource = SensitiveData::Source;
|
||||
@@ -1,95 +0,0 @@
|
||||
/**
|
||||
* DEPRECATED -- use flow state instead
|
||||
*
|
||||
* This defines a `PathGraph` where sinks from `TaintTracking::Configuration`s are identified with
|
||||
* sources from `TaintTracking2::Configuration`s if they represent the same `ControlFlowNode`.
|
||||
*
|
||||
* Paths are then connected appropriately.
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.dataflow.new.DataFlow2
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
import semmle.python.dataflow.new.TaintTracking2
|
||||
|
||||
/**
|
||||
* A `DataFlow::Node` that appears as a sink in Config1 and a source in Config2.
|
||||
*/
|
||||
private predicate crossoverNode(DataFlow::Node n) {
|
||||
any(TaintTracking::Configuration t1).isSink(n) and
|
||||
any(TaintTracking2::Configuration t2).isSource(n)
|
||||
}
|
||||
|
||||
/**
|
||||
* A new type which represents the union of the two sets of nodes.
|
||||
*/
|
||||
private newtype TCustomPathNode =
|
||||
Config1Node(DataFlow::PathNode node1) { not crossoverNode(node1.getNode()) } or
|
||||
Config2Node(DataFlow2::PathNode node2) { not crossoverNode(node2.getNode()) } or
|
||||
CrossoverNode(DataFlow::Node node) { crossoverNode(node) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use flow state instead
|
||||
*
|
||||
* A class representing the set of all the path nodes in either config.
|
||||
*/
|
||||
deprecated class CustomPathNode extends TCustomPathNode {
|
||||
/** Gets the PathNode if it is in Config1. */
|
||||
DataFlow::PathNode asNode1() {
|
||||
this = Config1Node(result) or this = CrossoverNode(result.getNode())
|
||||
}
|
||||
|
||||
/** Gets the PathNode if it is in Config2. */
|
||||
DataFlow2::PathNode asNode2() {
|
||||
this = Config2Node(result) or this = CrossoverNode(result.getNode())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this.asNode1().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
or
|
||||
this.asNode2().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() {
|
||||
result = this.asNode1().toString()
|
||||
or
|
||||
result = this.asNode2().toString()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use flow state instead
|
||||
*
|
||||
* Holds if `(a,b)` is an edge in the graph of data flow path explanations.
|
||||
*/
|
||||
deprecated query predicate edges(CustomPathNode a, CustomPathNode b) {
|
||||
// Edge is in Config1 graph
|
||||
DataFlow::PathGraph::edges(a.asNode1(), b.asNode1())
|
||||
or
|
||||
// Edge is in Config2 graph
|
||||
DataFlow2::PathGraph::edges(a.asNode2(), b.asNode2())
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use flow state instead
|
||||
*
|
||||
* Holds if `n` is a node in the graph of data flow path explanations.
|
||||
*/
|
||||
deprecated query predicate nodes(CustomPathNode n, string key, string val) {
|
||||
// Node is in Config1 graph
|
||||
DataFlow::PathGraph::nodes(n.asNode1(), key, val)
|
||||
or
|
||||
// Node is in Config2 graph
|
||||
DataFlow2::PathGraph::nodes(n.asNode2(), key, val)
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
/** DEPRECATED. Import `CleartextLoggingQuery` instead. */
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.dataflow.new.BarrierGuards
|
||||
private import semmle.python.dataflow.new.SensitiveDataSources
|
||||
|
||||
/** DEPRECATED. Import `CleartextLoggingQuery` instead. */
|
||||
deprecated module CleartextLogging {
|
||||
import CleartextLoggingQuery // ignore-query-import
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
/** DEPRECATED. Import `CleartextStorageQuery` instead. */
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.dataflow.new.BarrierGuards
|
||||
private import semmle.python.dataflow.new.SensitiveDataSources
|
||||
|
||||
/** DEPRECATED. Import `CleartextStorageQuery` instead. */
|
||||
deprecated module CleartextStorage {
|
||||
import CleartextStorageQuery // ignore-query-import
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
/** DEPRECATED. Import `CodeInjectionQuery` instead. */
|
||||
|
||||
private import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
|
||||
/** DEPRECATED. Import `CodeInjectionQuery` instead. */
|
||||
deprecated module CodeInjection {
|
||||
import CodeInjectionQuery // ignore-query-import
|
||||
}
|
||||
|
||||
/** DEPRECATED. Import `CodeInjectionQuery` instead. */
|
||||
deprecated class CodeInjectionConfiguration = CodeInjection::Configuration;
|
||||
@@ -1,13 +0,0 @@
|
||||
/** DEPRECATED. Import `CommandInjectionQuery` instead. */
|
||||
|
||||
private import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
|
||||
/** DEPRECATED. Import `CommandInjectionQuery` instead. */
|
||||
deprecated module CommandInjection {
|
||||
import CommandInjectionQuery // ignore-query-import
|
||||
}
|
||||
|
||||
/** DEPRECATED. Import `CommandInjectionQuery` instead. */
|
||||
deprecated class CommandInjectionConfiguration = CommandInjection::Configuration;
|
||||
@@ -1,12 +0,0 @@
|
||||
/** DEPRECATED. Import `LdapInjectionQuery` instead. */
|
||||
|
||||
import python
|
||||
import semmle.python.Concepts
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
import semmle.python.dataflow.new.RemoteFlowSources
|
||||
|
||||
/** DEPRECATED. Import `LdapInjectionQuery` instead. */
|
||||
deprecated module LdapInjection {
|
||||
import LdapInjectionQuery // ignore-query-import
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
/** DEPRECATED. Import `LogInjectionQuery` instead. */
|
||||
|
||||
import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
|
||||
/** DEPRECATED. Import `LogInjectionQuery` instead. */
|
||||
deprecated module LogInjection {
|
||||
import LogInjectionQuery // ignore-query-import
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
/** DEPRECATED. Import `PathInjectionQuery` instead. */
|
||||
|
||||
private import python
|
||||
private import semmle.python.Concepts
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
|
||||
/** DEPRECATED. Import `PathInjectionQuery` instead. */
|
||||
deprecated module PathInjection {
|
||||
import PathInjectionQuery // ignore-query-import
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Old, deprecated code
|
||||
// ---------------------------------------------------------------------------
|
||||
private import semmle.python.dataflow.new.DataFlow2
|
||||
private import semmle.python.dataflow.new.TaintTracking2
|
||||
private import ChainedConfigs12
|
||||
import PathInjectionCustomizations::PathInjection
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Case 1. The path is never normalized.
|
||||
// ---------------------------------------------------------------------------
|
||||
/**
|
||||
* DEPRECATED: Import `PathInjectionQuery` instead.
|
||||
*
|
||||
* Configuration to find paths from sources to sinks that contain no normalization.
|
||||
*/
|
||||
deprecated class PathNotNormalizedConfiguration extends TaintTracking::Configuration {
|
||||
PathNotNormalizedConfiguration() { this = "PathNotNormalizedConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
node instanceof Sanitizer
|
||||
or
|
||||
node instanceof Path::PathNormalization
|
||||
}
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Import `PathInjectionQuery` instead.
|
||||
*
|
||||
* Holds if there is a path injection from source to sink, where the (python) path is
|
||||
* not normalized.
|
||||
*/
|
||||
deprecated predicate pathNotNormalized(CustomPathNode source, CustomPathNode sink) {
|
||||
any(PathNotNormalizedConfiguration config).hasFlowPath(source.asNode1(), sink.asNode1())
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Case 2. The path is normalized at least once, but never checked afterwards.
|
||||
// ---------------------------------------------------------------------------
|
||||
/**
|
||||
* DEPRECATED: Import `PathInjectionQuery` instead.
|
||||
*
|
||||
* Configuration to find paths from sources to normalizations that contain no prior normalizations.
|
||||
*/
|
||||
deprecated class FirstNormalizationConfiguration extends TaintTracking::Configuration {
|
||||
FirstNormalizationConfiguration() { this = "FirstNormalizationConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Path::PathNormalization }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
override predicate isSanitizerOut(DataFlow::Node node) { node instanceof Path::PathNormalization }
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Import `PathInjectionQuery` instead.
|
||||
*
|
||||
* Configuration to find paths from normalizations to sinks that do not go through a check.
|
||||
*/
|
||||
deprecated class NormalizedPathNotCheckedConfiguration extends TaintTracking2::Configuration {
|
||||
NormalizedPathNotCheckedConfiguration() { this = "NormalizedPathNotCheckedConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Path::PathNormalization }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
node instanceof Path::SafeAccessCheck
|
||||
or
|
||||
node instanceof Sanitizer
|
||||
}
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Import `PathInjectionQuery` instead.
|
||||
*
|
||||
* Holds if there is a path injection from source to sink, where the (python) path is
|
||||
* normalized at least once, but never checked afterwards.
|
||||
*/
|
||||
deprecated predicate pathNotCheckedAfterNormalization(CustomPathNode source, CustomPathNode sink) {
|
||||
exists(
|
||||
FirstNormalizationConfiguration config, DataFlow::PathNode mid1, DataFlow2::PathNode mid2,
|
||||
NormalizedPathNotCheckedConfiguration config2
|
||||
|
|
||||
config.hasFlowPath(source.asNode1(), mid1) and
|
||||
config2.hasFlowPath(mid2, sink.asNode2()) and
|
||||
mid1.getNode().asCfgNode() = mid2.getNode().asCfgNode()
|
||||
)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Query: Either case 1 or case 2.
|
||||
// ---------------------------------------------------------------------------
|
||||
/**
|
||||
* DEPRECATED: Import `PathInjectionQuery` instead.
|
||||
*
|
||||
* Holds if there is a path injection from source to sink
|
||||
*/
|
||||
deprecated predicate pathInjection(CustomPathNode source, CustomPathNode sink) {
|
||||
pathNotNormalized(source, sink)
|
||||
or
|
||||
pathNotCheckedAfterNormalization(source, sink)
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
/** DEPRECATED. Import `PolynomialReDoSQuery` instead. */
|
||||
|
||||
private import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
|
||||
/** DEPRECATED. Import `PolynomialReDoSQuery` instead. */
|
||||
deprecated module PolynomialReDoS {
|
||||
import PolynomialReDoSQuery // ignore-query-import
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
/** DEPRECATED. Import `ReflectedXSSQuery` instead. */
|
||||
|
||||
private import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
|
||||
/** DEPRECATED. Import `ReflectedXSSQuery` instead. */
|
||||
deprecated module ReflectedXss {
|
||||
import ReflectedXssQuery // ignore-query-import
|
||||
}
|
||||
|
||||
/** DEPRECATED. Import `ReflectedXSSQuery` instead. */
|
||||
deprecated module ReflectedXSS = ReflectedXss;
|
||||
|
||||
/** DEPRECATED. Import `ReflectedXSSQuery` instead. */
|
||||
deprecated class ReflectedXssConfiguration = ReflectedXss::Configuration;
|
||||
@@ -76,6 +76,3 @@ module ReflectedXss {
|
||||
*/
|
||||
class StringConstCompareAsSanitizerGuard extends Sanitizer, StringConstCompareBarrier { }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for ReflectedXss */
|
||||
deprecated module ReflectedXSS = ReflectedXss;
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
/** DEPRECATED. Import `RegexInjectionQuery` instead. */
|
||||
|
||||
private import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
|
||||
/** DEPRECATED. Import `RegexInjectionQuery` instead. */
|
||||
deprecated module RegexInjection {
|
||||
import RegexInjectionQuery // ignore-query-import
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/** DEPRECATED. Import `ServerSideRequestForgeryQuery` instead. */
|
||||
|
||||
private import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
import semmle.python.Concepts
|
||||
import ServerSideRequestForgeryQuery as ServerSideRequestForgeryQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `ServerSideRequestForgeryQuery` instead. */
|
||||
deprecated module FullServerSideRequestForgery {
|
||||
import ServerSideRequestForgeryCustomizations::ServerSideRequestForgery
|
||||
|
||||
class Configuration = ServerSideRequestForgeryQuery::FullServerSideRequestForgeryConfiguration;
|
||||
}
|
||||
|
||||
/** DEPRECATED. Import `ServerSideRequestForgeryQuery` instead. */
|
||||
deprecated predicate fullyControlledRequest =
|
||||
ServerSideRequestForgeryQuery::fullyControlledRequest/1;
|
||||
|
||||
/** DEPRECATED. Import `ServerSideRequestForgeryQuery` instead. */
|
||||
deprecated module PartialServerSideRequestForgery {
|
||||
import ServerSideRequestForgeryCustomizations::ServerSideRequestForgery
|
||||
|
||||
class Configuration = ServerSideRequestForgeryQuery::PartialServerSideRequestForgeryConfiguration;
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
/** DEPRECATED. Import `SqlInjectionQuery` instead. */
|
||||
|
||||
private import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
|
||||
/** DEPRECATED. Import `SqlInjectionQuery` instead. */
|
||||
deprecated module SqlInjection {
|
||||
import SqlInjectionQuery // ignore-query-import
|
||||
}
|
||||
|
||||
/** DEPRECATED. Import `SqlInjectionQuery` instead. */
|
||||
deprecated class SqlInjectionConfiguration = SqlInjection::Configuration;
|
||||
|
||||
/** DEPRECATED. Import `SqlInjectionQuery` instead. */
|
||||
deprecated class SQLInjectionConfiguration = SqlInjectionConfiguration;
|
||||
@@ -1,13 +0,0 @@
|
||||
/** DEPRECATED. Import `StackTraceExposureQuery` instead. */
|
||||
|
||||
private import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
|
||||
/** DEPRECATED. Import `StackTraceExposureQuery` instead. */
|
||||
deprecated module StackTraceExposure {
|
||||
import StackTraceExposureQuery // ignore-query-import
|
||||
}
|
||||
|
||||
/** DEPRECATED. Import `StackTraceExposureQuery` instead. */
|
||||
deprecated class StackTraceExposureConfiguration = StackTraceExposure::Configuration;
|
||||
@@ -1,13 +0,0 @@
|
||||
/** DEPRECATED. Import `UnsafeDeserializationQuery` instead. */
|
||||
|
||||
private import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
|
||||
/** DEPRECATED. Import `UnsafeDeserializationQuery` instead. */
|
||||
deprecated module UnsafeDeserialization {
|
||||
import UnsafeDeserializationQuery // ignore-query-import
|
||||
}
|
||||
|
||||
/** DEPRECATED. Import `UnsafeDeserializationQuery` instead. */
|
||||
deprecated class UnsafeDeserializationConfiguration = UnsafeDeserialization::Configuration;
|
||||
@@ -1,13 +0,0 @@
|
||||
/** DEPRECATED. Import `UrlRedirectQuery` instead. */
|
||||
|
||||
private import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
|
||||
/** DEPRECATED. Import `UrlRedirectQuery` instead. */
|
||||
deprecated module UrlRedirect {
|
||||
import UrlRedirectQuery // ignore-query-import
|
||||
}
|
||||
|
||||
/** DEPRECATED. Import `UrlRedirectQuery` instead. */
|
||||
deprecated class UrlRedirectConfiguration = UrlRedirect::Configuration;
|
||||
@@ -1,19 +0,0 @@
|
||||
/** DEPRECATED. Import `WeakSensitiveDataHashingQuery` instead. */
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.dataflow.new.BarrierGuards
|
||||
private import semmle.python.dataflow.new.SensitiveDataSources
|
||||
|
||||
/** DEPRECATED. Import `WeakSensitiveDataHashingQuery` instead. */
|
||||
deprecated module NormalHashFunction {
|
||||
import WeakSensitiveDataHashingQuery::NormalHashFunction // ignore-query-import
|
||||
}
|
||||
|
||||
/** DEPRECATED. Import `WeakSensitiveDataHashingQuery` instead. */
|
||||
deprecated module ComputationallyExpensiveHashFunction {
|
||||
import WeakSensitiveDataHashingQuery::ComputationallyExpensiveHashFunction // ignore-query-import
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
/** DEPRECATED. Import `XpathInjectionQuery` instead. */
|
||||
|
||||
private import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
|
||||
/** DEPRECATED. Import `XpathInjectionQuery` instead. */
|
||||
deprecated module XpathInjection {
|
||||
import XpathInjectionQuery // ignore-query-import
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import python
|
||||
import semmle.python.security.strings.Basic
|
||||
|
||||
/** Assume that taint flows from argument to result for *any* call */
|
||||
deprecated class AnyCallStringFlow extends DataFlowExtension::DataFlowNode {
|
||||
AnyCallStringFlow() { any(CallNode call).getAnArg() = this }
|
||||
|
||||
override ControlFlowNode getASuccessorNode() { result.(CallNode).getAnArg() = this }
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import python
|
||||
import semmle.python.dataflow.TaintTracking
|
||||
|
||||
/** `pickle.loads(untrusted)` vulnerability. */
|
||||
abstract deprecated class DeserializationSink extends TaintSink {
|
||||
bindingset[this]
|
||||
DeserializationSink() { this = this }
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/**
|
||||
* Provides class and predicates to track external data that
|
||||
* may represent malicious Python code.
|
||||
*
|
||||
* This module is intended to be imported into a taint-tracking query
|
||||
* to extend `TaintKind` and `TaintSink`.
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.dataflow.TaintTracking
|
||||
import semmle.python.security.strings.Untrusted
|
||||
|
||||
/**
|
||||
* A taint sink that represents an argument to exec or eval that is vulnerable to malicious input.
|
||||
* The `vuln` in `exec(vuln)` or similar.
|
||||
*/
|
||||
deprecated class StringEvaluationNode extends TaintSink {
|
||||
override string toString() { result = "exec or eval" }
|
||||
|
||||
StringEvaluationNode() {
|
||||
exists(Exec exec | exec.getASubExpression().getAFlowNode() = this)
|
||||
or
|
||||
Value::named("exec").getACall().getAnArg() = this
|
||||
or
|
||||
Value::named("eval").getACall().getAnArg() = this
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/**
|
||||
* Provides class and predicates to track external data that
|
||||
* may represent malicious marshals.
|
||||
*
|
||||
* This module is intended to be imported into a taint-tracking query
|
||||
* to extend `TaintKind` and `TaintSink`.
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.dataflow.TaintTracking
|
||||
import semmle.python.security.strings.Untrusted
|
||||
import semmle.python.security.injection.Deserialization
|
||||
|
||||
deprecated private FunctionObject marshalLoads() {
|
||||
result = ModuleObject::named("marshal").attr("loads")
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint sink that is potentially vulnerable to malicious marshaled objects.
|
||||
* The `vuln` in `marshal.loads(vuln)`.
|
||||
*/
|
||||
deprecated class UnmarshalingNode extends DeserializationSink {
|
||||
override string toString() { result = "unmarshaling vulnerability" }
|
||||
|
||||
UnmarshalingNode() {
|
||||
exists(CallNode call |
|
||||
marshalLoads().getACall() = call and
|
||||
call.getAnArg() = this
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
import python
|
||||
import semmle.python.dataflow.TaintTracking
|
||||
import semmle.python.security.strings.Untrusted
|
||||
|
||||
/**
|
||||
* Prevents taint flowing through ntpath.normpath()
|
||||
* NormalizedPath below handles that case.
|
||||
*/
|
||||
deprecated class PathSanitizer extends Sanitizer {
|
||||
PathSanitizer() { this = "path.sanitizer" }
|
||||
|
||||
override predicate sanitizingNode(TaintKind taint, ControlFlowNode node) {
|
||||
taint instanceof ExternalStringKind and
|
||||
abspath_call(node, _)
|
||||
}
|
||||
}
|
||||
|
||||
deprecated private FunctionObject abspath() {
|
||||
exists(ModuleObject os_path | ModuleObject::named("os").attr("path") = os_path |
|
||||
os_path.attr("abspath") = result
|
||||
or
|
||||
os_path.attr("normpath") = result
|
||||
)
|
||||
}
|
||||
|
||||
/** A path that has been normalized, but not verified to be safe */
|
||||
deprecated class NormalizedPath extends TaintKind {
|
||||
NormalizedPath() { this = "normalized.path.injection" }
|
||||
|
||||
override string repr() { result = "normalized path" }
|
||||
}
|
||||
|
||||
deprecated private predicate abspath_call(CallNode call, ControlFlowNode arg) {
|
||||
call.getFunction().refersTo(abspath()) and
|
||||
arg = call.getArg(0)
|
||||
}
|
||||
|
||||
deprecated class AbsPath extends DataFlowExtension::DataFlowNode {
|
||||
AbsPath() { abspath_call(_, this) }
|
||||
|
||||
override ControlFlowNode getASuccessorNode(TaintKind fromkind, TaintKind tokind) {
|
||||
abspath_call(result, this) and
|
||||
tokind instanceof NormalizedPath and
|
||||
fromkind instanceof ExternalStringKind
|
||||
}
|
||||
}
|
||||
|
||||
deprecated class NormalizedPathSanitizer extends Sanitizer {
|
||||
NormalizedPathSanitizer() { this = "normalized.path.sanitizer" }
|
||||
|
||||
override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) {
|
||||
taint instanceof NormalizedPath and
|
||||
test.getTest().(CallNode).getFunction().(AttrNode).getName() = "startswith" and
|
||||
test.getSense() = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint sink that is vulnerable to malicious paths.
|
||||
* The `vuln` in `open(vuln)` and similar.
|
||||
*/
|
||||
deprecated class OpenNode extends TaintSink {
|
||||
override string toString() { result = "argument to open()" }
|
||||
|
||||
OpenNode() {
|
||||
exists(CallNode call |
|
||||
call = Value::named("open").getACall() and
|
||||
(
|
||||
call.getArg(0) = this
|
||||
or
|
||||
call.getArgByName("file") = this
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) {
|
||||
kind instanceof ExternalStringKind
|
||||
or
|
||||
kind instanceof NormalizedPath
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/**
|
||||
* Provides class and predicates to track external data that
|
||||
* may represent malicious pickles.
|
||||
*
|
||||
* This module is intended to be imported into a taint-tracking query
|
||||
* to extend `TaintKind` and `TaintSink`.
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.dataflow.TaintTracking
|
||||
import semmle.python.security.strings.Untrusted
|
||||
import semmle.python.security.injection.Deserialization
|
||||
|
||||
deprecated private ModuleObject pickleModule() {
|
||||
result.getName() = "pickle"
|
||||
or
|
||||
result.getName() = "cPickle"
|
||||
or
|
||||
result.getName() = "dill"
|
||||
}
|
||||
|
||||
deprecated private FunctionObject pickleLoads() { result = pickleModule().attr("loads") }
|
||||
|
||||
/** `pickle.loads(untrusted)` vulnerability. */
|
||||
deprecated class UnpicklingNode extends DeserializationSink {
|
||||
override string toString() { result = "unpickling untrusted data" }
|
||||
|
||||
UnpicklingNode() {
|
||||
exists(CallNode call |
|
||||
pickleLoads().getACall() = call and
|
||||
call.getAnArg() = this
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
/** DEPRECATED: use semmle.python.security.dataflow.RegexInjection instead. */
|
||||
|
||||
private import semmle.python.security.dataflow.RegexInjection as New
|
||||
|
||||
/** DEPRECATED: use semmle.python.security.dataflow.RegexInjection instead. */
|
||||
deprecated module RegexInjection = New::RegexInjection;
|
||||
@@ -1,6 +0,0 @@
|
||||
/** DEPRECATED: use semmle.python.security.dataflow.RegexInjectionCustomizations instead. */
|
||||
|
||||
private import semmle.python.security.dataflow.RegexInjectionCustomizations as New
|
||||
|
||||
/** DEPRECATED: use semmle.python.security.dataflow.RegexInjectionCustomizations instead. */
|
||||
deprecated module RegexInjection = New::RegexInjection;
|
||||
@@ -1,70 +0,0 @@
|
||||
/**
|
||||
* Provides class and predicates to track external data that
|
||||
* may represent malicious XML objects.
|
||||
*
|
||||
* This module is intended to be imported into a taint-tracking query
|
||||
* to extend `TaintKind` and `TaintSink`.
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.dataflow.TaintTracking
|
||||
import semmle.python.security.strings.Untrusted
|
||||
import semmle.python.security.injection.Deserialization
|
||||
|
||||
deprecated private ModuleObject xmlElementTreeModule() {
|
||||
result.getName() = "xml.etree.ElementTree"
|
||||
}
|
||||
|
||||
deprecated private ModuleObject xmlMiniDomModule() { result.getName() = "xml.dom.minidom" }
|
||||
|
||||
deprecated private ModuleObject xmlPullDomModule() { result.getName() = "xml.dom.pulldom" }
|
||||
|
||||
deprecated private ModuleObject xmlSaxModule() { result.getName() = "xml.sax" }
|
||||
|
||||
deprecated private class ExpatParser extends TaintKind {
|
||||
ExpatParser() { this = "expat.parser" }
|
||||
}
|
||||
|
||||
deprecated private FunctionObject expatCreateParseFunction() {
|
||||
result = ModuleObject::named("xml.parsers.expat").attr("ParserCreate")
|
||||
}
|
||||
|
||||
deprecated private class ExpatCreateParser extends TaintSource {
|
||||
ExpatCreateParser() { expatCreateParseFunction().getACall() = this }
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof ExpatParser }
|
||||
|
||||
override string toString() { result = "expat.create.parser" }
|
||||
}
|
||||
|
||||
deprecated private FunctionObject xmlFromString() {
|
||||
result = xmlElementTreeModule().attr("fromstring")
|
||||
or
|
||||
result = xmlMiniDomModule().attr("parseString")
|
||||
or
|
||||
result = xmlPullDomModule().attr("parseString")
|
||||
or
|
||||
result = xmlSaxModule().attr("parseString")
|
||||
}
|
||||
|
||||
/** A (potentially) malicious XML string. */
|
||||
deprecated class ExternalXmlString extends ExternalStringKind {
|
||||
ExternalXmlString() { this = "external xml encoded object" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to an XML library function that is potentially vulnerable to a
|
||||
* specially crafted XML string.
|
||||
*/
|
||||
deprecated class XmlLoadNode extends DeserializationSink {
|
||||
override string toString() { result = "xml.load vulnerability" }
|
||||
|
||||
XmlLoadNode() {
|
||||
exists(CallNode call | call.getAnArg() = this |
|
||||
xmlFromString().getACall() = call or
|
||||
any(ExpatParser parser).taints(call.getFunction().(AttrNode).getObject("Parse"))
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) { kind instanceof ExternalXmlString }
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/**
|
||||
* Provides class and predicates to track external data that
|
||||
* may represent malicious yaml-encoded objects.
|
||||
*
|
||||
* This module is intended to be imported into a taint-tracking query
|
||||
* to extend `TaintKind` and `TaintSink`.
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.dataflow.TaintTracking
|
||||
import semmle.python.security.strings.Untrusted
|
||||
import semmle.python.security.injection.Deserialization
|
||||
|
||||
deprecated private FunctionObject yamlLoad() { result = ModuleObject::named("yaml").attr("load") }
|
||||
|
||||
/** `yaml.load(untrusted)` vulnerability. */
|
||||
deprecated class YamlLoadNode extends DeserializationSink {
|
||||
override string toString() { result = "yaml.load vulnerability" }
|
||||
|
||||
YamlLoadNode() {
|
||||
exists(CallNode call |
|
||||
yamlLoad().getACall() = call and
|
||||
call.getAnArg() = this
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
|
||||
}
|
||||
@@ -14,7 +14,6 @@ import python
|
||||
private import semmle.python.pointsto.PointsTo
|
||||
private import semmle.python.pointsto.PointsToContext
|
||||
private import semmle.python.objects.TObject
|
||||
private import semmle.python.web.HttpConstants
|
||||
/* Make ObjectInternal visible to save extra imports in user code */
|
||||
import semmle.python.objects.ObjectInternal
|
||||
|
||||
@@ -52,30 +51,6 @@ class RangeIterationVariableFact extends PointsToExtension {
|
||||
}
|
||||
}
|
||||
|
||||
/* bottle module route constants */
|
||||
deprecated class BottleRoutePointToExtension extends PointsToExtension {
|
||||
string name;
|
||||
|
||||
BottleRoutePointToExtension() {
|
||||
exists(DefinitionNode defn |
|
||||
defn.getScope().(Module).getName() = "bottle" and
|
||||
this = defn.getValue() and
|
||||
name = defn.(NameNode).getId()
|
||||
|
|
||||
name = "route" or
|
||||
name = httpVerbLower()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate pointsTo(Context context, ObjectInternal value, ControlFlowNode origin) {
|
||||
context.isImport() and
|
||||
exists(CfgOrigin orig |
|
||||
Module::named("bottle").attr("Bottle").(ClassObjectInternal).attribute(name, value, orig) and
|
||||
origin = orig.asCfgNodeOrHere(this)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/* Python 3.6+ regex module constants */
|
||||
string short_flag(string flag) {
|
||||
flag in ["ASCII", "IGNORECASE", "LOCALE", "UNICODE", "MULTILINE", "TEMPLATE"] and
|
||||
|
||||
@@ -34,4 +34,4 @@ where
|
||||
)
|
||||
)
|
||||
select div, "Result of division may be truncated as its $@ and $@ arguments may both be integers.",
|
||||
left.getLocation(), "left", right.getLocation(), "right"
|
||||
left, "left", right, "right"
|
||||
|
||||
@@ -33,9 +33,6 @@ class SafeExternalApi extends Unit {
|
||||
DataFlowPrivate::DataFlowCallable getSafeCallable() { none() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for SafeExternalApi */
|
||||
deprecated class SafeExternalAPI = SafeExternalApi;
|
||||
|
||||
/** The default set of "safe" external APIs. */
|
||||
private class DefaultSafeExternalApi extends SafeExternalApi {
|
||||
override DataFlow::CallCfgNode getSafeCall() {
|
||||
@@ -170,9 +167,6 @@ class ExternalApiDataNode extends DataFlow::Node {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for ExternalApiDataNode */
|
||||
deprecated class ExternalAPIDataNode = ExternalApiDataNode;
|
||||
|
||||
/** A configuration for tracking flow from `RemoteFlowSource`s to `ExternalApiDataNode`s. */
|
||||
class UntrustedDataToExternalApiConfig extends TaintTracking::Configuration {
|
||||
UntrustedDataToExternalApiConfig() { this = "UntrustedDataToExternalAPIConfig" }
|
||||
@@ -182,9 +176,6 @@ class UntrustedDataToExternalApiConfig extends TaintTracking::Configuration {
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof ExternalApiDataNode }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for UntrustedDataToExternalApiConfig */
|
||||
deprecated class UntrustedDataToExternalAPIConfig = UntrustedDataToExternalApiConfig;
|
||||
|
||||
/** A node representing untrusted data being passed to an external API. */
|
||||
class UntrustedExternalApiDataNode extends ExternalApiDataNode {
|
||||
UntrustedExternalApiDataNode() { any(UntrustedDataToExternalApiConfig c).hasFlow(_, this) }
|
||||
@@ -195,9 +186,6 @@ class UntrustedExternalApiDataNode extends ExternalApiDataNode {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for UntrustedExternalApiDataNode */
|
||||
deprecated class UntrustedExternalAPIDataNode = UntrustedExternalApiDataNode;
|
||||
|
||||
/** An external API which is used with untrusted data. */
|
||||
private newtype TExternalApi =
|
||||
MkExternalApi(string repr, DataFlowPrivate::ArgumentPosition apos) {
|
||||
@@ -230,6 +218,3 @@ class ExternalApiUsedWithUntrustedData extends MkExternalApi {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = repr + " [" + apos + "]" }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for ExternalApiUsedWithUntrustedData */
|
||||
deprecated class ExternalAPIUsedWithUntrustedData = ExternalApiUsedWithUntrustedData;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @name Deserializing untrusted input
|
||||
* @name Deserialization of user-controlled data
|
||||
* @description Deserializing user-controlled data may allow attackers to execute arbitrary code.
|
||||
* @kind path-problem
|
||||
* @id py/unsafe-deserialization
|
||||
|
||||
@@ -36,5 +36,5 @@ where
|
||||
ex = Value::named("sys.exc_info") and
|
||||
ex.getACall().getScope() = f
|
||||
)
|
||||
select del, "Unnecessary deletion of local variable $@ in function $@.", e.getLocation(),
|
||||
e.toString(), f.getLocation(), f.getName()
|
||||
select del, "Unnecessary deletion of local variable $@ in function $@.", e, e.toString(), f,
|
||||
f.getName()
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: fix
|
||||
---
|
||||
* The display name (`@name`) of the `py/unsafe-deserialization` query has been updated in favor of consistency with other languages.
|
||||
@@ -5,7 +5,7 @@
|
||||
* @problem.severity error
|
||||
* @security-severity 9.3
|
||||
* @precision high
|
||||
* @id py/command-injection
|
||||
* @id py/paramiko-command-injection
|
||||
* @tags security
|
||||
* experimental
|
||||
* external/cwe/cwe-074
|
||||
|
||||
@@ -90,9 +90,6 @@ module LdapQuery {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for LdapQuery */
|
||||
deprecated module LDAPQuery = LdapQuery;
|
||||
|
||||
/**
|
||||
* A data-flow node that collect methods executing a LDAP query.
|
||||
*
|
||||
@@ -106,9 +103,6 @@ class LdapQuery extends DataFlow::Node instanceof LdapQuery::Range {
|
||||
DataFlow::Node getQuery() { result = super.getQuery() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for LdapQuery */
|
||||
deprecated class LDAPQuery = LdapQuery;
|
||||
|
||||
/** Provides classes for modeling LDAP components escape-related APIs. */
|
||||
module LdapEscape {
|
||||
/**
|
||||
@@ -125,9 +119,6 @@ module LdapEscape {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for LdapEscape */
|
||||
deprecated module LDAPEscape = LdapEscape;
|
||||
|
||||
/**
|
||||
* A data-flow node that collects functions escaping LDAP components.
|
||||
*
|
||||
@@ -141,9 +132,6 @@ class LdapEscape extends DataFlow::Node instanceof LdapEscape::Range {
|
||||
DataFlow::Node getAnInput() { result = super.getAnInput() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for LdapEscape */
|
||||
deprecated class LDAPEscape = LdapEscape;
|
||||
|
||||
/** Provides classes for modeling LDAP bind-related APIs. */
|
||||
module LdapBind {
|
||||
/**
|
||||
@@ -173,9 +161,6 @@ module LdapBind {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for LdapBind */
|
||||
deprecated module LDAPBind = LdapBind;
|
||||
|
||||
/**
|
||||
* A data-flow node that collects methods binding a LDAP connection.
|
||||
*
|
||||
@@ -202,9 +187,6 @@ class LdapBind extends DataFlow::Node instanceof LdapBind::Range {
|
||||
deprecated predicate useSSL() { this.useSsl() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for LdapBind */
|
||||
deprecated class LDAPBind = LdapBind;
|
||||
|
||||
/** Provides classes for modeling SQL sanitization libraries. */
|
||||
module SqlEscape {
|
||||
/**
|
||||
@@ -221,9 +203,6 @@ module SqlEscape {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for SqlEscape */
|
||||
deprecated module SQLEscape = SqlEscape;
|
||||
|
||||
/**
|
||||
* A data-flow node that collects functions escaping SQL statements.
|
||||
*
|
||||
@@ -237,9 +216,6 @@ class SqlEscape extends DataFlow::Node instanceof SqlEscape::Range {
|
||||
DataFlow::Node getAnInput() { result = super.getAnInput() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for SqlEscape */
|
||||
deprecated class SQLEscape = SqlEscape;
|
||||
|
||||
/** Provides a class for modeling NoSql execution APIs. */
|
||||
module NoSqlQuery {
|
||||
/**
|
||||
@@ -254,9 +230,6 @@ module NoSqlQuery {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for NoSqlQuery */
|
||||
deprecated module NoSQLQuery = NoSqlQuery;
|
||||
|
||||
/**
|
||||
* A data-flow node that executes NoSQL queries.
|
||||
*
|
||||
@@ -268,9 +241,6 @@ class NoSqlQuery extends DataFlow::Node instanceof NoSqlQuery::Range {
|
||||
DataFlow::Node getQuery() { result = super.getQuery() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for NoSqlQuery */
|
||||
deprecated class NoSQLQuery = NoSqlQuery;
|
||||
|
||||
/** Provides classes for modeling NoSql sanitization-related APIs. */
|
||||
module NoSqlSanitizer {
|
||||
/**
|
||||
@@ -285,9 +255,6 @@ module NoSqlSanitizer {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for NoSqlSanitizer */
|
||||
deprecated module NoSQLSanitizer = NoSqlSanitizer;
|
||||
|
||||
/**
|
||||
* A data-flow node that collects functions sanitizing NoSQL queries.
|
||||
*
|
||||
@@ -299,9 +266,6 @@ class NoSqlSanitizer extends DataFlow::Node instanceof NoSqlSanitizer::Range {
|
||||
DataFlow::Node getAnInput() { result = super.getAnInput() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for NoSqlSanitizer */
|
||||
deprecated class NoSQLSanitizer = NoSqlSanitizer;
|
||||
|
||||
/** Provides classes for modeling HTTP Header APIs. */
|
||||
module HeaderDeclaration {
|
||||
/**
|
||||
@@ -450,9 +414,6 @@ module JwtEncoding {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for JwtEncoding */
|
||||
deprecated module JWTEncoding = JwtEncoding;
|
||||
|
||||
/**
|
||||
* A data-flow node that collects methods encoding a JWT token.
|
||||
*
|
||||
@@ -481,9 +442,6 @@ class JwtEncoding extends DataFlow::Node instanceof JwtEncoding::Range {
|
||||
string getAlgorithmString() { result = super.getAlgorithmString() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for JwtEncoding */
|
||||
deprecated class JWTEncoding = JwtEncoding;
|
||||
|
||||
/** Provides classes for modeling JWT decoding-related APIs. */
|
||||
module JwtDecoding {
|
||||
/**
|
||||
@@ -525,9 +483,6 @@ module JwtDecoding {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for JwtDecoding */
|
||||
deprecated module JWTDecoding = JwtDecoding;
|
||||
|
||||
/**
|
||||
* A data-flow node that collects methods encoding a JWT token.
|
||||
*
|
||||
@@ -566,9 +521,6 @@ class JwtDecoding extends DataFlow::Node instanceof JwtDecoding::Range {
|
||||
predicate verifiesSignature() { super.verifiesSignature() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for JwtDecoding */
|
||||
deprecated class JWTDecoding = JwtDecoding;
|
||||
|
||||
/** Provides classes for modeling Email APIs. */
|
||||
module EmailSender {
|
||||
/**
|
||||
|
||||
@@ -29,23 +29,14 @@ class LdapFullHost extends StrConst {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for LdapFullHost */
|
||||
deprecated class LDAPFullHost = LdapFullHost;
|
||||
|
||||
class LdapSchema extends StrConst {
|
||||
LdapSchema() { this.getText().regexpMatch(getSchemaRegex()) }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for LdapSchema */
|
||||
deprecated class LDAPSchema = LdapSchema;
|
||||
|
||||
class LdapPrivateHost extends StrConst {
|
||||
LdapPrivateHost() { this.getText().regexpMatch(getPrivateHostRegex()) }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for LdapPrivateHost */
|
||||
deprecated class LDAPPrivateHost = LdapPrivateHost;
|
||||
|
||||
predicate concatAndCompareAgainstFullHostRegex(LdapSchema schema, StrConst host) {
|
||||
not host instanceof LdapPrivateHost and
|
||||
(schema.getText() + host.getText()).regexpMatch(getFullHostRegex())
|
||||
@@ -56,9 +47,6 @@ class LdapBothStrings extends BinaryExpr {
|
||||
LdapBothStrings() { concatAndCompareAgainstFullHostRegex(this.getLeft(), this.getRight()) }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for LdapBothStrings */
|
||||
deprecated class LDAPBothStrings = LdapBothStrings;
|
||||
|
||||
// schema + host
|
||||
class LdapBothVar extends BinaryExpr {
|
||||
LdapBothVar() {
|
||||
@@ -73,9 +61,6 @@ class LdapBothVar extends BinaryExpr {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for LdapBothVar */
|
||||
deprecated class LDAPBothVar = LdapBothVar;
|
||||
|
||||
// schema + "somethingon.theinternet.com"
|
||||
class LdapVarString extends BinaryExpr {
|
||||
LdapVarString() {
|
||||
@@ -89,9 +74,6 @@ class LdapVarString extends BinaryExpr {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for LdapVarString */
|
||||
deprecated class LDAPVarString = LdapVarString;
|
||||
|
||||
// "ldap://" + host
|
||||
class LdapStringVar extends BinaryExpr {
|
||||
LdapStringVar() {
|
||||
@@ -103,9 +85,6 @@ class LdapStringVar extends BinaryExpr {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for LdapStringVar */
|
||||
deprecated class LDAPStringVar = LdapStringVar;
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for detecting LDAP insecure authentications.
|
||||
*/
|
||||
@@ -125,6 +104,3 @@ class LdapInsecureAuthConfig extends TaintTracking::Configuration {
|
||||
exists(LdapBind ldapBind | not ldapBind.useSsl() and sink = ldapBind.getHost())
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for LdapInsecureAuthConfig */
|
||||
deprecated class LDAPInsecureAuthConfig = LdapInsecureAuthConfig;
|
||||
|
||||
@@ -52,6 +52,3 @@ module NoSqlInjection {
|
||||
ConvertedToDict() { this = "ConvertedToDict" }
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for NoSqlInjection */
|
||||
deprecated module NoSQLInjection = NoSqlInjection;
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
| TruncatedDivision_test.py:65:7:65:11 | BinaryExpr | Result of division may be truncated as its $@ and $@ arguments may both be integers. | TruncatedDivision_test.py:65:7:65:7 | TruncatedDivision_test.py:65 | left | TruncatedDivision_test.py:65:11:65:11 | TruncatedDivision_test.py:65 | right |
|
||||
| TruncatedDivision_test.py:72:7:72:35 | BinaryExpr | Result of division may be truncated as its $@ and $@ arguments may both be integers. | TruncatedDivision_test.py:25:12:25:12 | TruncatedDivision_test.py:25 | left | TruncatedDivision_test.py:28:12:28:12 | TruncatedDivision_test.py:28 | right |
|
||||
| TruncatedDivision_test.py:65:7:65:11 | BinaryExpr | Result of division may be truncated as its $@ and $@ arguments may both be integers. | TruncatedDivision_test.py:65:7:65:7 | ControlFlowNode for IntegerLiteral | left | TruncatedDivision_test.py:65:11:65:11 | ControlFlowNode for IntegerLiteral | right |
|
||||
| TruncatedDivision_test.py:72:7:72:35 | BinaryExpr | Result of division may be truncated as its $@ and $@ arguments may both be integers. | TruncatedDivision_test.py:25:12:25:12 | ControlFlowNode for IntegerLiteral | left | TruncatedDivision_test.py:28:12:28:12 | ControlFlowNode for IntegerLiteral | right |
|
||||
|
||||
@@ -1 +1 @@
|
||||
| nonsense.py:0:1:0:1 | Syntax Error | Syntax Error (in Python 2). |
|
||||
| nonsense.py:1:1:1:1 | Syntax Error | Syntax Error (in Python 2). |
|
||||
|
||||
@@ -1 +1 @@
|
||||
| nonsense.py:0:1:0:1 | Syntax Error | Syntax Error (in Python 3). |
|
||||
| nonsense.py:1:2:1:2 | Syntax Error | Syntax Error (in Python 3). |
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
failures
|
||||
testFailures
|
||||
|
||||
@@ -4,16 +4,14 @@ import semmle.python.dataflow.new.internal.DataFlowDispatch as DataFlowDispatch
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
private import semmle.python.dataflow.new.internal.PrintNode
|
||||
|
||||
class DataFlowCallTest extends InlineExpectationsTest {
|
||||
DataFlowCallTest() { this = "DataFlowCallTest" }
|
||||
|
||||
override string getARelevantTag() {
|
||||
module DataFlowCallTest implements TestSig {
|
||||
string getARelevantTag() {
|
||||
result in ["call", "callType"]
|
||||
or
|
||||
result = "arg[" + any(DataFlowDispatch::ArgumentPosition pos).toString() + "]"
|
||||
}
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(location.getFile().getRelativePath()) and
|
||||
exists(DataFlowDispatch::DataFlowCall call |
|
||||
location = call.getLocation() and
|
||||
@@ -35,3 +33,5 @@ class DataFlowCallTest extends InlineExpectationsTest {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import MakeTest<DataFlowCallTest>
|
||||
|
||||
@@ -57,7 +57,7 @@ def test_list_from_set():
|
||||
s = {SOURCE}
|
||||
l = list(s)
|
||||
SINK(l[0]) #$ flow="SOURCE, l:-2 -> l[0]"
|
||||
|
||||
|
||||
@expects(2)
|
||||
def test_list_from_dict():
|
||||
d = {SOURCE: 'v', NONSOURCE: 'v2'}
|
||||
@@ -154,19 +154,19 @@ def test_list_pop():
|
||||
def test_list_pop_index():
|
||||
l = [SOURCE]
|
||||
v = l.pop(0)
|
||||
SINK(v) #$ MISSING: flow="SOURCE, l:-2 -> v"
|
||||
SINK(v) #$ flow="SOURCE, l:-2 -> v"
|
||||
|
||||
def test_list_pop_index_imprecise():
|
||||
l = [SOURCE, NONSOURCE]
|
||||
v = l.pop(1)
|
||||
SINK_F(v)
|
||||
SINK_F(v) #$ SPURIOUS: flow="SOURCE, l:-2 -> v"
|
||||
|
||||
@expects(2)
|
||||
def test_list_copy():
|
||||
l0 = [SOURCE, NONSOURCE]
|
||||
l = l0.copy()
|
||||
SINK(l[0]) #$ MISSING: flow="SOURCE, l:-2 -> l[0]"
|
||||
SINK_F(l[1])
|
||||
SINK(l[0]) #$ flow="SOURCE, l:-2 -> l[0]"
|
||||
SINK_F(l[1]) #$ SPURIOUS: flow="SOURCE, l:-3 -> l[1]"
|
||||
|
||||
def test_list_append():
|
||||
l = [NONSOURCE]
|
||||
@@ -183,7 +183,7 @@ def test_set_pop():
|
||||
def test_set_copy():
|
||||
s0 = {SOURCE}
|
||||
s = s0.copy()
|
||||
SINK(s.pop()) #$ MISSING: flow="SOURCE, l:-2 -> s.pop()"
|
||||
SINK(s.pop()) #$ flow="SOURCE, l:-2 -> s.pop()"
|
||||
|
||||
def test_set_add():
|
||||
s = set([])
|
||||
@@ -222,28 +222,31 @@ def test_dict_pop():
|
||||
v1 = d.pop("k", NONSOURCE)
|
||||
SINK_F(v1) #$ SPURIOUS: flow="SOURCE, l:-4 -> v1"
|
||||
v2 = d.pop("non-existing", SOURCE)
|
||||
SINK(v2) #$ MISSING: flow="SOURCE, l:-1 -> v2"
|
||||
SINK(v2) #$ flow="SOURCE, l:-1 -> v2"
|
||||
|
||||
@expects(2)
|
||||
@expects(3)
|
||||
def test_dict_get():
|
||||
d = {'k': SOURCE}
|
||||
v = d.get("k")
|
||||
SINK(v) #$ flow="SOURCE, l:-2 -> v"
|
||||
v1 = d.get("non-existing", SOURCE)
|
||||
SINK(v1) #$ MISSING: flow="SOURCE, l:-1 -> v1"
|
||||
SINK(v1) #$ flow="SOURCE, l:-1 -> v1"
|
||||
k = "k"
|
||||
v2 = d.get(k)
|
||||
SINK(v2) #$ flow="SOURCE, l:-7 -> v2"
|
||||
|
||||
@expects(2)
|
||||
def test_dict_popitem():
|
||||
d = {'k': SOURCE}
|
||||
t = d.popitem() # could be any pair (before 3.7), but we only have one
|
||||
SINK_F(t[0])
|
||||
SINK(t[1]) #$ MISSING: flow="SOURCE, l:-3 -> t[1]"
|
||||
SINK(t[1]) #$ flow="SOURCE, l:-3 -> t[1]"
|
||||
|
||||
@expects(2)
|
||||
def test_dict_copy():
|
||||
d = {'k': SOURCE, 'k1': NONSOURCE}
|
||||
d1 = d.copy()
|
||||
SINK(d1["k"]) #$ MISSING: flow="SOURCE, l:-2 -> d[k]"
|
||||
SINK(d1["k"]) #$ flow="SOURCE, l:-2 -> d1['k']"
|
||||
SINK_F(d1["k1"])
|
||||
|
||||
|
||||
@@ -354,4 +357,4 @@ def test_next_dict():
|
||||
d = {SOURCE: "val"}
|
||||
i = iter(d)
|
||||
n = next(i)
|
||||
SINK(n) #$ MISSING: flow="SOURCE, l:-3 -> n"
|
||||
SINK(n) #$ MISSING: flow="SOURCE, l:-3 -> n"
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
failures
|
||||
testFailures
|
||||
|
||||
@@ -2,12 +2,10 @@ import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
class GlobalReadTest extends InlineExpectationsTest {
|
||||
GlobalReadTest() { this = "GlobalReadTest" }
|
||||
module GlobalReadTest implements TestSig {
|
||||
string getARelevantTag() { result = "reads" }
|
||||
|
||||
override string getARelevantTag() { result = "reads" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(DataFlow::ModuleVariableNode n, DataFlow::Node read |
|
||||
read = n.getARead() and
|
||||
value = n.getVariable().getId() and
|
||||
@@ -19,12 +17,10 @@ class GlobalReadTest extends InlineExpectationsTest {
|
||||
}
|
||||
}
|
||||
|
||||
class GlobalWriteTest extends InlineExpectationsTest {
|
||||
GlobalWriteTest() { this = "GlobalWriteTest" }
|
||||
module GlobalWriteTest implements TestSig {
|
||||
string getARelevantTag() { result = "writes" }
|
||||
|
||||
override string getARelevantTag() { result = "writes" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(DataFlow::ModuleVariableNode n, DataFlow::Node read |
|
||||
read = n.getAWrite() and
|
||||
value = n.getVariable().getId() and
|
||||
@@ -34,3 +30,5 @@ class GlobalWriteTest extends InlineExpectationsTest {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import MakeTest<MergeTests<GlobalReadTest, GlobalWriteTest>>
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
failures
|
||||
testFailures
|
||||
|
||||
@@ -8,12 +8,10 @@ import TestUtilities.InlineExpectationsTest
|
||||
import semmle.python.dataflow.new.SensitiveDataSources
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
class SensitiveDataSourcesTest extends InlineExpectationsTest {
|
||||
SensitiveDataSourcesTest() { this = "SensitiveDataSourcesTest" }
|
||||
module SensitiveDataSourcesTest implements TestSig {
|
||||
string getARelevantTag() { result in ["SensitiveDataSource", "SensitiveUse"] }
|
||||
|
||||
override string getARelevantTag() { result in ["SensitiveDataSource", "SensitiveUse"] }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(location.getFile().getRelativePath()) and
|
||||
exists(SensitiveDataSource source |
|
||||
location = source.getLocation() and
|
||||
@@ -32,6 +30,8 @@ class SensitiveDataSourcesTest extends InlineExpectationsTest {
|
||||
}
|
||||
}
|
||||
|
||||
import MakeTest<SensitiveDataSourcesTest>
|
||||
|
||||
class SensitiveUseConfiguration extends TaintTracking::Configuration {
|
||||
SensitiveUseConfiguration() { this = "SensitiveUseConfiguration" }
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
failures
|
||||
testFailures
|
||||
|
||||
@@ -3,12 +3,10 @@ import semmle.python.dataflow.new.DataFlow
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import experimental.dataflow.testConfig
|
||||
|
||||
class CaptureTest extends InlineExpectationsTest {
|
||||
CaptureTest() { this = "CaptureTest" }
|
||||
module CaptureTest implements TestSig {
|
||||
string getARelevantTag() { result = "captured" }
|
||||
|
||||
override string getARelevantTag() { result = "captured" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(DataFlow::Node sink | exists(TestConfiguration cfg | cfg.hasFlowTo(sink)) |
|
||||
location = sink.getLocation() and
|
||||
tag = "captured" and
|
||||
@@ -17,3 +15,5 @@ class CaptureTest extends InlineExpectationsTest {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import MakeTest<CaptureTest>
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
failures
|
||||
testFailures
|
||||
|
||||
@@ -72,12 +72,10 @@ private class ImportConfiguration extends DataFlow::Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
class ResolutionTest extends InlineExpectationsTest {
|
||||
ResolutionTest() { this = "ResolutionTest" }
|
||||
module ResolutionTest implements TestSig {
|
||||
string getARelevantTag() { result = "prints" }
|
||||
|
||||
override string getARelevantTag() { result = "prints" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
(
|
||||
exists(DataFlow::PathNode source, DataFlow::PathNode sink, ImportConfiguration config |
|
||||
config.hasFlowPath(source, sink) and
|
||||
@@ -105,12 +103,10 @@ private string getTagForVersion(int version) {
|
||||
version = major_version()
|
||||
}
|
||||
|
||||
class VersionSpecificResolutionTest extends InlineExpectationsTest {
|
||||
VersionSpecificResolutionTest() { this = "VersionSpecificResolutionTest" }
|
||||
module VersionSpecificResolutionTest implements TestSig {
|
||||
string getARelevantTag() { result = getTagForVersion(_) }
|
||||
|
||||
override string getARelevantTag() { result = getTagForVersion(_) }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
(
|
||||
exists(DataFlow::PathNode source, DataFlow::PathNode sink, ImportConfiguration config |
|
||||
config.hasFlowPath(source, sink) and
|
||||
@@ -130,3 +126,5 @@ class VersionSpecificResolutionTest extends InlineExpectationsTest {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import MakeTest<MergeTests<ResolutionTest, VersionSpecificResolutionTest>>
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
failures
|
||||
testFailures
|
||||
|
||||
@@ -18,12 +18,10 @@ private class ImmediateModuleRef extends DataFlow::Node {
|
||||
string getAsname() { result = alias }
|
||||
}
|
||||
|
||||
class ImportTest extends InlineExpectationsTest {
|
||||
ImportTest() { this = "ImportTest" }
|
||||
module ImportTest implements TestSig {
|
||||
string getARelevantTag() { result = "imports" }
|
||||
|
||||
override string getARelevantTag() { result = "imports" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(ImmediateModuleRef ref |
|
||||
tag = "imports" and
|
||||
location = ref.getLocation() and
|
||||
@@ -33,12 +31,10 @@ class ImportTest extends InlineExpectationsTest {
|
||||
}
|
||||
}
|
||||
|
||||
class AliasTest extends InlineExpectationsTest {
|
||||
AliasTest() { this = "AliasTest" }
|
||||
module AliasTest implements TestSig {
|
||||
string getARelevantTag() { result = "as" }
|
||||
|
||||
override string getARelevantTag() { result = "as" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(ImmediateModuleRef ref |
|
||||
tag = "as" and
|
||||
location = ref.getLocation() and
|
||||
@@ -47,3 +43,5 @@ class AliasTest extends InlineExpectationsTest {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import MakeTest<MergeTests<ImportTest, AliasTest>>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
failures
|
||||
testFailures
|
||||
debug_callableNotUnique
|
||||
pointsTo_found_typeTracker_notFound
|
||||
typeTracker_found_pointsTo_notFound
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
failures
|
||||
testFailures
|
||||
debug_callableNotUnique
|
||||
pointsTo_found_typeTracker_notFound
|
||||
typeTracker_found_pointsTo_notFound
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
failures
|
||||
testFailures
|
||||
debug_callableNotUnique
|
||||
pointsTo_found_typeTracker_notFound
|
||||
| code/class_attr_assign.py:10:9:10:27 | ControlFlowNode for Attribute() | my_func |
|
||||
|
||||
@@ -37,12 +37,10 @@ predicate typeTrackerClassCall(CallNode call, Function callable) {
|
||||
)
|
||||
}
|
||||
|
||||
class CallGraphTest extends InlineExpectationsTest {
|
||||
CallGraphTest() { this = "CallGraphTest" }
|
||||
module CallGraphTest implements TestSig {
|
||||
string getARelevantTag() { result in ["pt", "tt"] }
|
||||
|
||||
override string getARelevantTag() { result in ["pt", "tt"] }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(location.getFile().getRelativePath()) and
|
||||
exists(CallNode call, Function target |
|
||||
tag = "tt" and
|
||||
@@ -58,6 +56,8 @@ class CallGraphTest extends InlineExpectationsTest {
|
||||
}
|
||||
}
|
||||
|
||||
import MakeTest<CallGraphTest>
|
||||
|
||||
bindingset[call, target]
|
||||
string getCallEdgeValue(CallNode call, Function target) {
|
||||
if call.getLocation().getFile() = target.getLocation().getFile()
|
||||
|
||||
@@ -3,6 +3,7 @@ edges
|
||||
| UnsafeUnpack.py:5:26:5:32 | GSSA Variable request | UnsafeUnpack.py:11:18:11:24 | ControlFlowNode for request |
|
||||
| UnsafeUnpack.py:11:18:11:24 | ControlFlowNode for request | UnsafeUnpack.py:11:18:11:29 | ControlFlowNode for Attribute |
|
||||
| UnsafeUnpack.py:11:18:11:29 | ControlFlowNode for Attribute | UnsafeUnpack.py:11:18:11:49 | ControlFlowNode for Attribute() |
|
||||
| UnsafeUnpack.py:11:18:11:29 | ControlFlowNode for Attribute | UnsafeUnpack.py:17:27:17:38 | ControlFlowNode for Attribute |
|
||||
| UnsafeUnpack.py:11:18:11:49 | ControlFlowNode for Attribute() | UnsafeUnpack.py:17:27:17:38 | ControlFlowNode for Attribute |
|
||||
| UnsafeUnpack.py:17:27:17:38 | ControlFlowNode for Attribute | UnsafeUnpack.py:19:35:19:41 | ControlFlowNode for tarpath |
|
||||
| UnsafeUnpack.py:33:50:33:65 | ControlFlowNode for local_ziped_path | UnsafeUnpack.py:34:23:34:38 | ControlFlowNode for local_ziped_path |
|
||||
@@ -15,7 +16,9 @@ edges
|
||||
| UnsafeUnpack.py:103:23:103:27 | SSA variable chunk | UnsafeUnpack.py:105:35:105:42 | ControlFlowNode for savepath |
|
||||
| UnsafeUnpack.py:103:32:103:44 | ControlFlowNode for Attribute | UnsafeUnpack.py:103:32:103:54 | ControlFlowNode for Subscript |
|
||||
| UnsafeUnpack.py:103:32:103:54 | ControlFlowNode for Subscript | UnsafeUnpack.py:103:23:103:27 | SSA variable chunk |
|
||||
| UnsafeUnpack.py:108:22:108:34 | ControlFlowNode for Attribute | UnsafeUnpack.py:108:22:108:48 | ControlFlowNode for Attribute() |
|
||||
| UnsafeUnpack.py:108:22:108:34 | ControlFlowNode for Attribute | UnsafeUnpack.py:112:35:112:43 | ControlFlowNode for file_path |
|
||||
| UnsafeUnpack.py:108:22:108:48 | ControlFlowNode for Attribute() | UnsafeUnpack.py:112:35:112:43 | ControlFlowNode for file_path |
|
||||
| UnsafeUnpack.py:116:17:116:21 | SSA variable ufile | UnsafeUnpack.py:118:38:118:47 | ControlFlowNode for Attribute |
|
||||
| UnsafeUnpack.py:116:27:116:39 | ControlFlowNode for Attribute | UnsafeUnpack.py:116:17:116:21 | SSA variable ufile |
|
||||
| UnsafeUnpack.py:118:38:118:47 | ControlFlowNode for Attribute | UnsafeUnpack.py:120:41:120:58 | ControlFlowNode for uploaded_file_path |
|
||||
@@ -50,6 +53,7 @@ nodes
|
||||
| UnsafeUnpack.py:103:32:103:54 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| UnsafeUnpack.py:105:35:105:42 | ControlFlowNode for savepath | semmle.label | ControlFlowNode for savepath |
|
||||
| UnsafeUnpack.py:108:22:108:34 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| UnsafeUnpack.py:108:22:108:48 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| UnsafeUnpack.py:112:35:112:43 | ControlFlowNode for file_path | semmle.label | ControlFlowNode for file_path |
|
||||
| UnsafeUnpack.py:116:17:116:21 | SSA variable ufile | semmle.label | SSA variable ufile |
|
||||
| UnsafeUnpack.py:116:27:116:39 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
|
||||
@@ -3,19 +3,23 @@ edges
|
||||
| csv_bad.py:9:19:9:25 | GSSA Variable request | csv_bad.py:16:16:16:22 | ControlFlowNode for request |
|
||||
| csv_bad.py:9:19:9:25 | GSSA Variable request | csv_bad.py:24:16:24:22 | ControlFlowNode for request |
|
||||
| csv_bad.py:16:16:16:22 | ControlFlowNode for request | csv_bad.py:16:16:16:27 | ControlFlowNode for Attribute |
|
||||
| csv_bad.py:16:16:16:27 | ControlFlowNode for Attribute | csv_bad.py:18:24:18:31 | ControlFlowNode for csv_data |
|
||||
| csv_bad.py:16:16:16:27 | ControlFlowNode for Attribute | csv_bad.py:19:25:19:32 | ControlFlowNode for csv_data |
|
||||
| csv_bad.py:16:16:16:27 | ControlFlowNode for Attribute | csv_bad.py:16:16:16:38 | ControlFlowNode for Attribute() |
|
||||
| csv_bad.py:16:16:16:38 | ControlFlowNode for Attribute() | csv_bad.py:18:24:18:31 | ControlFlowNode for csv_data |
|
||||
| csv_bad.py:16:16:16:38 | ControlFlowNode for Attribute() | csv_bad.py:19:25:19:32 | ControlFlowNode for csv_data |
|
||||
| csv_bad.py:24:16:24:22 | ControlFlowNode for request | csv_bad.py:24:16:24:27 | ControlFlowNode for Attribute |
|
||||
| csv_bad.py:24:16:24:27 | ControlFlowNode for Attribute | csv_bad.py:25:46:25:53 | ControlFlowNode for csv_data |
|
||||
| csv_bad.py:24:16:24:27 | ControlFlowNode for Attribute | csv_bad.py:24:16:24:38 | ControlFlowNode for Attribute() |
|
||||
| csv_bad.py:24:16:24:38 | ControlFlowNode for Attribute() | csv_bad.py:25:46:25:53 | ControlFlowNode for csv_data |
|
||||
nodes
|
||||
| csv_bad.py:9:19:9:25 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember |
|
||||
| csv_bad.py:9:19:9:25 | GSSA Variable request | semmle.label | GSSA Variable request |
|
||||
| csv_bad.py:16:16:16:22 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| csv_bad.py:16:16:16:27 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| csv_bad.py:16:16:16:38 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| csv_bad.py:18:24:18:31 | ControlFlowNode for csv_data | semmle.label | ControlFlowNode for csv_data |
|
||||
| csv_bad.py:19:25:19:32 | ControlFlowNode for csv_data | semmle.label | ControlFlowNode for csv_data |
|
||||
| csv_bad.py:24:16:24:22 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||
| csv_bad.py:24:16:24:27 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| csv_bad.py:24:16:24:38 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| csv_bad.py:25:46:25:53 | ControlFlowNode for csv_data | semmle.label | ControlFlowNode for csv_data |
|
||||
subpaths
|
||||
#select
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
failures
|
||||
testFailures
|
||||
|
||||
@@ -3,22 +3,20 @@ import semmle.python.dataflow.new.DataFlow
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import semmle.python.ApiGraphs
|
||||
|
||||
class ApiUseTest extends InlineExpectationsTest {
|
||||
ApiUseTest() { this = "ApiUseTest" }
|
||||
|
||||
override string getARelevantTag() { result = "use" }
|
||||
module ApiUseTest implements TestSig {
|
||||
string getARelevantTag() { result = "use" }
|
||||
|
||||
private predicate relevant_node(API::Node a, DataFlow::Node n, Location l) {
|
||||
n = a.getAValueReachableFromSource() and l = n.getLocation()
|
||||
}
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(DataFlow::Node n | this.relevant_node(_, n, location) |
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(DataFlow::Node n | relevant_node(_, n, location) |
|
||||
tag = "use" and
|
||||
// Only report the longest path on this line:
|
||||
value =
|
||||
max(API::Node a2, Location l2 |
|
||||
this.relevant_node(a2, _, l2) and
|
||||
relevant_node(a2, _, l2) and
|
||||
l2.getFile() = location.getFile() and
|
||||
l2.getStartLine() = location.getStartLine()
|
||||
|
|
||||
@@ -28,3 +26,5 @@ class ApiUseTest extends InlineExpectationsTest {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import MakeTest<ApiUseTest>
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
failures
|
||||
testFailures
|
||||
| test.py:1:1:1:3 | foo | Tag mismatch: Actual result with tag 'foo' that is not part of getARelevantTag() |
|
||||
| test.py:4:1:4:3 | foo | Tag mismatch: Actual result with tag 'foo' that is not part of getARelevantTag() |
|
||||
|
||||
@@ -4,12 +4,10 @@
|
||||
import python
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
class MissingRelevantTag extends InlineExpectationsTest {
|
||||
MissingRelevantTag() { this = "MissingRelevantTag" }
|
||||
module MissingRelevantTag implements TestSig {
|
||||
string getARelevantTag() { none() }
|
||||
|
||||
override string getARelevantTag() { none() }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(Name name | name.getId() = "foo" |
|
||||
location = name.getLocation() and
|
||||
element = name.toString() and
|
||||
@@ -18,3 +16,5 @@ class MissingRelevantTag extends InlineExpectationsTest {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import MakeTest<MissingRelevantTag>
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
failures
|
||||
testFailures
|
||||
|
||||
@@ -2,12 +2,10 @@ import python
|
||||
import semmle.python.essa.SsaCompute
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
class UseTest extends InlineExpectationsTest {
|
||||
UseTest() { this = "UseTest" }
|
||||
module UseTest implements TestSig {
|
||||
string getARelevantTag() { result in ["use-use", "def-use", "def"] }
|
||||
|
||||
override string getARelevantTag() { result in ["use-use", "def-use", "def"] }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(location.getFile().getRelativePath()) and
|
||||
exists(string name | name in ["x", "y"] |
|
||||
exists(NameNode nodeTo, Location prevLoc |
|
||||
@@ -39,3 +37,5 @@ class UseTest extends InlineExpectationsTest {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import MakeTest<UseTest>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from flask import Flask, request
|
||||
from flask import Flask, request, render_template_string, stream_template_string
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route("/test_taint/<name>/<int:number>") # $routeSetup="/test_taint/<name>/<int:number>"
|
||||
@@ -215,7 +215,34 @@ def test_taint(name = "World!", number="0", foo="foo"): # $requestHandler route
|
||||
gd(), # $ tainted
|
||||
)
|
||||
|
||||
# ----------------------------------
|
||||
# non-request related taint-steps
|
||||
# ----------------------------------
|
||||
|
||||
# render_template_string
|
||||
source = TAINTED_STRING
|
||||
ensure_tainted(source) # $ tainted
|
||||
res = render_template_string(source)
|
||||
ensure_tainted(res) # $ tainted
|
||||
|
||||
# since template variables are auto-escaped, we don't treat result as tainted
|
||||
# see https://flask.palletsprojects.com/en/2.3.x/api/#flask.render_template_string
|
||||
res = render_template_string("Hello {{ foo }}", foo=TAINTED_STRING)
|
||||
ensure_not_tainted(res)
|
||||
|
||||
|
||||
# stream_template_string
|
||||
source = TAINTED_STRING
|
||||
ensure_tainted(source) # $ tainted
|
||||
res = stream_template_string(source)
|
||||
for x in res:
|
||||
ensure_tainted(x) # $ tainted
|
||||
|
||||
# since template variables are auto-escaped, we don't treat result as tainted
|
||||
# see https://flask.palletsprojects.com/en/2.3.x/api/#flask.stream_template_string
|
||||
res = stream_template_string("Hello {{ foo }}", foo=TAINTED_STRING)
|
||||
for x in res:
|
||||
ensure_not_tainted(x)
|
||||
|
||||
|
||||
@app.route("/debug/<foo>/<bar>", methods=['GET']) # $routeSetup="/debug/<foo>/<bar>"
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
failures
|
||||
testFailures
|
||||
|
||||
@@ -3,12 +3,10 @@ private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.frameworks.internal.PoorMansFunctionResolution
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
class InlinePoorMansFunctionResolutionTest extends InlineExpectationsTest {
|
||||
InlinePoorMansFunctionResolutionTest() { this = "InlinePoorMansFunctionResolutionTest" }
|
||||
module InlinePoorMansFunctionResolutionTest implements TestSig {
|
||||
string getARelevantTag() { result = "resolved" }
|
||||
|
||||
override string getARelevantTag() { result = "resolved" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(location.getFile().getRelativePath()) and
|
||||
exists(Function func, DataFlow::Node ref |
|
||||
ref = poorMansFunctionTracker(func) and
|
||||
@@ -26,3 +24,5 @@ class InlinePoorMansFunctionResolutionTest extends InlineExpectationsTest {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import MakeTest<InlinePoorMansFunctionResolutionTest>
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
WARNING: Module SensitiveData has been deprecated and may be removed in future (Sources.ql:4,6-19)
|
||||
| test.py:16:1:16:14 | test.py:16 | a call returning a password |
|
||||
| test.py:17:1:17:12 | test.py:17 | a call returning a password |
|
||||
| test.py:18:1:18:12 | test.py:18 | a call returning a secret |
|
||||
| test.py:19:1:19:19 | test.py:19 | a call returning a certificate or key |
|
||||
| test.py:20:1:20:12 | test.py:20 | a call returning an ID |
|
||||
@@ -1,5 +0,0 @@
|
||||
import python
|
||||
import semmle.python.security.SensitiveData
|
||||
|
||||
from SensitiveData::Source src
|
||||
select src.getLocation(), src.repr()
|
||||
@@ -1,21 +0,0 @@
|
||||
|
||||
from not_found import get_passwd, account_id
|
||||
|
||||
def get_password():
|
||||
pass
|
||||
|
||||
def get_secret():
|
||||
pass
|
||||
|
||||
def fetch_certificate():
|
||||
pass
|
||||
|
||||
def encrypt_password(pwd):
|
||||
pass
|
||||
|
||||
get_password()
|
||||
get_passwd()
|
||||
get_secret()
|
||||
fetch_certificate()
|
||||
account_id()
|
||||
safe_to_store = encrypt_password(pwd)
|
||||
@@ -1,5 +0,0 @@
|
||||
WARNING: Type HttpResponseTaintSink has been deprecated and may be removed in future (HttpResponseSinks.ql:5,6-27)
|
||||
| test.py:9:12:9:26 | bottle handler function result | externally controlled string |
|
||||
| test.py:13:12:13:24 | bottle handler function result | externally controlled string |
|
||||
| test.py:19:12:19:33 | bottle handler function result | externally controlled string |
|
||||
| test.py:36:21:36:51 | Taint sink | externally controlled string |
|
||||
@@ -1,7 +0,0 @@
|
||||
import python
|
||||
import semmle.python.web.HttpResponse
|
||||
import semmle.python.security.strings.Untrusted
|
||||
|
||||
from HttpResponseTaintSink sink, TaintKind kind
|
||||
where sink.sinks(kind)
|
||||
select sink, kind
|
||||
@@ -1,9 +0,0 @@
|
||||
WARNING: Type HttpRequestTaintSource has been deprecated and may be removed in future (HttpSources.ql:5,6-28)
|
||||
| ../../../query-tests/Security/lib/bottle.py:64:11:64:24 | LocalRequest() | bottle.request |
|
||||
| test.py:3:35:3:41 | ImportMember | bottle.request |
|
||||
| test.py:8:11:8:14 | name | externally controlled string |
|
||||
| test.py:12:9:12:12 | name | externally controlled string |
|
||||
| test.py:18:12:18:18 | request | bottle.request |
|
||||
| test.py:27:12:27:16 | where | externally controlled string |
|
||||
| test.py:32:14:32:20 | request | bottle.request |
|
||||
| test.py:36:34:36:40 | request | bottle.request |
|
||||
@@ -1,7 +0,0 @@
|
||||
import python
|
||||
import semmle.python.web.HttpRequest
|
||||
import semmle.python.security.strings.Untrusted
|
||||
|
||||
from HttpRequestTaintSource source, TaintKind kind
|
||||
where source.isSourceOf(kind)
|
||||
select source.(ControlFlowNode).getNode(), kind
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user