diff --git a/docs/codeql/codeql-language-guides/codeql-library-for-javascript.rst b/docs/codeql/codeql-language-guides/codeql-library-for-javascript.rst index 6742dfa8e76..d39da11411a 100644 --- a/docs/codeql/codeql-language-guides/codeql-library-for-javascript.rst +++ b/docs/codeql/codeql-language-guides/codeql-library-for-javascript.rst @@ -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; + +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 ~~~~~~~~~~~~~ diff --git a/javascript/ql/test/tutorials/Introducing the JavaScript libraries/query17.qll b/javascript/ql/test/tutorials/Introducing the JavaScript libraries/query17.qll index 1de380f710b..e6dff623e97 100644 --- a/javascript/ql/test/tutorials/Introducing the JavaScript libraries/query17.qll +++ b/javascript/ql/test/tutorials/Introducing the JavaScript libraries/query17.qll @@ -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; + 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." ) }