Skip to content

Commit 70232a3

Browse files
authored
Merge pull request #19006 from Napalys/js/vue_tanstack_model
Js: Added support for `@tanstack/vue-query`
2 parents 474b8a5 + 933f3c6 commit 70232a3

File tree

9 files changed

+209
-30
lines changed

9 files changed

+209
-30
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Added support for the `@tanstack/vue-query` package.

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,9 @@ extensions:
33
pack: codeql/javascript-all
44
extensible: summaryModel
55
data:
6-
- ["@tanstack/angular-query-experimental", "Member[injectQuery]", "Argument[0].ReturnValue.Member[queryFn].ReturnValue", "ReturnValue.Member[data].Awaited", "taint"]
7-
- ["@tanstack/angular-query", "Member[injectQuery]", "Argument[0].ReturnValue.Member[queryFn].ReturnValue", "ReturnValue.Member[data].Awaited", "taint"]
6+
- ["@tanstack/angular-query-experimental", "Member[injectQuery]", "Argument[0].ReturnValue.Member[queryFn].ReturnValue", "ReturnValue.Member[data].Awaited", "value"]
7+
- ["@tanstack/angular-query", "Member[injectQuery]", "Argument[0].ReturnValue.Member[queryFn].ReturnValue", "ReturnValue.Member[data].Awaited", "value"]
8+
- ["@tanstack/vue-query", "Member[useQuery]", "Argument[0].Member[queryFn].ReturnValue.Awaited", "ReturnValue.Member[data]", "value"]
9+
- ["@tanstack/vue-query", "Member[useQueries]", "Argument[0].Member[queries].ArrayElement.Member[queryFn].ReturnValue.Awaited", "ReturnValue.AnyMember.Member[data]", "value"]
10+
- ["@tanstack/react-query", "Member[useQueries]", "Argument[0].Member[queries].ArrayElement.Member[queryFn].ReturnValue.Awaited", "ReturnValue.AnyMember.Member[data]", "value"]
11+
- ["@tanstack/react-query", "Member[useQuery]", "Argument[0].Member[queryFn].ReturnValue.Awaited", "ReturnValue.Member[data]", "value"]

javascript/ql/lib/javascript.qll

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,6 @@ import semmle.javascript.frameworks.Webix
139139
import semmle.javascript.frameworks.WebSocket
140140
import semmle.javascript.frameworks.XmlParsers
141141
import semmle.javascript.frameworks.xUnit
142-
import semmle.javascript.frameworks.Tanstack
143142
import semmle.javascript.linters.ESLint
144143
import semmle.javascript.linters.JSLint
145144
import semmle.javascript.linters.Linting

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

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

javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/Xss.expected

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
| test.jsx:27:29:27:32 | data | test.jsx:5:28:5:63 | fetch(" ... ntent") | test.jsx:27:29:27:32 | data | Cross-site scripting vulnerability due to $@. | test.jsx:5:28:5:63 | fetch(" ... ntent") | user-provided value |
33
| test.ts:21:57:21:76 | response.description | test.ts:8:9:8:79 | this.#h ... query') | test.ts:21:57:21:76 | response.description | Cross-site scripting vulnerability due to $@. | test.ts:8:9:8:79 | this.#h ... query') | user-provided value |
44
| test.ts:24:36:24:90 | `<h2>${ ... o}</p>` | test.ts:8:9:8:79 | this.#h ... query') | test.ts:24:36:24:90 | `<h2>${ ... o}</p>` | Cross-site scripting vulnerability due to $@. | test.ts:8:9:8:79 | this.#h ... query') | user-provided value |
5+
| test.vue:22:10:22:22 | v-html=data | test.vue:10:32:10:84 | fetch(" ... sts/1") | test.vue:22:10:22:22 | v-html=data | Cross-site scripting vulnerability due to $@. | test.vue:10:32:10:84 | fetch(" ... sts/1") | user-provided value |
56
| testReactRelay.tsx:7:43:7:58 | commentData.text | testReactRelay.tsx:5:23:5:52 | useFrag ... entRef) | testReactRelay.tsx:7:43:7:58 | commentData.text | Cross-site scripting vulnerability due to $@. | testReactRelay.tsx:5:23:5:52 | useFrag ... entRef) | user-provided value |
67
| testReactRelay.tsx:18:48:18:68 | data.co ... 0].text | testReactRelay.tsx:17:16:17:42 | useLazy ... ry, {}) | testReactRelay.tsx:18:48:18:68 | data.co ... 0].text | Cross-site scripting vulnerability due to $@. | testReactRelay.tsx:17:16:17:42 | useLazy ... ry, {}) | user-provided value |
78
| testReactRelay.tsx:28:17:28:67 | usePrel ... r?.name | testReactRelay.tsx:28:17:28:56 | usePrel ... erence) | testReactRelay.tsx:28:17:28:67 | usePrel ... r?.name | Cross-site scripting vulnerability due to $@. | testReactRelay.tsx:28:17:28:56 | usePrel ... erence) | user-provided value |
@@ -12,6 +13,9 @@
1213
| testReactRelay.tsx:113:48:113:58 | fragmentRef | testReactRelay.tsx:100:14:100:16 | res | testReactRelay.tsx:113:48:113:58 | fragmentRef | Cross-site scripting vulnerability due to $@. | testReactRelay.tsx:100:14:100:16 | res | user-provided value |
1314
| testReactRelay.tsx:127:35:127:43 | data.user | testReactRelay.tsx:124:12:124:15 | data | testReactRelay.tsx:127:35:127:43 | data.user | Cross-site scripting vulnerability due to $@. | testReactRelay.tsx:124:12:124:15 | data | user-provided value |
1415
| testReactRelay.tsx:137:50:137:53 | data | testReactRelay.tsx:136:16:136:39 | readFra ... y, key) | testReactRelay.tsx:137:50:137:53 | data | Cross-site scripting vulnerability due to $@. | testReactRelay.tsx:136:16:136:39 | readFra ... y, key) | user-provided value |
16+
| testReactUseQueries.jsx:37:25:37:38 | repoQuery.data | testReactUseQueries.jsx:4:26:4:53 | fetch(' ... e.com') | testReactUseQueries.jsx:37:25:37:38 | repoQuery.data | Cross-site scripting vulnerability due to $@. | testReactUseQueries.jsx:4:26:4:53 | fetch(' ... e.com') | user-provided value |
17+
| testUseQueries2.vue:40:10:40:23 | v-html=data3 | testUseQueries2.vue:6:28:6:63 | fetch(" ... ntent") | testUseQueries2.vue:40:10:40:23 | v-html=data3 | Cross-site scripting vulnerability due to $@. | testUseQueries2.vue:6:28:6:63 | fetch(" ... ntent") | user-provided value |
18+
| testUseQueries2.vue:40:10:40:23 | v-html=data3 | testUseQueries2.vue:12:28:12:41 | fetch("${id}") | testUseQueries2.vue:40:10:40:23 | v-html=data3 | Cross-site scripting vulnerability due to $@. | testUseQueries2.vue:12:28:12:41 | fetch("${id}") | user-provided value |
1519
edges
1620
| test.jsx:5:11:5:63 | response | test.jsx:6:24:6:31 | response | provenance | |
1721
| test.jsx:5:22:5:63 | await f ... ntent") | test.jsx:5:11:5:63 | response | provenance | |
@@ -20,8 +24,9 @@ edges
2024
| test.jsx:6:18:6:38 | await r ... .json() | test.jsx:6:11:6:38 | data | provenance | |
2125
| test.jsx:6:24:6:31 | response | test.jsx:6:24:6:38 | response.json() | provenance | |
2226
| test.jsx:6:24:6:38 | response.json() | test.jsx:6:18:6:38 | await r ... .json() | provenance | |
23-
| test.jsx:7:12:7:15 | data | test.jsx:15:11:17:5 | data | provenance | |
27+
| test.jsx:7:12:7:15 | data | test.jsx:15:13:15:16 | data | provenance | |
2428
| test.jsx:15:11:17:5 | data | test.jsx:27:29:27:32 | data | provenance | |
29+
| test.jsx:15:13:15:16 | data | test.jsx:15:11:17:5 | data | provenance | |
2530
| test.ts:8:9:8:79 | this.#h ... query') | test.ts:20:28:20:35 | response | provenance | |
2631
| test.ts:20:28:20:35 | response | test.ts:21:57:21:64 | response | provenance | |
2732
| test.ts:20:28:20:35 | response | test.ts:24:43:24:50 | response | provenance | |
@@ -31,6 +36,14 @@ edges
3136
| test.ts:24:43:24:55 | response.name | test.ts:24:36:24:90 | `<h2>${ ... o}</p>` | provenance | |
3237
| test.ts:24:67:24:74 | response | test.ts:24:67:24:84 | response.owner.bio | provenance | |
3338
| test.ts:24:67:24:84 | response.owner.bio | test.ts:24:36:24:90 | `<h2>${ ... o}</p>` | provenance | |
39+
| test.vue:7:11:13:6 | data | test.vue:15:21:15:24 | data | provenance | |
40+
| test.vue:7:45:7:48 | data | test.vue:7:11:13:6 | data | provenance | |
41+
| test.vue:10:15:10:84 | response | test.vue:11:16:11:23 | response | provenance | |
42+
| test.vue:10:26:10:84 | await f ... sts/1") | test.vue:10:15:10:84 | response | provenance | |
43+
| test.vue:10:32:10:84 | fetch(" ... sts/1") | test.vue:10:26:10:84 | await f ... sts/1") | provenance | |
44+
| test.vue:11:16:11:23 | response | test.vue:11:16:11:30 | response.json() | provenance | |
45+
| test.vue:11:16:11:30 | response.json() | test.vue:7:45:7:48 | data | provenance | |
46+
| test.vue:15:21:15:24 | data | test.vue:22:10:22:22 | v-html=data | provenance | |
3447
| testReactRelay.tsx:5:9:5:52 | commentData | testReactRelay.tsx:7:43:7:53 | commentData | provenance | |
3548
| testReactRelay.tsx:5:23:5:52 | useFrag ... entRef) | testReactRelay.tsx:5:9:5:52 | commentData | provenance | |
3649
| testReactRelay.tsx:7:43:7:53 | commentData | testReactRelay.tsx:7:43:7:58 | commentData.text | provenance | |
@@ -56,6 +69,25 @@ edges
5669
| testReactRelay.tsx:127:35:127:38 | data | testReactRelay.tsx:127:35:127:43 | data.user | provenance | |
5770
| testReactRelay.tsx:136:9:136:39 | data | testReactRelay.tsx:137:50:137:53 | data | provenance | |
5871
| testReactRelay.tsx:136:16:136:39 | readFra ... y, key) | testReactRelay.tsx:136:9:136:39 | data | provenance | |
72+
| testReactUseQueries.jsx:4:9:4:53 | response | testReactUseQueries.jsx:5:10:5:17 | response | provenance | |
73+
| testReactUseQueries.jsx:4:20:4:53 | await f ... e.com') | testReactUseQueries.jsx:4:9:4:53 | response | provenance | |
74+
| testReactUseQueries.jsx:4:26:4:53 | fetch(' ... e.com') | testReactUseQueries.jsx:4:20:4:53 | await f ... e.com') | provenance | |
75+
| testReactUseQueries.jsx:5:10:5:17 | response | testReactUseQueries.jsx:5:10:5:24 | response.json() | provenance | |
76+
| testReactUseQueries.jsx:5:10:5:24 | response.json() | testReactUseQueries.jsx:37:25:37:38 | repoQuery.data | provenance | |
77+
| testUseQueries2.vue:6:11:6:63 | response | testUseQueries2.vue:7:24:7:31 | response | provenance | |
78+
| testUseQueries2.vue:6:22:6:63 | await f ... ntent") | testUseQueries2.vue:6:11:6:63 | response | provenance | |
79+
| testUseQueries2.vue:6:28:6:63 | fetch(" ... ntent") | testUseQueries2.vue:6:22:6:63 | await f ... ntent") | provenance | |
80+
| testUseQueries2.vue:7:11:7:38 | data | testUseQueries2.vue:8:12:8:15 | data | provenance | |
81+
| testUseQueries2.vue:7:18:7:38 | await r ... .json() | testUseQueries2.vue:7:11:7:38 | data | provenance | |
82+
| testUseQueries2.vue:7:24:7:31 | response | testUseQueries2.vue:7:24:7:38 | response.json() | provenance | |
83+
| testUseQueries2.vue:7:24:7:38 | response.json() | testUseQueries2.vue:7:18:7:38 | await r ... .json() | provenance | |
84+
| testUseQueries2.vue:8:12:8:15 | data | testUseQueries2.vue:33:22:33:36 | results[0].data | provenance | |
85+
| testUseQueries2.vue:12:11:12:41 | response | testUseQueries2.vue:13:12:13:19 | response | provenance | |
86+
| testUseQueries2.vue:12:22:12:41 | await fetch("${id}") | testUseQueries2.vue:12:11:12:41 | response | provenance | |
87+
| testUseQueries2.vue:12:28:12:41 | fetch("${id}") | testUseQueries2.vue:12:22:12:41 | await fetch("${id}") | provenance | |
88+
| testUseQueries2.vue:13:12:13:19 | response | testUseQueries2.vue:13:12:13:26 | response.json() | provenance | |
89+
| testUseQueries2.vue:13:12:13:26 | response.json() | testUseQueries2.vue:33:22:33:36 | results[0].data | provenance | |
90+
| testUseQueries2.vue:33:22:33:36 | results[0].data | testUseQueries2.vue:40:10:40:23 | v-html=data3 | provenance | |
5991
nodes
6092
| test.jsx:5:11:5:63 | response | semmle.label | response |
6193
| test.jsx:5:22:5:63 | await f ... ntent") | semmle.label | await f ... ntent") |
@@ -66,6 +98,7 @@ nodes
6698
| test.jsx:6:24:6:38 | response.json() | semmle.label | response.json() |
6799
| test.jsx:7:12:7:15 | data | semmle.label | data |
68100
| test.jsx:15:11:17:5 | data | semmle.label | data |
101+
| test.jsx:15:13:15:16 | data | semmle.label | data |
69102
| test.jsx:27:29:27:32 | data | semmle.label | data |
70103
| test.ts:8:9:8:79 | this.#h ... query') | semmle.label | this.#h ... query') |
71104
| test.ts:20:28:20:35 | response | semmle.label | response |
@@ -76,6 +109,15 @@ nodes
76109
| test.ts:24:43:24:55 | response.name | semmle.label | response.name |
77110
| test.ts:24:67:24:74 | response | semmle.label | response |
78111
| test.ts:24:67:24:84 | response.owner.bio | semmle.label | response.owner.bio |
112+
| test.vue:7:11:13:6 | data | semmle.label | data |
113+
| test.vue:7:45:7:48 | data | semmle.label | data |
114+
| test.vue:10:15:10:84 | response | semmle.label | response |
115+
| test.vue:10:26:10:84 | await f ... sts/1") | semmle.label | await f ... sts/1") |
116+
| test.vue:10:32:10:84 | fetch(" ... sts/1") | semmle.label | fetch(" ... sts/1") |
117+
| test.vue:11:16:11:23 | response | semmle.label | response |
118+
| test.vue:11:16:11:30 | response.json() | semmle.label | response.json() |
119+
| test.vue:15:21:15:24 | data | semmle.label | data |
120+
| test.vue:22:10:22:22 | v-html=data | semmle.label | v-html=data |
79121
| testReactRelay.tsx:5:9:5:52 | commentData | semmle.label | commentData |
80122
| testReactRelay.tsx:5:23:5:52 | useFrag ... entRef) | semmle.label | useFrag ... entRef) |
81123
| testReactRelay.tsx:7:43:7:53 | commentData | semmle.label | commentData |
@@ -111,4 +153,25 @@ nodes
111153
| testReactRelay.tsx:136:9:136:39 | data | semmle.label | data |
112154
| testReactRelay.tsx:136:16:136:39 | readFra ... y, key) | semmle.label | readFra ... y, key) |
113155
| testReactRelay.tsx:137:50:137:53 | data | semmle.label | data |
156+
| testReactUseQueries.jsx:4:9:4:53 | response | semmle.label | response |
157+
| testReactUseQueries.jsx:4:20:4:53 | await f ... e.com') | semmle.label | await f ... e.com') |
158+
| testReactUseQueries.jsx:4:26:4:53 | fetch(' ... e.com') | semmle.label | fetch(' ... e.com') |
159+
| testReactUseQueries.jsx:5:10:5:17 | response | semmle.label | response |
160+
| testReactUseQueries.jsx:5:10:5:24 | response.json() | semmle.label | response.json() |
161+
| testReactUseQueries.jsx:37:25:37:38 | repoQuery.data | semmle.label | repoQuery.data |
162+
| testUseQueries2.vue:6:11:6:63 | response | semmle.label | response |
163+
| testUseQueries2.vue:6:22:6:63 | await f ... ntent") | semmle.label | await f ... ntent") |
164+
| testUseQueries2.vue:6:28:6:63 | fetch(" ... ntent") | semmle.label | fetch(" ... ntent") |
165+
| testUseQueries2.vue:7:11:7:38 | data | semmle.label | data |
166+
| testUseQueries2.vue:7:18:7:38 | await r ... .json() | semmle.label | await r ... .json() |
167+
| testUseQueries2.vue:7:24:7:31 | response | semmle.label | response |
168+
| testUseQueries2.vue:7:24:7:38 | response.json() | semmle.label | response.json() |
169+
| testUseQueries2.vue:8:12:8:15 | data | semmle.label | data |
170+
| testUseQueries2.vue:12:11:12:41 | response | semmle.label | response |
171+
| testUseQueries2.vue:12:22:12:41 | await fetch("${id}") | semmle.label | await fetch("${id}") |
172+
| testUseQueries2.vue:12:28:12:41 | fetch("${id}") | semmle.label | fetch("${id}") |
173+
| testUseQueries2.vue:13:12:13:19 | response | semmle.label | response |
174+
| testUseQueries2.vue:13:12:13:26 | response.json() | semmle.label | response.json() |
175+
| testUseQueries2.vue:33:22:33:36 | results[0].data | semmle.label | results[0].data |
176+
| testUseQueries2.vue:40:10:40:23 | v-html=data3 | semmle.label | v-html=data3 |
114177
subpaths
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<script>
2+
import { useQuery, VueQueryClientProvider } from "@tanstack/vue-query";
3+
4+
5+
export default {
6+
data() {
7+
const { isPending, isError, isFetching, data, error } = useQuery({
8+
queryKey: ["post"],
9+
queryFn: async () => {
10+
const response = await fetch("https://jsonplaceholder.typicode.com/posts/1"); // $ Source
11+
return response.json();
12+
},
13+
});
14+
15+
return { data : data };
16+
}
17+
}
18+
</script>
19+
20+
<template>
21+
<VueQueryClientProvider :client="queryClient">
22+
<div v-html="data"></div> <!--$ Alert[js/xss] -->
23+
</VueQueryClientProvider>
24+
</template>
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { useQueries } from '@tanstack/react-query';
2+
3+
const fetchRepoData = async () => {
4+
const response = await fetch('https://example.com'); // $ Source
5+
return response.json();
6+
};
7+
8+
async function fetchPost() {
9+
const response = await fetch("www.example.com"); // $ MISSING: Source
10+
return response.json();
11+
}
12+
13+
export default function UseQueriesComponent() {
14+
const results = useQueries({
15+
queries: [
16+
{
17+
queryKey: ['repoData'],
18+
queryFn: fetchRepoData,
19+
},
20+
{
21+
queryKey: ['repoData'],
22+
queryFn: () => fetchPost,
23+
},
24+
],
25+
});
26+
27+
const repoQuery = results[0];
28+
29+
if (repoQuery.isLoading) return <p>Loading...</p>;
30+
if (repoQuery.isError) return <p>Error: {repoQuery.error.message}</p>;
31+
32+
return (
33+
<div>
34+
<h1>Content with Dangerous HTML</h1>
35+
<div
36+
dangerouslySetInnerHTML={{
37+
__html: repoQuery.data, // $ Alert
38+
}}
39+
/>
40+
</div>
41+
);
42+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<script>
2+
import { useQueries } from "@tanstack/vue-query";
3+
4+
export default {
5+
data() {
6+
const ids = [1, 2, 3]
7+
const results = useQueries({
8+
queries: ids.map((id) => ({
9+
queryKey: ['post', id],
10+
queryFn: async () => {
11+
const response = await fetch("${id}"); // $ MISSING: Source
12+
return response.json();
13+
},
14+
staleTime: Infinity,
15+
})),
16+
});
17+
18+
return { data2 : results[0].data };
19+
}
20+
}
21+
</script>
22+
23+
<template>
24+
<VueQueryClientProvider :client="queryClient">
25+
<div v-html="data2"></div> <!--$ MISSING: Alert -->
26+
</VueQueryClientProvider>
27+
</template>
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<script>
2+
import { useQueries } from "@tanstack/vue-query";
3+
import { computed } from "vue";
4+
5+
const fetchContent = async () => {
6+
const response = await fetch("https://example.com/content"); // $ Source
7+
const data = await response.json();
8+
return data;
9+
};
10+
11+
async function fetchPost() {
12+
const response = await fetch("${id}"); // $ Source
13+
return response.json();
14+
}
15+
16+
export default {
17+
data() {
18+
const results = useQueries({
19+
queries: [
20+
{
21+
queryKey: ["post", 1],
22+
queryFn: fetchContent,
23+
staleTime: Infinity,
24+
},
25+
{
26+
queryKey: ["post", 2],
27+
queryFn: () => fetchPost(),
28+
staleTime: Infinity,
29+
},
30+
],
31+
});
32+
33+
return { data3 : results[0].data };
34+
},
35+
};
36+
</script>
37+
38+
<template>
39+
<VueQueryClientProvider :client="queryClient">
40+
<div v-html="data3"></div> <!--$ Alert -->
41+
</VueQueryClientProvider>
42+
</template>

0 commit comments

Comments
 (0)