Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ The class `ASTNode <https://codeql.github.com/codeql-standard-libraries/javascri
.. pull-quote::

Note

These predicates should only be used to perform generic AST traversal. To access children of specific AST node types, the specialized predicates introduced below should be used instead. In particular, queries should not rely on the numeric indices of child nodes relative to their parent nodes: these are considered an implementation detail that may change between versions of the library.

Top-levels
Expand Down Expand Up @@ -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:

Expand All @@ -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
Expand All @@ -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"
}
}

Expand All @@ -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
~~~~~~~~~~~~~
Expand All @@ -794,7 +771,7 @@ The ``semmle.javascript.frameworks.AngularJS`` library provides support for work
HTTP framework libraries
^^^^^^^^^^^^^^^^^^^^^^^^

The library ``semmle.javacript.frameworks.HTTP`` provides classes modeling common concepts from various HTTP frameworks.
The library ``semmle.javacript.frameworks.HTTP`` provides classes modeling common concepts from various HTTP frameworks.

Currently supported frameworks are `Express <https://expressjs.com/>`__, the standard Node.js ``http`` and ``https`` modules, `Connect <https://github.com/senchalabs/connect>`__, `Koa <https://koajs.com>`__, `Hapi <https://hapi.dev/>`__ and `Restify <http://restify.com/>`__.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}