diff --git a/javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll b/javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll index f86d8806304d..46801bd1ad7e 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll @@ -1610,6 +1610,11 @@ module DataFlow { pred = TElementPatternNode(_, element) and succ = lvalueNodeInternal(element) ) + or + exists(Expr rest | + pred = TRestPatternNode(_, rest) and + succ = lvalueNodeInternal(rest) + ) } /** diff --git a/javascript/ql/test/library-tests/DataFlow/tests.expected b/javascript/ql/test/library-tests/DataFlow/tests.expected index 3637927d0e25..55c6771eef02 100644 --- a/javascript/ql/test/library-tests/DataFlow/tests.expected +++ b/javascript/ql/test/library-tests/DataFlow/tests.expected @@ -1094,6 +1094,7 @@ flowStep | tst.js:87:11:87:24 | o | tst.js:90:15:90:15 | o | | tst.js:87:11:87:24 | x | tst.js:91:10:91:10 | x | | tst.js:87:13:87:16 | p: x | tst.js:87:11:87:24 | x | +| tst.js:87:22:87:22 | ...o | tst.js:87:11:87:24 | o | | tst.js:88:7:88:18 | y | tst.js:91:14:91:14 | y | | tst.js:88:9:88:12 | q: y | tst.js:88:7:88:18 | y | | tst.js:88:18:88:18 | o | tst.js:88:7:88:14 | { q: y } | @@ -1110,6 +1111,7 @@ flowStep | tst.js:98:11:98:24 | rest | tst.js:101:13:101:16 | rest | | tst.js:98:11:98:24 | x | tst.js:102:10:102:10 | x | | tst.js:98:13:98:13 | x | tst.js:98:11:98:24 | x | +| tst.js:98:19:98:22 | ...rest | tst.js:98:11:98:24 | rest | | tst.js:99:7:99:18 | y | tst.js:102:14:102:14 | y | | tst.js:99:9:99:9 | y | tst.js:99:7:99:18 | y | | tst.js:99:15:99:18 | rest | tst.js:99:7:99:11 | [ y ] | @@ -1264,6 +1266,7 @@ getImmediatePredecessor | tst.js:87:11:87:24 | o | tst.js:90:15:90:15 | o | | tst.js:87:11:87:24 | x | tst.js:91:10:91:10 | x | | tst.js:87:13:87:16 | p: x | tst.js:87:11:87:24 | x | +| tst.js:87:22:87:22 | ...o | tst.js:87:11:87:24 | o | | tst.js:88:7:88:18 | y | tst.js:91:14:91:14 | y | | tst.js:88:9:88:12 | q: y | tst.js:88:7:88:18 | y | | tst.js:88:18:88:18 | o | tst.js:88:7:88:14 | { q: y } | @@ -1279,6 +1282,7 @@ getImmediatePredecessor | tst.js:98:11:98:24 | rest | tst.js:101:13:101:16 | rest | | tst.js:98:11:98:24 | x | tst.js:102:10:102:10 | x | | tst.js:98:13:98:13 | x | tst.js:98:11:98:24 | x | +| tst.js:98:19:98:22 | ...rest | tst.js:98:11:98:24 | rest | | tst.js:99:7:99:18 | y | tst.js:102:14:102:14 | y | | tst.js:99:9:99:9 | y | tst.js:99:7:99:18 | y | | tst.js:99:15:99:18 | rest | tst.js:99:7:99:11 | [ y ] | diff --git a/javascript/ql/test/library-tests/GetALocalSource/rest-pattern.js b/javascript/ql/test/library-tests/GetALocalSource/rest-pattern.js new file mode 100644 index 000000000000..3e0a62755f04 --- /dev/null +++ b/javascript/ql/test/library-tests/GetALocalSource/rest-pattern.js @@ -0,0 +1,15 @@ +function t1() { + const { ...rest } = source('t1.1'); + rest; // $ getALocalSource=rest +} + +function t2() { + const [ ...rest ] = source('t2.1'); + rest; // $ getALocalSource=rest +} + +function t3() { + const { p1, ...rest } = source('t3.1'); + p1; // $ getALocalSource=p1 + rest; // $ getALocalSource=rest +} diff --git a/javascript/ql/test/library-tests/GetALocalSource/test.expected b/javascript/ql/test/library-tests/GetALocalSource/test.expected new file mode 100644 index 000000000000..5770e50b8a62 --- /dev/null +++ b/javascript/ql/test/library-tests/GetALocalSource/test.expected @@ -0,0 +1,4 @@ +| rest-pattern.js:3:5:3:8 | rest | rest | +| rest-pattern.js:8:5:8:8 | rest | rest | +| rest-pattern.js:13:5:13:6 | p1 | p1 | +| rest-pattern.js:14:5:14:8 | rest | rest | diff --git a/javascript/ql/test/library-tests/GetALocalSource/test.ql b/javascript/ql/test/library-tests/GetALocalSource/test.ql new file mode 100644 index 000000000000..7803f6fd4959 --- /dev/null +++ b/javascript/ql/test/library-tests/GetALocalSource/test.ql @@ -0,0 +1,30 @@ +import javascript + +string nodeName(DataFlow::SourceNode node) { + result = node.getAstNode().(VarRef).getName() + or + result = node.getAstNode().(PropertyPattern).getName() + or + result = node.getAstNode().(PropAccess).getPropertyName() + or + exists(DataFlow::InvokeNode invoke | + node = invoke and + invoke.getCalleeName() = "source" and + result = invoke.getArgument(0).getStringValue() + ) +} + +bindingset[node1, node2] +pragma[inline_late] +predicate sameLine(DataFlow::Node node1, DataFlow::Node node2) { + node1.getLocation().getFile() = node2.getLocation().getFile() and + node1.getLocation().getStartLine() = node2.getLocation().getStartLine() +} + +query predicate getALocalSource(DataFlow::Node node, string name) { + exists(DataFlow::SourceNode sn | + sn = node.getALocalSource() and + name = nodeName(sn) and + not sameLine(node, sn) + ) +} diff --git a/javascript/ql/test/library-tests/GetALocalSource/test.qlref b/javascript/ql/test/library-tests/GetALocalSource/test.qlref new file mode 100644 index 000000000000..ab6773f15f90 --- /dev/null +++ b/javascript/ql/test/library-tests/GetALocalSource/test.qlref @@ -0,0 +1,2 @@ +query: test.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql