Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions javascript/ql/lib/change-notes/2025-03-13-tanstack-vue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Added support for the `@tanstack/vue-query` package.
8 changes: 6 additions & 2 deletions javascript/ql/lib/ext/tanstack.model.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,9 @@ extensions:
pack: codeql/javascript-all
extensible: summaryModel
data:
- ["@tanstack/angular-query-experimental", "Member[injectQuery]", "Argument[0].ReturnValue.Member[queryFn].ReturnValue", "ReturnValue.Member[data].Awaited", "taint"]
- ["@tanstack/angular-query", "Member[injectQuery]", "Argument[0].ReturnValue.Member[queryFn].ReturnValue", "ReturnValue.Member[data].Awaited", "taint"]
- ["@tanstack/angular-query-experimental", "Member[injectQuery]", "Argument[0].ReturnValue.Member[queryFn].ReturnValue", "ReturnValue.Member[data].Awaited", "value"]
- ["@tanstack/angular-query", "Member[injectQuery]", "Argument[0].ReturnValue.Member[queryFn].ReturnValue", "ReturnValue.Member[data].Awaited", "value"]
- ["@tanstack/vue-query", "Member[useQuery]", "Argument[0].Member[queryFn].ReturnValue.Awaited", "ReturnValue.Member[data]", "value"]
- ["@tanstack/vue-query", "Member[useQueries]", "Argument[0].Member[queries].ArrayElement.Member[queryFn].ReturnValue.Awaited", "ReturnValue.AnyMember.Member[data]", "value"]
- ["@tanstack/react-query", "Member[useQueries]", "Argument[0].Member[queries].ArrayElement.Member[queryFn].ReturnValue.Awaited", "ReturnValue.AnyMember.Member[data]", "value"]
- ["@tanstack/react-query", "Member[useQuery]", "Argument[0].Member[queryFn].ReturnValue.Awaited", "ReturnValue.Member[data]", "value"]
1 change: 0 additions & 1 deletion javascript/ql/lib/javascript.qll
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,6 @@ import semmle.javascript.frameworks.Webix
import semmle.javascript.frameworks.WebSocket
import semmle.javascript.frameworks.XmlParsers
import semmle.javascript.frameworks.xUnit
import semmle.javascript.frameworks.Tanstack
import semmle.javascript.linters.ESLint
import semmle.javascript.linters.JSLint
import semmle.javascript.linters.Linting
Expand Down
26 changes: 0 additions & 26 deletions javascript/ql/lib/semmle/javascript/frameworks/Tanstack.qll

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
Expand All @@ -12,6 +13,9 @@
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
edges
| test.jsx:5:11:5:63 | response | test.jsx:6:24:6:31 | response | provenance | |
| test.jsx:5:22:5:63 | await f ... ntent") | test.jsx:5:11:5:63 | response | provenance | |
Expand All @@ -20,8 +24,9 @@ edges
| test.jsx:6:18:6:38 | await r ... .json() | test.jsx:6:11:6:38 | data | provenance | |
| test.jsx:6:24:6:31 | response | test.jsx:6:24:6:38 | response.json() | provenance | |
| test.jsx:6:24:6:38 | response.json() | test.jsx:6:18:6:38 | await r ... .json() | provenance | |
| test.jsx:7:12:7:15 | data | test.jsx:15:11:17:5 | data | provenance | |
| test.jsx:7:12:7:15 | data | test.jsx:15:13:15:16 | data | provenance | |
| test.jsx:15:11:17:5 | data | test.jsx:27:29:27:32 | data | provenance | |
| test.jsx:15:13:15:16 | data | test.jsx:15:11:17:5 | data | provenance | |
| test.ts:8:9:8:79 | this.#h ... query') | test.ts:20:28:20:35 | response | provenance | |
| test.ts:20:28:20:35 | response | test.ts:21:57:21:64 | response | provenance | |
| test.ts:20:28:20:35 | response | test.ts:24:43:24:50 | response | provenance | |
Expand All @@ -31,6 +36,14 @@ edges
| test.ts:24:43:24:55 | response.name | test.ts:24:36:24:90 | `<h2>${ ... o}</p>` | provenance | |
| test.ts:24:67:24:74 | response | test.ts:24:67:24:84 | response.owner.bio | provenance | |
| test.ts:24:67:24:84 | response.owner.bio | test.ts:24:36:24:90 | `<h2>${ ... o}</p>` | provenance | |
| test.vue:7:11:13:6 | data | test.vue:15:21:15:24 | data | provenance | |
| test.vue:7:45:7:48 | data | test.vue:7:11:13:6 | data | provenance | |
| test.vue:10:15:10:84 | response | test.vue:11:16:11:23 | response | provenance | |
| test.vue:10:26:10:84 | await f ... sts/1") | test.vue:10:15:10:84 | response | provenance | |
| test.vue:10:32:10:84 | fetch(" ... sts/1") | test.vue:10:26:10:84 | await f ... sts/1") | provenance | |
| test.vue:11:16:11:23 | response | test.vue:11:16:11:30 | response.json() | provenance | |
| test.vue:11:16:11:30 | response.json() | test.vue:7:45:7:48 | data | provenance | |
| test.vue:15:21:15:24 | data | test.vue:22:10:22:22 | v-html=data | provenance | |
| testReactRelay.tsx:5:9:5:52 | commentData | testReactRelay.tsx:7:43:7:53 | commentData | provenance | |
| testReactRelay.tsx:5:23:5:52 | useFrag ... entRef) | testReactRelay.tsx:5:9:5:52 | commentData | provenance | |
| testReactRelay.tsx:7:43:7:53 | commentData | testReactRelay.tsx:7:43:7:58 | commentData.text | provenance | |
Expand All @@ -56,6 +69,25 @@ edges
| testReactRelay.tsx:127:35:127:38 | data | testReactRelay.tsx:127:35:127:43 | data.user | provenance | |
| testReactRelay.tsx:136:9:136:39 | data | testReactRelay.tsx:137:50:137:53 | data | provenance | |
| testReactRelay.tsx:136:16:136:39 | readFra ... y, key) | testReactRelay.tsx:136:9:136:39 | data | provenance | |
| testReactUseQueries.jsx:4:9:4:53 | response | testReactUseQueries.jsx:5:10:5:17 | response | provenance | |
| testReactUseQueries.jsx:4:20:4:53 | await f ... e.com') | testReactUseQueries.jsx:4:9:4:53 | response | provenance | |
| testReactUseQueries.jsx:4:26:4:53 | fetch(' ... e.com') | testReactUseQueries.jsx:4:20:4:53 | await f ... e.com') | provenance | |
| testReactUseQueries.jsx:5:10:5:17 | response | testReactUseQueries.jsx:5:10:5:24 | response.json() | provenance | |
| testReactUseQueries.jsx:5:10:5:24 | response.json() | testReactUseQueries.jsx:37:25:37:38 | repoQuery.data | provenance | |
| testUseQueries2.vue:6:11:6:63 | response | testUseQueries2.vue:7:24:7:31 | response | provenance | |
| testUseQueries2.vue:6:22:6:63 | await f ... ntent") | testUseQueries2.vue:6:11:6:63 | response | provenance | |
| testUseQueries2.vue:6:28:6:63 | fetch(" ... ntent") | testUseQueries2.vue:6:22:6:63 | await f ... ntent") | provenance | |
| testUseQueries2.vue:7:11:7:38 | data | testUseQueries2.vue:8:12:8:15 | data | provenance | |
| testUseQueries2.vue:7:18:7:38 | await r ... .json() | testUseQueries2.vue:7:11:7:38 | data | provenance | |
| testUseQueries2.vue:7:24:7:31 | response | testUseQueries2.vue:7:24:7:38 | response.json() | provenance | |
| testUseQueries2.vue:7:24:7:38 | response.json() | testUseQueries2.vue:7:18:7:38 | await r ... .json() | provenance | |
| testUseQueries2.vue:8:12:8:15 | data | testUseQueries2.vue:33:22:33:36 | results[0].data | provenance | |
| testUseQueries2.vue:12:11:12:41 | response | testUseQueries2.vue:13:12:13:19 | response | provenance | |
| testUseQueries2.vue:12:22:12:41 | await fetch("${id}") | testUseQueries2.vue:12:11:12:41 | response | provenance | |
| testUseQueries2.vue:12:28:12:41 | fetch("${id}") | testUseQueries2.vue:12:22:12:41 | await fetch("${id}") | provenance | |
| testUseQueries2.vue:13:12:13:19 | response | testUseQueries2.vue:13:12:13:26 | response.json() | provenance | |
| testUseQueries2.vue:13:12:13:26 | response.json() | testUseQueries2.vue:33:22:33:36 | results[0].data | provenance | |
| testUseQueries2.vue:33:22:33:36 | results[0].data | testUseQueries2.vue:40:10:40:23 | v-html=data3 | provenance | |
nodes
| test.jsx:5:11:5:63 | response | semmle.label | response |
| test.jsx:5:22:5:63 | await f ... ntent") | semmle.label | await f ... ntent") |
Expand All @@ -66,6 +98,7 @@ nodes
| test.jsx:6:24:6:38 | response.json() | semmle.label | response.json() |
| test.jsx:7:12:7:15 | data | semmle.label | data |
| test.jsx:15:11:17:5 | data | semmle.label | data |
| test.jsx:15:13:15:16 | data | semmle.label | data |
| test.jsx:27:29:27:32 | data | semmle.label | data |
| test.ts:8:9:8:79 | this.#h ... query') | semmle.label | this.#h ... query') |
| test.ts:20:28:20:35 | response | semmle.label | response |
Expand All @@ -76,6 +109,15 @@ nodes
| test.ts:24:43:24:55 | response.name | semmle.label | response.name |
| test.ts:24:67:24:74 | response | semmle.label | response |
| test.ts:24:67:24:84 | response.owner.bio | semmle.label | response.owner.bio |
| test.vue:7:11:13:6 | data | semmle.label | data |
| test.vue:7:45:7:48 | data | semmle.label | data |
| test.vue:10:15:10:84 | response | semmle.label | response |
| test.vue:10:26:10:84 | await f ... sts/1") | semmle.label | await f ... sts/1") |
| test.vue:10:32:10:84 | fetch(" ... sts/1") | semmle.label | fetch(" ... sts/1") |
| test.vue:11:16:11:23 | response | semmle.label | response |
| test.vue:11:16:11:30 | response.json() | semmle.label | response.json() |
| test.vue:15:21:15:24 | data | semmle.label | data |
| test.vue:22:10:22:22 | v-html=data | semmle.label | v-html=data |
| testReactRelay.tsx:5:9:5:52 | commentData | semmle.label | commentData |
| testReactRelay.tsx:5:23:5:52 | useFrag ... entRef) | semmle.label | useFrag ... entRef) |
| testReactRelay.tsx:7:43:7:53 | commentData | semmle.label | commentData |
Expand Down Expand Up @@ -111,4 +153,25 @@ nodes
| testReactRelay.tsx:136:9:136:39 | data | semmle.label | data |
| testReactRelay.tsx:136:16:136:39 | readFra ... y, key) | semmle.label | readFra ... y, key) |
| testReactRelay.tsx:137:50:137:53 | data | semmle.label | data |
| testReactUseQueries.jsx:4:9:4:53 | response | semmle.label | response |
| testReactUseQueries.jsx:4:20:4:53 | await f ... e.com') | semmle.label | await f ... e.com') |
| testReactUseQueries.jsx:4:26:4:53 | fetch(' ... e.com') | semmle.label | fetch(' ... e.com') |
| testReactUseQueries.jsx:5:10:5:17 | response | semmle.label | response |
| testReactUseQueries.jsx:5:10:5:24 | response.json() | semmle.label | response.json() |
| testReactUseQueries.jsx:37:25:37:38 | repoQuery.data | semmle.label | repoQuery.data |
| testUseQueries2.vue:6:11:6:63 | response | semmle.label | response |
| testUseQueries2.vue:6:22:6:63 | await f ... ntent") | semmle.label | await f ... ntent") |
| testUseQueries2.vue:6:28:6:63 | fetch(" ... ntent") | semmle.label | fetch(" ... ntent") |
| testUseQueries2.vue:7:11:7:38 | data | semmle.label | data |
| testUseQueries2.vue:7:18:7:38 | await r ... .json() | semmle.label | await r ... .json() |
| testUseQueries2.vue:7:24:7:31 | response | semmle.label | response |
| testUseQueries2.vue:7:24:7:38 | response.json() | semmle.label | response.json() |
| testUseQueries2.vue:8:12:8:15 | data | semmle.label | data |
| testUseQueries2.vue:12:11:12:41 | response | semmle.label | response |
| testUseQueries2.vue:12:22:12:41 | await fetch("${id}") | semmle.label | await fetch("${id}") |
| testUseQueries2.vue:12:28:12:41 | fetch("${id}") | semmle.label | fetch("${id}") |
| testUseQueries2.vue:13:12:13:19 | response | semmle.label | response |
| testUseQueries2.vue:13:12:13:26 | response.json() | semmle.label | response.json() |
| testUseQueries2.vue:33:22:33:36 | results[0].data | semmle.label | results[0].data |
| testUseQueries2.vue:40:10:40:23 | v-html=data3 | semmle.label | v-html=data3 |
subpaths
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<script>
import { useQuery, VueQueryClientProvider } from "@tanstack/vue-query";


export default {
data() {
const { isPending, isError, isFetching, data, error } = useQuery({
queryKey: ["post"],
queryFn: async () => {
const response = await fetch("https://jsonplaceholder.typicode.com/posts/1"); // $ Source
return response.json();
},
});

return { data : data };
}
}
</script>

<template>
<VueQueryClientProvider :client="queryClient">
<div v-html="data"></div> <!--$ Alert[js/xss] -->
</VueQueryClientProvider>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { useQueries } from '@tanstack/react-query';

const fetchRepoData = async () => {
const response = await fetch('https://example.com'); // $ Source
return response.json();
};

async function fetchPost() {
const response = await fetch("www.example.com"); // $ MISSING: Source
return response.json();
}

export default function UseQueriesComponent() {
const results = useQueries({
queries: [
{
queryKey: ['repoData'],
queryFn: fetchRepoData,
},
{
queryKey: ['repoData'],
queryFn: () => fetchPost,
},
],
});

const repoQuery = results[0];

if (repoQuery.isLoading) return <p>Loading...</p>;
if (repoQuery.isError) return <p>Error: {repoQuery.error.message}</p>;

return (
<div>
<h1>Content with Dangerous HTML</h1>
<div
dangerouslySetInnerHTML={{
__html: repoQuery.data, // $ Alert
}}
/>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<script>
import { useQueries } from "@tanstack/vue-query";

export default {
data() {
const ids = [1, 2, 3]
const results = useQueries({
queries: ids.map((id) => ({
queryKey: ['post', id],
queryFn: async () => {
const response = await fetch("${id}"); // $ MISSING: Source
return response.json();
},
staleTime: Infinity,
})),
});

return { data2 : results[0].data };
}
}
</script>

<template>
<VueQueryClientProvider :client="queryClient">
<div v-html="data2"></div> <!--$ MISSING: Alert -->
</VueQueryClientProvider>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<script>
import { useQueries } from "@tanstack/vue-query";
import { computed } from "vue";

const fetchContent = async () => {
const response = await fetch("https://example.com/content"); // $ Source
const data = await response.json();
return data;
};

async function fetchPost() {
const response = await fetch("${id}"); // $ Source
return response.json();
}

export default {
data() {
const results = useQueries({
queries: [
{
queryKey: ["post", 1],
queryFn: fetchContent,
staleTime: Infinity,
},
{
queryKey: ["post", 2],
queryFn: () => fetchPost(),
staleTime: Infinity,
},
],
});

return { data3 : results[0].data };
},
};
</script>

<template>
<VueQueryClientProvider :client="queryClient">
<div v-html="data3"></div> <!--$ Alert -->
</VueQueryClientProvider>
</template>