Skip to content

Commit f329988

Browse files
authored
Merge pull request #2 from adrienpessu/copilot/modify-vue-qll-for-maintenance
Move Vue Composition API flow models into Vue.qll
2 parents dfb0487 + c5b3ca0 commit f329988

6 files changed

Lines changed: 72 additions & 15 deletions

File tree

javascript/ql/lib/change-notes/2026-07-16-vue-router-useRoute-query.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
category: minorAnalysis
33
---
44
* The query parameter of Vue Router's `useRoute()` Composition API is now recognized as a client-side remote flow source.
5+
* Added flow models for Vue's `ref`, `shallowRef`, `toRef`, `reactive`, and `computed` Composition API helpers.

javascript/ql/lib/ext/vue.model.yml

Lines changed: 0 additions & 15 deletions
This file was deleted.

javascript/ql/lib/semmle/javascript/frameworks/Vue.qll

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,43 @@ module Vue {
3535
result = any(GlobalVueEntryPoint e).getANode()
3636
}
3737

38+
/** Models data flow through Vue Composition API helpers. */
39+
overlay[local?]
40+
private class VueCompositionApiSummary extends DataFlow::SummarizedCallable::Range {
41+
string name;
42+
43+
VueCompositionApiSummary() {
44+
name = ["ref", "shallowRef", "toRef", "reactive", "computed"] and
45+
this = "vue." + name
46+
}
47+
48+
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
49+
name = ["ref", "shallowRef"] and
50+
input = "Argument[0]" and
51+
output = "ReturnValue.Member[value]" and
52+
preservesValue = true
53+
or
54+
name = "toRef" and
55+
input = "Argument[0]" and
56+
output = "ReturnValue.Member[value]" and
57+
preservesValue = false
58+
or
59+
name = "reactive" and
60+
input = "Argument[0]" and
61+
output = "ReturnValue" and
62+
preservesValue = false
63+
or
64+
name = "computed" and
65+
input = "Argument[0].ReturnValue" and
66+
output = "ReturnValue.Member[value]" and
67+
preservesValue = true
68+
}
69+
70+
override DataFlow::InvokeNode getACall() {
71+
result = API::moduleImport("vue").getMember(name).getACall()
72+
}
73+
}
74+
3875
/**
3976
* Gets a reference to the 'Vue' object.
4077
*/

javascript/ql/test/library-tests/frameworks/Vue/tests.expected

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,3 +248,13 @@ threatModelSource
248248
| single-file-component-6.vue:5:11:5:15 | input | view-component-input |
249249
| single-file-component-7.vue:5:11:5:15 | input | view-component-input |
250250
| single-file-component-8.vue:5:11:5:15 | input | view-component-input |
251+
compositionApiDataFlow
252+
| tst.js:119:14:119:26 | source("ref") | tst.js:119:6:119:33 | Vue.ref ... ).value |
253+
| tst.js:120:21:120:40 | source("shallowRef") | tst.js:120:6:120:47 | Vue.sha ... ).value |
254+
| tst.js:123:25:123:42 | source("computed") | tst.js:123:6:123:49 | Vue.com ... ).value |
255+
compositionApiTaintFlow
256+
| tst.js:119:14:119:26 | source("ref") | tst.js:119:6:119:33 | Vue.ref ... ).value |
257+
| tst.js:120:21:120:40 | source("shallowRef") | tst.js:120:6:120:47 | Vue.sha ... ).value |
258+
| tst.js:121:16:121:30 | source("toRef") | tst.js:121:6:121:37 | Vue.toR ... ).value |
259+
| tst.js:122:19:122:36 | source("reactive") | tst.js:122:6:122:37 | Vue.rea ... tive")) |
260+
| tst.js:123:25:123:42 | source("computed") | tst.js:123:6:123:49 | Vue.com ... ).value |

javascript/ql/test/library-tests/frameworks/Vue/tests.ql

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,24 @@
11
import javascript
22
import semmle.javascript.security.dataflow.DomBasedXssCustomizations
33

4+
module TestConfig implements DataFlow::ConfigSig {
5+
predicate isSource(DataFlow::Node source) {
6+
source.(DataFlow::CallNode).getCalleeName() = "source"
7+
}
8+
9+
predicate isSink(DataFlow::Node sink) {
10+
sink = any(DataFlow::CallNode call | call.getCalleeName() = "sink").getAnArgument()
11+
}
12+
}
13+
14+
module TestDataFlow = DataFlow::Global<TestConfig>;
15+
16+
module TestTaintFlow = TaintTracking::Global<TestConfig>;
17+
18+
query predicate compositionApiDataFlow = TestDataFlow::flow/2;
19+
20+
query predicate compositionApiTaintFlow = TestTaintFlow::flow/2;
21+
422
query predicate component_getAPropertyValue(Vue::Component c, string name, DataFlow::Node prop) {
523
c.getAPropertyValue(name) = prop
624
}

javascript/ql/test/library-tests/frameworks/Vue/tst.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,9 @@ let subclass2 = base.extend({
115115
fromSubclass2: 100
116116
}
117117
});
118+
119+
sink(Vue.ref(source("ref")).value);
120+
sink(Vue.shallowRef(source("shallowRef")).value);
121+
sink(Vue.toRef(source("toRef")).value);
122+
sink(Vue.reactive(source("reactive")));
123+
sink(Vue.computed(() => source("computed")).value);

0 commit comments

Comments
 (0)