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``.
|
||||
|
||||
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.
|
||||
- ``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.
|
||||
- ``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;
|
||||
- ``isBarrier(DataFlow::Node nd)`` selects all nodes ``nd`` that act as a barrier/sanitizer for data flow.
|
||||
- ``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).
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
class PasswordTracker extends DataFlow::Configuration {
|
||||
PasswordTracker() {
|
||||
// unique identifier for this configuration
|
||||
this = "PasswordTracker"
|
||||
}
|
||||
module PasswordConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node nd) { nd.asExpr() instanceof StringLiteral }
|
||||
|
||||
override predicate isSource(DataFlow::Node 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"
|
||||
}
|
||||
predicate isSink(DataFlow::Node nd) { passwordVarAssign(_, nd) }
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
from PasswordTracker pt, DataFlow::Node source, DataFlow::Node sink, Variable v
|
||||
where pt.hasFlow(source, sink) and pt.passwordVarAssign(v, sink)
|
||||
from DataFlow::Node source, DataFlow::Node sink, Variable v
|
||||
where PasswordFlow::flow(_, sink) and passwordVarAssign(v, sink)
|
||||
select sink, "Password variable " + v + " is assigned a constant string."
|
||||
|
||||
|
||||
Syntax errors
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
import javascript
|
||||
|
||||
class PasswordTracker extends DataFlow::Configuration {
|
||||
PasswordTracker() {
|
||||
// unique identifier for this configuration
|
||||
this = "PasswordTracker"
|
||||
}
|
||||
module PasswordConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node nd) { nd.asExpr() instanceof StringLiteral }
|
||||
|
||||
override predicate isSource(DataFlow::Node nd) { nd.asExpr() instanceof StringLiteral }
|
||||
|
||||
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 isSink(DataFlow::Node nd) { passwordVarAssign(_, nd) }
|
||||
}
|
||||
|
||||
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) {
|
||||
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."
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user