Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
7ef911e
Model Vue Router useRoute sources
Copilot Jul 16, 2026
7449658
Test Vue useRoute query source
Copilot Jul 16, 2026
9aaa149
Add change note for Vue Router useRoute query support
Copilot Jul 16, 2026
03d2ad5
Add YYYY-MM-DD format change note for Vue Router useRoute query
Copilot Jul 16, 2026
7e08178
Add Vue summary models for ref, shallowRef, toRef, reactive, and comp…
Copilot Jul 16, 2026
dfb0487
Merge pull request #1 from adrienpessu/copilot/add-codeql-model-for-u…
adrienpessu Jul 16, 2026
dc15554
Move Vue flow models into QL
Copilot Jul 16, 2026
d99bb54
Fix and verify Vue flow summaries
Copilot Jul 16, 2026
c5b3ca0
Merge Vue change notes
Copilot Jul 16, 2026
f329988
Merge pull request #2 from adrienpessu/copilot/modify-vue-qll-for-mai…
adrienpessu Jul 16, 2026
0f8f884
Add Vue toRef value-flow and computed object-overload tests
adrienpessu Jul 17, 2026
ceed0c3
Merge pull request #3 from adrienpessu/adrienpessu-vue-toref-computed…
adrienpessu Jul 17, 2026
02979ba
Merge branch 'main' into main
adrienpessu Jul 21, 2026
148d9cc
Update change-notes/1.26/analysis-javascript.md
adrienpessu Jul 27, 2026
b5136b6
JS: Move Vue Composition API and useRoute source config to data exten…
adrienpessu Jul 27, 2026
bd21395
Merge pull request #5 from adrienpessu/adrienpessu-solid-eureka
adrienpessu Jul 27, 2026
f75fe13
Merge branch 'main' into main
adrienpessu Jul 27, 2026
fe71e0a
Merge branch 'main' into main
asgerf Jul 31, 2026
c984506
Merge branch 'main' into main
adrienpessu Jul 31, 2026
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
1 change: 1 addition & 0 deletions change-notes/1.26/analysis-javascript.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
- [styled-components](https://www.npmjs.com/package/styled-components)
- [throttle-debounce](https://www.npmjs.com/package/throttle-debounce)
- [underscore](https://www.npmjs.com/package/underscore)
- [vue-router](https://www.npmjs.com/package/vue-router)
Comment thread
adrienpessu marked this conversation as resolved.
Outdated

* Analyzing files with the ".cjs" extension is now supported.
* ES2021 features are now supported.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
category: minorAnalysis
---
* The query parameter of Vue Router's `useRoute()` Composition API is now recognized as a client-side remote flow source.
* Added flow models for Vue's `ref`, `shallowRef`, `toRef`, `reactive`, and `computed` Composition API helpers.
9 changes: 9 additions & 0 deletions javascript/ql/lib/ext/vue.model.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
extensions:
- addsTo:
pack: codeql/javascript-all
extensible: summaryModel
data:
# `computed(() => ...)` — function overload: the getter's return value flows to `.value`.
- ["vue", "Member[computed]", "Argument[0].ReturnValue", "ReturnValue.Member[value]", "value"]
# `computed({ get() { ... } })` — object overload: the `get` getter's return value flows to `.value`.
- ["vue", "Member[computed]", "Argument[0].Member[get].ReturnValue", "ReturnValue.Member[value]", "value"]
36 changes: 36 additions & 0 deletions javascript/ql/lib/semmle/javascript/frameworks/Vue.qll
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,40 @@ module Vue {
result = any(GlobalVueEntryPoint e).getANode()
}

/**
* Models data flow through Vue Composition API helpers.
*
* Note that `computed` is not modeled here but in the `vue.model.yml` data
* extension, because its object overload (`computed({ get() { ... } })`)
* requires callback flow synthesis that data extensions support but a
* hand-written `SummarizedCallable` does not.
*/
overlay[local?]
private class VueCompositionApiSummary extends DataFlow::SummarizedCallable::Range {
Comment thread
adrienpessu marked this conversation as resolved.
Outdated
string name;

VueCompositionApiSummary() {
name = ["ref", "shallowRef", "toRef", "reactive"] and
this = "vue." + name
}

override predicate propagatesFlow(string input, string output, boolean preservesValue) {
name = ["ref", "shallowRef", "toRef"] and
input = "Argument[0]" and
output = "ReturnValue.Member[value]" and
preservesValue = true
or
name = "reactive" and
input = "Argument[0]" and
output = "ReturnValue" and
preservesValue = false
}

override DataFlow::InvokeNode getACall() {
result = API::moduleImport("vue").getMember(name).getACall()
}
}

/**
* Gets a reference to the 'Vue' object.
*/
Expand Down Expand Up @@ -652,6 +686,8 @@ module Vue {
t.start() and
(
exists(API::Node router | router = API::moduleImport("vue-router") |
result = router.getMember("useRoute").getACall()
or
result = router.getInstance().getMember("currentRoute").asSource()
or
result =
Expand Down
4 changes: 3 additions & 1 deletion javascript/ql/test/library-tests/frameworks/Vue/router.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Router from 'vue-router';
import Router, { useRoute } from 'vue-router';

export const router = new Router({
routes: [
Expand Down Expand Up @@ -43,3 +43,5 @@ router.afterEach((to, from) => {
to.query.x;
from.query.x;
});

useRoute().query;
15 changes: 15 additions & 0 deletions javascript/ql/test/library-tests/frameworks/Vue/tests.expected
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ remoteFlowSource
| router.js:39:5:39:14 | from.query |
| router.js:43:5:43:12 | to.query |
| router.js:44:5:44:14 | from.query |
| router.js:47:1:47:16 | useRoute().query |
parseErrors
attribute
| compont-with-route.vue:2:8:2:21 | v-html=dataA | v-html |
Expand Down Expand Up @@ -239,10 +240,24 @@ threatModelSource
| router.js:39:5:39:14 | from.query | remote |
| router.js:43:5:43:12 | to.query | remote |
| router.js:44:5:44:14 | from.query | remote |
| router.js:47:1:47:16 | useRoute().query | remote |
| single-component-file-1.vue:7:45:7:54 | this.input | view-component-input |
| single-file-component-3-script.js:5:42:5:51 | this.input | view-component-input |
| single-file-component-4.vue:21:14:21:23 | this.input | view-component-input |
| single-file-component-5.vue:19:14:19:23 | this.input | view-component-input |
| single-file-component-6.vue:5:11:5:15 | input | view-component-input |
| single-file-component-7.vue:5:11:5:15 | input | view-component-input |
| single-file-component-8.vue:5:11:5:15 | input | view-component-input |
compositionApiDataFlow
| tst.js:119:14:119:26 | source("ref") | tst.js:119:6:119:33 | Vue.ref ... ).value |
| tst.js:120:21:120:40 | source("shallowRef") | tst.js:120:6:120:47 | Vue.sha ... ).value |
| tst.js:121:16:121:30 | source("toRef") | tst.js:121:6:121:37 | Vue.toR ... ).value |
| tst.js:123:25:123:42 | source("computed") | tst.js:123:6:123:49 | Vue.com ... ).value |
| tst.js:124:36:124:59 | source( ... bject") | tst.js:124:6:124:82 | Vue.com ... ).value |
compositionApiTaintFlow
| tst.js:119:14:119:26 | source("ref") | tst.js:119:6:119:33 | Vue.ref ... ).value |
| tst.js:120:21:120:40 | source("shallowRef") | tst.js:120:6:120:47 | Vue.sha ... ).value |
| tst.js:121:16:121:30 | source("toRef") | tst.js:121:6:121:37 | Vue.toR ... ).value |
| tst.js:122:19:122:36 | source("reactive") | tst.js:122:6:122:37 | Vue.rea ... tive")) |
| tst.js:123:25:123:42 | source("computed") | tst.js:123:6:123:49 | Vue.com ... ).value |
| tst.js:124:36:124:59 | source( ... bject") | tst.js:124:6:124:82 | Vue.com ... ).value |
18 changes: 18 additions & 0 deletions javascript/ql/test/library-tests/frameworks/Vue/tests.ql
Original file line number Diff line number Diff line change
@@ -1,6 +1,24 @@
import javascript
import semmle.javascript.security.dataflow.DomBasedXssCustomizations

module TestConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
source.(DataFlow::CallNode).getCalleeName() = "source"
}

predicate isSink(DataFlow::Node sink) {
sink = any(DataFlow::CallNode call | call.getCalleeName() = "sink").getAnArgument()
}
}

module TestDataFlow = DataFlow::Global<TestConfig>;

module TestTaintFlow = TaintTracking::Global<TestConfig>;

query predicate compositionApiDataFlow = TestDataFlow::flow/2;

query predicate compositionApiTaintFlow = TestTaintFlow::flow/2;

query predicate component_getAPropertyValue(Vue::Component c, string name, DataFlow::Node prop) {
c.getAPropertyValue(name) = prop
}
Expand Down
7 changes: 7 additions & 0 deletions javascript/ql/test/library-tests/frameworks/Vue/tst.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,10 @@ let subclass2 = base.extend({
fromSubclass2: 100
}
});

sink(Vue.ref(source("ref")).value);
sink(Vue.shallowRef(source("shallowRef")).value);
sink(Vue.toRef(source("toRef")).value);
Comment thread
adrienpessu marked this conversation as resolved.
Comment thread
adrienpessu marked this conversation as resolved.
Comment thread
adrienpessu marked this conversation as resolved.
sink(Vue.reactive(source("reactive")));
sink(Vue.computed(() => source("computed")).value);
Comment thread
adrienpessu marked this conversation as resolved.
Comment thread
adrienpessu marked this conversation as resolved.
sink(Vue.computed({ get() { return source("computedObject"); }, set(v) {} }).value);
Loading