Docs: Update data flow documentation to the new API.

This commit is contained in:
Anders Schack-Mulligen
2023-07-13 09:21:12 +02:00
parent a0e96594d8
commit 2947f176ef
18 changed files with 352 additions and 431 deletions

View File

@@ -62,8 +62,8 @@ The library class ``SecurityOptions`` provides a (configurable) model of what co
import semmle.code.cpp.security.Security
class TaintedFormatConfig extends TaintTracking::Configuration {
override predicate isSource(DataFlow::Node source) {
module TaintedFormatConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
exists (SecurityOptions opts |
opts.isUserInput(source.asExpr(), _)
)
@@ -85,8 +85,8 @@ Use the ``FormattingFunction`` class to fill in the definition of ``isSink``.
import semmle.code.cpp.security.Security
class TaintedFormatConfig extends TaintTracking::Configuration {
override predicate isSink(DataFlow::Node sink) {
module TaintedFormatConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node sink) {
/* Fill me in */
}
...
@@ -105,8 +105,8 @@ Use the ``FormattingFunction`` class, we can write the sink as:
import semmle.code.cpp.security.Security
class TaintedFormatConfig extends TaintTracking::Configuration {
override predicate isSink(DataFlow::Node sink) {
module TaintedFormatConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node sink) {
exists (FormattingFunction ff, Call c |
c.getTarget() = ff and
c.getArgument(ff.getFormatParameterIndex()) = sink.asExpr()
@@ -132,9 +132,8 @@ Add an additional taint step that (heuristically) taints a local variable if it
.. code-block:: ql
class TaintedFormatConfig extends TaintTracking::Configuration {
override predicate isAdditionalTaintStep(DataFlow::Node pred,
DataFlow::Node succ) {
module TaintedFormatConfig implements DataFlow::ConfigSig {
predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
exists (Call c, Expr arg, LocalVariable lv |
arg = c.getAnArgument() and
arg = pred.asExpr() and
@@ -153,8 +152,8 @@ Add a sanitizer, stopping propagation at parameters of formatting functions, to
.. code-block:: ql
class TaintedFormatConfig extends TaintTracking::Configuration {
override predicate isSanitizer(DataFlow::Node nd) {
module TaintedFormatConfig implements DataFlow::ConfigSig {
predicate isBarrier(DataFlow::Node nd) {
exists (FormattingFunction ff, int idx |
idx = ff.getFormatParameterIndex() and
nd = DataFlow::parameterNode(ff.getParameter(idx))

View File

@@ -71,7 +71,7 @@ Finding the RCE yourself
**Hint**: Use ``Method.getDeclaringType()`` and ``Type.getASupertype()``
#. Implement a ``DataFlow::Configuration``, defining the source as the first parameter of a ``toObject`` method, and the sink as an instance of ``UnsafeDeserializationSink``.
#. Implement a ``DataFlow::ConfigSig``, defining the source as the first parameter of a ``toObject`` method, and the sink as an instance of ``UnsafeDeserializationSink``.
**Hint**: Use ``Node::asParameter()``
@@ -114,13 +114,13 @@ Model answer, step 3
* Configuration that tracks the flow of taint from the first parameter of
* `ContentTypeHandler.toObject` to an instance of unsafe deserialization.
*/
class StrutsUnsafeDeserializationConfig extends Configuration {
StrutsUnsafeDeserializationConfig() { this = "StrutsUnsafeDeserializationConfig" }
override predicate isSource(Node source) {
module StrutsUnsafeDeserializationConfig implements ConfigSig {
predicate isSource(Node source) {
source.asParameter() = any(ContentTypeHandlerDeserialization des).getParameter(0)
}
override predicate isSink(Node sink) { sink instanceof UnsafeDeserializationSink }
predicate isSink(Node sink) { sink instanceof UnsafeDeserializationSink }
}
module StrutsUnsafeDeserializationFlow = Global<StrutsUnsafeDeserializationConfig>;
Model answer, step 4
====================
@@ -129,9 +129,8 @@ Model answer, step 4
import PathGraph
...
from PathNode source, PathNode sink, StrutsUnsafeDeserializationConfig conf
where conf.hasFlowPath(source, sink)
and sink.getNode() instanceof UnsafeDeserializationSink
select sink.getNode().(UnsafeDeserializationSink).getMethodAccess(), source, sink, "Unsafe deserialization of $@.", source, "user input"
from PathNode source, PathNode sink
where StrutsUnsafeDeserializationFlow::flowPath(source, sink)
select sink.getNode().(UnsafeDeserializationSink).getMethodAccess(), source, sink, "Unsafe deserialization of $@.", source, "user input"
More full-featured version: https://github.com/github/securitylab/tree/main/CodeQL_Queries/java/Apache_Struts_CVE-2017-9805

View File

@@ -78,12 +78,12 @@ We want to look for method calls where the method name is ``getNamespace()``, an
import semmle.code.java.security.Security
class TaintedOGNLConfig extends TaintTracking::Configuration {
override predicate isSource(DataFlow::Node source) {
module TaintedOGNLConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
exists(Method m |
m.getName() = "getNamespace" and
m.getDeclaringType().getName() = "ActionProxy" and
source.asExpr() = m.getAReference()
m.getName() = "getNamespace" and
m.getDeclaringType().getName() = "ActionProxy" and
source.asExpr() = m.getAReference()
)
}
...
@@ -105,8 +105,8 @@ Fill in the definition of ``isSink``.
import semmle.code.java.security.Security
class TaintedOGNLConfig extends TaintTracking::Configuration {
override predicate isSink(DataFlow::Node sink) {
module TaintedOGNLConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node sink) {
/* Fill me in */
}
...
@@ -125,9 +125,9 @@ Find a method access to ``compileAndExecute``, and mark the first argument.
import semmle.code.java.security.Security
class TaintedOGNLConfig extends TaintTracking::Configuration {
override predicate isSink(DataFlow::Node sink) {
exists(MethodAccess ma |
module TaintedOGNLConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node sink) {
exists(MethodAccess ma |
ma.getMethod().getName() = "compileAndExecute" and
ma.getArgument(0) = sink.asExpr()
)
@@ -148,8 +148,8 @@ A sanitizer allows us to *prevent* flow through a particular node in the graph.
.. code-block:: ql
class TaintedOGNLConfig extends TaintTracking::Configuration {
override predicate isSanitizer(DataFlow::Node nd) {
module TaintedOGNLConfig implements DataFlow::ConfigSig {
predicate isBarrier(DataFlow::Node nd) {
nd.getEnclosingCallable()
.getDeclaringType()
.getName() = "ValueStackShadowMap"
@@ -164,9 +164,8 @@ Add an additional taint step that (heuristically) taints a local variable if it
.. code-block:: ql
class TaintedOGNLConfig extends TaintTracking::Configuration {
override predicate isAdditionalTaintStep(DataFlow::Node node1,
DataFlow::Node node2) {
module TaintedOGNLConfig implements DataFlow::ConfigSig {
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(Field f, RefType t |
node1.asExpr() = f.getAnAssignedValue() and
node2.asExpr() = f.getAnAccess() and

View File

@@ -1,14 +1,14 @@
import cpp
import semmle.code.cpp.dataflow.TaintTracking
class TaintedFormatConfig extends TaintTracking::Configuration {
TaintedFormatConfig() { this = "TaintedFormatConfig" }
module TaintedFormatConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { /* TBD */ }
override predicate isSource(DataFlow::Node source) { /* TBD */ }
override predicate isSink(DataFlow::Node sink) { /* TBD */ }
predicate isSink(DataFlow::Node sink) { /* TBD */ }
}
from TaintedFormatConfig cfg, DataFlow::Node source, DataFlow::Node sink
where cfg.hasFlow(source, sink)
module TaintedFormatFlow = TaintTracking::Global<TaintedFormatConfig>;
from DataFlow::Node source, DataFlow::Node sink
where TaintedFormatFlow::flow(source, sink)
select sink, "This format string may be derived from a $@.", source, "user-controlled value"

View File

@@ -1,14 +1,14 @@
import java
import semmle.code.java.dataflow.TaintTracking
class TaintedOGNLConfig extends TaintTracking::Configuration {
TaintedOGNLConfig() { this = "TaintedOGNLConfig" }
module TaintedOGNLConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { /* TBD */ }
override predicate isSource(DataFlow::Node source) { /* TBD */ }
override predicate isSink(DataFlow::Node sink) { /* TBD */ }
predicate isSink(DataFlow::Node sink) { /* TBD */ }
}
from TaintedOGNLConfig cfg, DataFlow::Node source, DataFlow::Node sink
where cfg.hasFlow(source, sink)
module TaintedOGNLFlow = TaintTracking::Global<TaintedOGNLConfig>;
from DataFlow::Node source, DataFlow::Node sink
where TaintedOGNLFlow::flow(source, sink)
select source, "This untrusted input is evaluated as an OGNL expression $@.", sink, "here"

View File

@@ -34,18 +34,18 @@ Global taint tracking library
The ``semmle.code.<language>.dataflow.TaintTracking`` library provides a framework for implementing solvers for global taint tracking problems:
#. Subclass ``TaintTracking::Configuration`` following this template:
#. Implement ``DataFlow::ConfigSig`` and use ``TaintTracking::Global`` following this template:
.. code-block:: ql
class Config extends TaintTracking::Configuration {
Config() { this = "<some unique identifier>" }
override predicate isSource(DataFlow::Node nd) { ... }
override predicate isSink(DataFlow::Node nd) { ... }
module Config implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node nd) { ... }
predicate isSink(DataFlow::Node nd) { ... }
}
module Flow = TaintTracking::Global<Config>;
#. Use ``Config.hasFlow(source, sink)`` to find inter-procedural paths.
#. Use ``Flow::flow(source, sink)`` to find inter-procedural paths.
.. note::
In addition to the taint tracking configuration described here, there is also an equivalent *data flow* configuration in ``semmle.code.<language>.dataflow.DataFlow``, ``DataFlow::Configuration``. Data flow configurations are used to track whether the exact value produced by a source is used by a sink, whereas taint tracking configurations are used to determine whether the source may influence the value used at the sink. Whether you use taint tracking or data flow depends on the analysis problem you are trying to solve.
In addition to the taint tracking flow configuration described here, there is also an equivalent *data flow* in ``semmle.code.<language>.dataflow.DataFlow``, ``DataFlow::Global<DataFlow::ConfigSig>``. Data flow is used to track whether the exact value produced by a source is used by a sink, whereas taint tracking is used to determine whether the source may influence the value used at the sink. Whether you use taint tracking or data flow depends on the analysis problem you are trying to solve.

View File

@@ -13,12 +13,16 @@ Use this template:
*/
import semmle.code.<language>.dataflow.TaintTracking
import DataFlow::PathGraph
...
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
module Flow = TaintTracking::Global<Configuration>;
import Flow::PathGraph
from Flow::PathNode source, Flow::PathNode sink
where Flow::flowPath(source, sink)
select sink, source, sink, "<message>"
.. note::
To see the paths between the source and the sinks, we can convert the query to a path problem query. There are a few minor changes that need to be made for this to workwe need an additional import, to specify ``PathNode`` rather than ``Node``, and to add the source/sink to the query output (so that we can automatically determine the paths).
To see the paths between the source and the sinks, we can convert the query to a path problem query. There are a few minor changes that need to be made for this to workwe need an additional import, to specify ``PathNode`` rather than ``Node``, and to add the source/sink to the query output (so that we can automatically determine the paths).