Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
category: minorAnalysis
---
* The route object returned by Vue Router's `useRoute()` Composition API is now recognized as a client-side remote flow source, covering its `query`, `params`, `path`, `fullPath`, and `hash` members. These members are additionally reported under the corresponding `browser-url-query`, `browser-url-path`, and `browser-url-fragment` threat models.
* Added flow models for Vue's `ref`, `shallowRef`, `toRef`, `reactive`, and `computed` Composition API helpers.
22 changes: 22 additions & 0 deletions javascript/ql/lib/ext/vue.model.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
extensions:
- addsTo:
pack: codeql/javascript-all
extensible: sourceModel
data:
# `useRoute()` — Vue Router's Composition API returns a route object whose URL-derived
# members are client-side remote flow sources.
- ["vue-router", "Member[useRoute].ReturnValue.Member[params,path,fullPath]", "browser-url-path"]
- ["vue-router", "Member[useRoute].ReturnValue.Member[query]", "browser-url-query"]
- ["vue-router", "Member[useRoute].ReturnValue.Member[hash]", "browser-url-fragment"]
- addsTo:
pack: codeql/javascript-all
extensible: summaryModel
data:
# `ref`, `shallowRef`, `toRef` — the wrapped argument flows to `.value`.
- ["vue", "Member[ref,shallowRef,toRef]", "Argument[0]", "ReturnValue.Member[value]", "value"]
# `reactive` — the wrapped argument taints the returned reactive object.
- ["vue", "Member[reactive]", "Argument[0]", "ReturnValue", "taint"]
# `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"]
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;
16 changes: 16 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,25 @@ 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 | browser-url-query |
| 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