mirror of
https://github.com/github/codeql.git
synced 2026-04-26 09:15:12 +02:00
Docs: Update data flow documentation to the new API.
This commit is contained in:
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 work–we 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 work–we 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).
|
||||
|
||||
Reference in New Issue
Block a user