From bf9197cb57db36e20739595818f01289ade831c9 Mon Sep 17 00:00:00 2001 From: Michael Hohn Date: Tue, 28 Nov 2023 17:04:57 -0800 Subject: [PATCH] Add def-use code sample and query --- cruft/snapshot-query-1 | 36 +++++++++++ cruft/snapshot-setup | 3 +- solutions/DefUseSample.ql | 80 ++++++++++++++++++++++++ tests/DefUseSample/DefUseSample.expected | 30 +++++++++ tests/DefUseSample/DefUseSample.qlref | 1 + tests/DefUseSample/sample-utility-0.js | 23 +++++++ tests/DefUseSample/sample-utility-1.js | 17 +++++ 7 files changed, 189 insertions(+), 1 deletion(-) create mode 100755 cruft/snapshot-query-1 create mode 100644 solutions/DefUseSample.ql create mode 100644 tests/DefUseSample/DefUseSample.expected create mode 100644 tests/DefUseSample/DefUseSample.qlref create mode 100644 tests/DefUseSample/sample-utility-0.js create mode 100644 tests/DefUseSample/sample-utility-1.js diff --git a/cruft/snapshot-query-1 b/cruft/snapshot-query-1 new file mode 100755 index 0000000..92826cd --- /dev/null +++ b/cruft/snapshot-query-1 @@ -0,0 +1,36 @@ +#!/bin/bash + +usage="Take a snapshot of the current session1.ql + +Usage: + $0 TheSnapShotName +" +if [ $# -ne 1 ] +then + echo "$usage" + exit 1 +fi + +if [ "$1" = "-h" ] +then + echo "$usage" + exit 1 +fi + +qname=$1 +# create the tests +mkdir -p tests/$qname +touch tests/$qname/$qname.expected +echo $qname.ql > tests/$qname/$qname.qlref +git add tests/$qname/$qname.expected +git add tests/$qname/$qname.qlref + +# snapshot the session +cp sample-utility-0.js tests/$qname/ +git add tests/$qname/sample-utility-0.js + +cp sample-utility-1.js tests/$qname/ +git add tests/$qname/sample-utility-1.js + +cp session/session1.ql solutions/$qname.ql +git add solutions/$qname.ql diff --git a/cruft/snapshot-setup b/cruft/snapshot-setup index 6fa2556..7807d0e 100755 --- a/cruft/snapshot-setup +++ b/cruft/snapshot-setup @@ -11,5 +11,6 @@ then fi mkdir -p tests mkdir -p solutions -echo 'import javascript' > session.ql +echo 'import javascript' > session/session.ql +echo 'import javascript' > session/session1.ql diff --git a/solutions/DefUseSample.ql b/solutions/DefUseSample.ql new file mode 100644 index 0000000..0c97167 --- /dev/null +++ b/solutions/DefUseSample.ql @@ -0,0 +1,80 @@ +/** + * @kind path-problem + * @problem.severity warning + * @id javascript/example/multiflow + */ + +import javascript +// XX: debug flow query +// import semmle.javascript.explore.ForwardDataFlow +import DataFlow::PathGraph + +// Flow to consider: +// +// var value = this.getParameter('value'); //: source 1 +// var ua = new GR('status'); //: source 2 +// ua.setValue('status',value); //: taint step +// ua.update(); //: sink (if from source 2) + +// var value = this.getParameter('value'); //: source 1 +class ParameterSource extends CallExpr { + ParameterSource() { + exists(Expr inst | + this.getCalleeName() = "getParameter" and + (this.getReceiver().(ThisExpr) = inst or this.getReceiver().(Identifier) = inst) + ) + } +} + +// ua.setValue('status',value); //: taint step +predicate setValueTaintStep(DataFlow::Node pred, DataFlow::Node succ) { + exists(DotExpr temp, MethodCallExpr mce, VarAccess gr, VarAccess postgr | + temp.getPropertyName() = "setValue" and + mce.getReceiver() = temp.getBase() and + gr = mce.getReceiver() and + pred.asExpr() = mce.getArgument(1) and + // + // Taint all accesses after setValue call. + // Trying data flow, this would be: + // succ = gr.flow().getASuccessor+() and + // + // Using control flow: + gr.getASuccessor+() = postgr and + succ.asExpr() = postgr + ) +} + +// ua.update(); //: sink (if from source 2) +DotExpr updateExpression() { result.getPropertyName() = "update" } + +VarRef recordUpdate() { result = updateExpression().getBase() } + +// var ua = new GR('status'); //: source 2 +class GR extends NewExpr { + GR() { this.getCalleeName() = "GR" } +} + +class FromRequestToGrUpdate extends TaintTracking::Configuration { + FromRequestToGrUpdate() { this = "FromRequestToGrUpdate" } + + override predicate isSource(DataFlow::Node source) { + exists(ParameterSource getParameter | source.asExpr() = getParameter) + } + + override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { + setValueTaintStep(pred, succ) + } + + override predicate isSink(DataFlow::Node sink) { + exists(VarRef grUpdate | + sink.asExpr() = recordUpdate() and + grUpdate = sink.asExpr() and + grUpdate.getName() = "ua" + ) + } +} + +from FromRequestToGrUpdate dataflow, DataFlow::PathNode source, DataFlow::PathNode sink +where dataflow.hasFlowPath(source, sink) +select sink, source, sink, "Data flow from $@ to $@.", source, source.toString(), sink, + sink.toString() diff --git a/tests/DefUseSample/DefUseSample.expected b/tests/DefUseSample/DefUseSample.expected new file mode 100644 index 0000000..1acd7c2 --- /dev/null +++ b/tests/DefUseSample/DefUseSample.expected @@ -0,0 +1,30 @@ +WARNING: Unused class GR (/Users/hohn/local/codeql-javascript-multiflow/solutions/DefUseSample.ql:53,7-9) +nodes +| sample-utility-0.js:5:6:5:39 | value | +| sample-utility-0.js:5:14:5:39 | this.ge ... value') | +| sample-utility-0.js:5:14:5:39 | this.ge ... value') | +| sample-utility-0.js:17:27:17:31 | value | +| sample-utility-0.js:18:6:18:7 | ua | +| sample-utility-0.js:18:6:18:7 | ua | +| sample-utility-1.js:2:9:2:42 | value | +| sample-utility-1.js:2:17:2:42 | this.ge ... value') | +| sample-utility-1.js:2:17:2:42 | this.ge ... value') | +| sample-utility-1.js:14:23:14:27 | value | +| sample-utility-1.js:15:2:15:3 | ua | +| sample-utility-1.js:15:2:15:3 | ua | +edges +| sample-utility-0.js:5:6:5:39 | value | sample-utility-0.js:17:27:17:31 | value | +| sample-utility-0.js:5:14:5:39 | this.ge ... value') | sample-utility-0.js:5:6:5:39 | value | +| sample-utility-0.js:5:14:5:39 | this.ge ... value') | sample-utility-0.js:5:6:5:39 | value | +| sample-utility-0.js:17:27:17:31 | value | sample-utility-0.js:17:27:17:31 | value | +| sample-utility-0.js:17:27:17:31 | value | sample-utility-0.js:18:6:18:7 | ua | +| sample-utility-0.js:17:27:17:31 | value | sample-utility-0.js:18:6:18:7 | ua | +| sample-utility-1.js:2:9:2:42 | value | sample-utility-1.js:14:23:14:27 | value | +| sample-utility-1.js:2:17:2:42 | this.ge ... value') | sample-utility-1.js:2:9:2:42 | value | +| sample-utility-1.js:2:17:2:42 | this.ge ... value') | sample-utility-1.js:2:9:2:42 | value | +| sample-utility-1.js:14:23:14:27 | value | sample-utility-1.js:14:23:14:27 | value | +| sample-utility-1.js:14:23:14:27 | value | sample-utility-1.js:15:2:15:3 | ua | +| sample-utility-1.js:14:23:14:27 | value | sample-utility-1.js:15:2:15:3 | ua | +#select +| sample-utility-0.js:18:6:18:7 | ua | sample-utility-0.js:5:14:5:39 | this.ge ... value') | sample-utility-0.js:18:6:18:7 | ua | Data flow from $@ to $@. | sample-utility-0.js:5:14:5:39 | this.ge ... value') | this.ge ... value') | sample-utility-0.js:18:6:18:7 | ua | ua | +| sample-utility-1.js:15:2:15:3 | ua | sample-utility-1.js:2:17:2:42 | this.ge ... value') | sample-utility-1.js:15:2:15:3 | ua | Data flow from $@ to $@. | sample-utility-1.js:2:17:2:42 | this.ge ... value') | this.ge ... value') | sample-utility-1.js:15:2:15:3 | ua | ua | diff --git a/tests/DefUseSample/DefUseSample.qlref b/tests/DefUseSample/DefUseSample.qlref new file mode 100644 index 0000000..4f6df9c --- /dev/null +++ b/tests/DefUseSample/DefUseSample.qlref @@ -0,0 +1 @@ +DefUseSample.ql diff --git a/tests/DefUseSample/sample-utility-0.js b/tests/DefUseSample/sample-utility-0.js new file mode 100644 index 0000000..f26d073 --- /dev/null +++ b/tests/DefUseSample/sample-utility-0.js @@ -0,0 +1,23 @@ +var SampleUtility = function(){}; +SampleUtility.prototype = Object.extendsObject(Processor, { + + setUserStatus: function() { + var value = this.getParameter('value'); + + var ua = new GR('users'); + ua.query(); + + if(!ua.hasNext()){ + ua.initialize(); + ua.setValue('status',value); + ua.insert(); + } + else { + ua.next(); + ua.setValue('status',value); + ua.update(); + } + }, + + type: 'SampleUtility' +}); diff --git a/tests/DefUseSample/sample-utility-1.js b/tests/DefUseSample/sample-utility-1.js new file mode 100644 index 0000000..aea3135 --- /dev/null +++ b/tests/DefUseSample/sample-utility-1.js @@ -0,0 +1,17 @@ +var SampleUtility = function() { + var value = this.getParameter('value'); + + var ua = new GR('users'); + ua.query(); + + if(!ua.hasNext()){ + ua.initialize(); + ua.setValue('status',value); + ua.insert(); + } + else { + ua.next(); + ua.setValue('status',value); + ua.update(); + } +}