mirror of
https://github.com/github/codeql.git
synced 2025-12-17 01:03:14 +01:00
JS: Remove mention of TrackedNode in docs
This commit is contained in:
@@ -700,28 +700,7 @@ 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``.
|
||||
|
||||
The simplest way of implementing an interprocedural data flow analysis is to extend either class ``DataFlow::TrackedNode`` or ``DataFlow::TrackedExpr``. The former is a subclass of ``DataFlow::Node``, the latter of ``Expr``, and extending them ensures that the newly added values are tracked interprocedurally. You can use the predicate ``flowsTo`` to find out which nodes/expressions the tracked value flows to.
|
||||
|
||||
For example, suppose that we are developing an analysis to find hard-coded passwords. We might start by writing a simple query that looks for string constants flowing into variables named ``"password"``. To do this, we can extend ``TrackedExpr`` to track all constant strings, ``flowsTo`` to find cases where such a string flows into a (SSA) definition of a password variable:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
import javascript
|
||||
|
||||
class TrackedStringLiteral extends DataFlow::TrackedNode {
|
||||
TrackedStringLiteral() {
|
||||
this.asExpr() instanceof ConstantString
|
||||
}
|
||||
}
|
||||
|
||||
from TrackedStringLiteral source, DataFlow::Node sink, SsaExplicitDefinition def
|
||||
where source.flowsTo(sink) and sink = DataFlow::ssaDefinitionNode(def) and
|
||||
def.getSourceVariable().getName().toLowerCase() = "password"
|
||||
select sink
|
||||
|
||||
Note that ``TrackedNode`` and ``TrackedExpr`` do not restrict the set of "sinks" for the inter-procedural flow analysis, tracking flow into any expression that they might flow to. This can be expensive for large code bases, and is often unnecessary, since usually you are only interested in flow to a particular set of sinks. For example, the above query only looks for flow into assignments to password variables.
|
||||
|
||||
This is a particular instance of a general pattern, whereby we want to specify a 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). The example does not include any sanitizers, but 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* 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.
|
||||
|
||||
The classes ``DataFlow::Configuration`` and ``TaintTracking::Configuration`` allow specifying a data flow or taint analysis, respectively, by overriding the following predicates:
|
||||
|
||||
@@ -735,10 +714,12 @@ Since for technical reasons both ``Configuration`` classes are subtypes of ``str
|
||||
|
||||
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.
|
||||
|
||||
To continue with our above example, we can phrase it as a data flow configuration as follows:
|
||||
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"``.
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
import javascript
|
||||
|
||||
class PasswordTracker extends DataFlow::Configuration {
|
||||
PasswordTracker() {
|
||||
// unique identifier for this configuration
|
||||
@@ -754,11 +735,8 @@ To continue with our above example, we can phrase it as a data flow configuratio
|
||||
}
|
||||
|
||||
predicate passwordVarAssign(Variable v, DataFlow::Node nd) {
|
||||
exists (SsaExplicitDefinition def |
|
||||
nd = DataFlow::ssaDefinitionNode(def) and
|
||||
def.getSourceVariable() = v and
|
||||
v.getName().toLowerCase() = "password"
|
||||
)
|
||||
v.getAnAssignedExpr() = nd.asExpr() and
|
||||
v.getName().toLowerCase() = "password"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -770,7 +748,6 @@ Now we can rephrase our query to use ``Configuration.hasFlow``:
|
||||
where pt.hasFlow(source, sink) and pt.passwordVarAssign(v, sink)
|
||||
select sink, "Password variable " + v + " is assigned a constant string."
|
||||
|
||||
Note that while analyses implemented in this way are inter-procedural in that they track flow and taint across function calls and returns, flow through global variables is not tracked. Flow through object properties is only tracked in limited cases, for example through properties of object literals or CommonJS ``module`` and ``exports`` objects.
|
||||
|
||||
Syntax errors
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
@@ -11,11 +11,8 @@ class PasswordTracker extends DataFlow::Configuration {
|
||||
override predicate isSink(DataFlow::Node nd) { this.passwordVarAssign(_, nd) }
|
||||
|
||||
predicate passwordVarAssign(Variable v, DataFlow::Node nd) {
|
||||
exists(SsaExplicitDefinition def |
|
||||
nd = DataFlow::ssaDefinitionNode(def) and
|
||||
def.getSourceVariable() = v and
|
||||
v.getName().toLowerCase() = "password"
|
||||
)
|
||||
v.getAnAssignedExpr() = nd.asExpr() and
|
||||
v.getName().toLowerCase() = "password"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ test_query4
|
||||
| tst.js:29:1:29:5 | 1 + 2 | This expression should be bracketed to clarify precedence rules. |
|
||||
test_query19
|
||||
test_query17
|
||||
| tst.js:38:18:38:23 | "blah" | Password variable password is assigned a constant string. |
|
||||
test_query18
|
||||
| m.js:1:1:3:0 | <toplevel> | 0 |
|
||||
test_query8
|
||||
@@ -18,6 +19,7 @@ test_query11
|
||||
| tst.js:31:12:31:12 | x | Dead store of local variable. |
|
||||
| tst.js:31:15:31:15 | y | Dead store of local variable. |
|
||||
| tst.js:31:18:31:18 | x | Dead store of local variable. |
|
||||
| tst.js:38:7:38:23 | password = "blah" | Dead store of local variable. |
|
||||
test_query12
|
||||
test_query20
|
||||
test_query3
|
||||
|
||||
@@ -32,4 +32,8 @@ function l(x, y, x) {
|
||||
for (i=0;i<10;++i);
|
||||
}
|
||||
|
||||
var j, j;
|
||||
var j, j;
|
||||
|
||||
function foo() {
|
||||
var password = "blah";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user