mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
JS: Update query17 from intro tutorial
This commit is contained in:
@@ -700,19 +700,16 @@ The data flow graph-based analyses described so far are all intraprocedural: the
|
|||||||
|
|
||||||
We distinguish here between data flow proper, and *taint tracking*: the latter not only considers value-preserving flow (such as from variable definitions to uses), but also cases where one value influences ("taints") another without determining it entirely. For example, in the assignment ``s2 = s1.substring(i)``, the value of ``s1`` influences the value of ``s2``, because ``s2`` is assigned a substring of ``s1``. In general, ``s2`` will not be assigned ``s1`` itself, so there is no data flow from ``s1`` to ``s2``, but ``s1`` still taints ``s2``.
|
We distinguish here between data flow proper, and *taint tracking*: the latter not only considers value-preserving flow (such as from variable definitions to uses), but also cases where one value influences ("taints") another without determining it entirely. For example, in the assignment ``s2 = s1.substring(i)``, the value of ``s1`` influences the value of ``s2``, because ``s2`` is assigned a substring of ``s1``. In general, ``s2`` will not be assigned ``s1`` itself, so there is no data flow from ``s1`` to ``s2``, but ``s1`` still taints ``s2``.
|
||||||
|
|
||||||
It is a common pattern that we wish to specify data flow or taint analysis in terms of its *sources* (where flow starts), *sinks* (where it should be tracked), and *barriers* or *sanitizers* (where flow is interrupted). Sanitizers they are very common in security analyses: for example, an analysis that tracks the flow of untrusted user input into, say, a SQL query has to keep track of code that validates the input, thereby making it safe to use. Such a validation step is an example of a sanitizer.
|
It is a common pattern that we wish to specify data flow or taint analysis in terms of its *sources* (where flow starts), *sinks* (where it should be tracked), and *barriers* (also called *sanitizers*) where flow is interrupted. Sanitizers they are very common in security analyses: for example, an analysis that tracks the flow of untrusted user input into, say, a SQL query has to keep track of code that validates the input, thereby making it safe to use. Such a validation step is an example of a sanitizer.
|
||||||
|
|
||||||
The classes ``DataFlow::Configuration`` and ``TaintTracking::Configuration`` allow specifying a data flow or taint analysis, respectively, by overriding the following predicates:
|
A module implementing the signature `DataFlow::ConfigSig` may specify a data flow or taint analysis by implementing the following predicates:
|
||||||
|
|
||||||
- ``isSource(DataFlow::Node nd)`` selects all nodes ``nd`` from where flow tracking starts.
|
- ``isSource(DataFlow::Node nd)`` selects all nodes ``nd`` from where flow tracking starts.
|
||||||
- ``isSink(DataFlow::Node nd)`` selects all nodes ``nd`` to which the flow is tracked.
|
- ``isSink(DataFlow::Node nd)`` selects all nodes ``nd`` to which the flow is tracked.
|
||||||
- ``isBarrier(DataFlow::Node nd)`` selects all nodes ``nd`` that act as a barrier for data flow; ``isSanitizer`` is the corresponding predicate for taint tracking configurations.
|
- ``isBarrier(DataFlow::Node nd)`` selects all nodes ``nd`` that act as a barrier/sanitizer for data flow.
|
||||||
- ``isBarrierEdge(DataFlow::Node src, DataFlow::Node trg)`` is a variant of ``isBarrier(nd)`` that allows specifying barrier *edges* in addition to barrier nodes; again, ``isSanitizerEdge`` is the corresponding predicate for taint tracking;
|
|
||||||
- ``isAdditionalFlowStep(DataFlow::Node src, DataFlow::Node trg)`` allows specifying custom additional flow steps for this analysis; ``isAdditionalTaintStep`` is the corresponding predicate for taint tracking configurations.
|
- ``isAdditionalFlowStep(DataFlow::Node src, DataFlow::Node trg)`` allows specifying custom additional flow steps for this analysis; ``isAdditionalTaintStep`` is the corresponding predicate for taint tracking configurations.
|
||||||
|
|
||||||
Since for technical reasons both ``Configuration`` classes are subtypes of ``string``, you have to choose a unique name for each flow configuration and equate ``this`` with it in the characteristic predicate (as in the example below).
|
Such a module can be passed to ``DataFlow::Global<...>``. This will produce a module with a ``flow`` predicate that performs the actual flow tracking, starting at a source and looking for flow to a sink that does not pass through a barrier node.
|
||||||
|
|
||||||
The predicate ``Configuration.hasFlow`` performs the actual flow tracking, starting at a source and looking for flow to a sink that does not pass through a barrier node or edge.
|
|
||||||
|
|
||||||
For example, suppose that we are developing an analysis to find hard-coded passwords. We might write a simple query that looks for string constants flowing into variables named ``"password"``.
|
For example, suppose that we are developing an analysis to find hard-coded passwords. We might write a simple query that looks for string constants flowing into variables named ``"password"``.
|
||||||
|
|
||||||
@@ -720,35 +717,27 @@ For example, suppose that we are developing an analysis to find hard-coded passw
|
|||||||
|
|
||||||
import javascript
|
import javascript
|
||||||
|
|
||||||
class PasswordTracker extends DataFlow::Configuration {
|
module PasswordConfig implements DataFlow::ConfigSig {
|
||||||
PasswordTracker() {
|
predicate isSource(DataFlow::Node nd) { nd.asExpr() instanceof StringLiteral }
|
||||||
// unique identifier for this configuration
|
|
||||||
this = "PasswordTracker"
|
|
||||||
}
|
|
||||||
|
|
||||||
override predicate isSource(DataFlow::Node nd) {
|
predicate isSink(DataFlow::Node nd) { passwordVarAssign(_, nd) }
|
||||||
nd.asExpr() instanceof StringLiteral
|
|
||||||
}
|
|
||||||
|
|
||||||
override predicate isSink(DataFlow::Node nd) {
|
|
||||||
passwordVarAssign(_, nd)
|
|
||||||
}
|
|
||||||
|
|
||||||
predicate passwordVarAssign(Variable v, DataFlow::Node nd) {
|
|
||||||
v.getAnAssignedExpr() = nd.asExpr() and
|
|
||||||
v.getName().toLowerCase() = "password"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Now we can rephrase our query to use ``Configuration.hasFlow``:
|
predicate passwordVarAssign(Variable v, DataFlow::Node nd) {
|
||||||
|
v.getAnAssignedExpr() = nd.asExpr() and
|
||||||
|
v.getName().toLowerCase() = "password"
|
||||||
|
}
|
||||||
|
|
||||||
|
module PasswordFlow = DataFlow::Global<PasswordConfig>;
|
||||||
|
|
||||||
|
Now we can rephrase our query to use ``PasswordFlow::flow``:
|
||||||
|
|
||||||
.. code-block:: ql
|
.. code-block:: ql
|
||||||
|
|
||||||
from PasswordTracker pt, DataFlow::Node source, DataFlow::Node sink, Variable v
|
from DataFlow::Node source, DataFlow::Node sink, Variable v
|
||||||
where pt.hasFlow(source, sink) and pt.passwordVarAssign(v, sink)
|
where PasswordFlow::flow(_, sink) and passwordVarAssign(v, sink)
|
||||||
select sink, "Password variable " + v + " is assigned a constant string."
|
select sink, "Password variable " + v + " is assigned a constant string."
|
||||||
|
|
||||||
|
|
||||||
Syntax errors
|
Syntax errors
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|||||||
@@ -1,23 +1,20 @@
|
|||||||
import javascript
|
import javascript
|
||||||
|
|
||||||
class PasswordTracker extends DataFlow::Configuration {
|
module PasswordConfig implements DataFlow::ConfigSig {
|
||||||
PasswordTracker() {
|
predicate isSource(DataFlow::Node nd) { nd.asExpr() instanceof StringLiteral }
|
||||||
// unique identifier for this configuration
|
|
||||||
this = "PasswordTracker"
|
|
||||||
}
|
|
||||||
|
|
||||||
override predicate isSource(DataFlow::Node nd) { nd.asExpr() instanceof StringLiteral }
|
predicate isSink(DataFlow::Node nd) { passwordVarAssign(_, nd) }
|
||||||
|
|
||||||
override predicate isSink(DataFlow::Node nd) { this.passwordVarAssign(_, nd) }
|
|
||||||
|
|
||||||
predicate passwordVarAssign(Variable v, DataFlow::Node nd) {
|
|
||||||
v.getAnAssignedExpr() = nd.asExpr() and
|
|
||||||
v.getName().toLowerCase() = "password"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
predicate passwordVarAssign(Variable v, DataFlow::Node nd) {
|
||||||
|
v.getAnAssignedExpr() = nd.asExpr() and
|
||||||
|
v.getName().toLowerCase() = "password"
|
||||||
|
}
|
||||||
|
|
||||||
|
module PasswordFlow = DataFlow::Global<PasswordConfig>;
|
||||||
|
|
||||||
query predicate test_query17(DataFlow::Node sink, string res) {
|
query predicate test_query17(DataFlow::Node sink, string res) {
|
||||||
exists(PasswordTracker pt, Variable v | pt.hasFlow(_, sink) and pt.passwordVarAssign(v, sink) |
|
exists(Variable v | PasswordFlow::flow(_, sink) and passwordVarAssign(v, sink) |
|
||||||
res = "Password variable " + v.toString() + " is assigned a constant string."
|
res = "Password variable " + v.toString() + " is assigned a constant string."
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user