From 7712ca368aae8c3a604f348d19c4e6c9a7a769b7 Mon Sep 17 00:00:00 2001 From: Napalys Date: Thu, 13 Mar 2025 08:20:05 +0100 Subject: [PATCH 01/10] Added `useQuery` `tanstack-vue` test case --- .../Xss.expected | 3 +++ .../DomBasedXssWithResponseThreat/test.vue | 24 +++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/test.vue diff --git a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/Xss.expected b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/Xss.expected index 313febe21946..ccbea1a798b9 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/Xss.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/Xss.expected @@ -112,3 +112,6 @@ nodes | testReactRelay.tsx:136:16:136:39 | readFra ... y, key) | semmle.label | readFra ... y, key) | | testReactRelay.tsx:137:50:137:53 | data | semmle.label | data | subpaths +testFailures +| test.vue:10:87:10:97 | // $ Source | Missing result: Source | +| test.vue:22:31:22:53 | | Missing result: Alert[js/xss] | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/test.vue b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/test.vue new file mode 100644 index 000000000000..0db6637a8cb5 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/test.vue @@ -0,0 +1,24 @@ + + + From 0c0158899e45b4d50b009befe1f42e2b66b6ab8e Mon Sep 17 00:00:00 2001 From: Napalys Date: Thu, 13 Mar 2025 08:21:10 +0100 Subject: [PATCH 02/10] Added `tanstack-vue` `useQuery` modeling --- javascript/ql/lib/ext/tanstack.model.yml | 1 + .../Xss.expected | 21 ++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/javascript/ql/lib/ext/tanstack.model.yml b/javascript/ql/lib/ext/tanstack.model.yml index 19b57eae6124..9655bca5dc0d 100644 --- a/javascript/ql/lib/ext/tanstack.model.yml +++ b/javascript/ql/lib/ext/tanstack.model.yml @@ -5,3 +5,4 @@ extensions: 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/vue-query", "Member[useQuery]", "Argument[0].Member[queryFn].ReturnValue.Awaited", "ReturnValue.Member[data]", "taint"] diff --git a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/Xss.expected b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/Xss.expected index ccbea1a798b9..fb3ceceb0550 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/Xss.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/Xss.expected @@ -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 | `

${ ... o}

` | test.ts:8:9:8:79 | this.#h ... query') | test.ts:24:36:24:90 | `

${ ... o}

` | 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 | @@ -31,6 +32,14 @@ edges | test.ts:24:43:24:55 | response.name | test.ts:24:36:24:90 | `

${ ... o}

` | 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 | `

${ ... o}

` | 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 | | @@ -76,6 +85,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 | @@ -112,6 +130,3 @@ nodes | testReactRelay.tsx:136:16:136:39 | readFra ... y, key) | semmle.label | readFra ... y, key) | | testReactRelay.tsx:137:50:137:53 | data | semmle.label | data | subpaths -testFailures -| test.vue:10:87:10:97 | // $ Source | Missing result: Source | -| test.vue:22:31:22:53 | | Missing result: Alert[js/xss] | From 4917d64ce71223e6708a53015f85bc09f6651de8 Mon Sep 17 00:00:00 2001 From: Napalys Date: Thu, 13 Mar 2025 11:39:35 +0100 Subject: [PATCH 03/10] Added test cases for `tanstack-vue` `useQueries`. --- .../Xss.expected | 3 ++ .../testUseQueries.vue | 27 ++++++++++++++ .../testUseQueries2.vue | 37 +++++++++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/testUseQueries.vue create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/testUseQueries2.vue diff --git a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/Xss.expected b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/Xss.expected index fb3ceceb0550..a6a305505f06 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/Xss.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/Xss.expected @@ -130,3 +130,6 @@ nodes | testReactRelay.tsx:136:16:136:39 | readFra ... y, key) | semmle.label | readFra ... y, key) | | testReactRelay.tsx:137:50:137:53 | data | semmle.label | data | subpaths +testFailures +| testUseQueries2.vue:6:66:6:76 | // $ Source | Missing result: Source | +| testUseQueries2.vue:35:32:35:46 | | Missing result: Alert | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/testUseQueries.vue b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/testUseQueries.vue new file mode 100644 index 000000000000..c02c0a79023f --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/testUseQueries.vue @@ -0,0 +1,27 @@ + + + diff --git a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/testUseQueries2.vue b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/testUseQueries2.vue new file mode 100644 index 000000000000..5ed896cf99d1 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/testUseQueries2.vue @@ -0,0 +1,37 @@ + + + From 6c9aa0e872ecf458544458fb2b95177f9c51ccb1 Mon Sep 17 00:00:00 2001 From: Napalys Date: Thu, 13 Mar 2025 11:40:44 +0100 Subject: [PATCH 04/10] Added modeling of `tanstack-vue` `useQueries`. --- javascript/ql/lib/ext/tanstack.model.yml | 1 + .../Xss.expected | 23 ++++++++++++++++--- .../testUseQueries2.vue | 7 +++++- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/javascript/ql/lib/ext/tanstack.model.yml b/javascript/ql/lib/ext/tanstack.model.yml index 9655bca5dc0d..f42a9bd17198 100644 --- a/javascript/ql/lib/ext/tanstack.model.yml +++ b/javascript/ql/lib/ext/tanstack.model.yml @@ -6,3 +6,4 @@ extensions: - ["@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/vue-query", "Member[useQuery]", "Argument[0].Member[queryFn].ReturnValue.Awaited", "ReturnValue.Member[data]", "taint"] + - ["@tanstack/vue-query", "Member[useQueries]", "Argument[0].Member[queries].ArrayElement.Member[queryFn].ReturnValue.Awaited", "ReturnValue.AnyMember.Member[data]", "taint"] diff --git a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/Xss.expected b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/Xss.expected index a6a305505f06..8d1c98ab4a9d 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/Xss.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/Xss.expected @@ -13,6 +13,7 @@ | 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 | +| 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 | 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 | | @@ -65,6 +66,15 @@ 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 | | +| 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: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") | @@ -129,7 +139,14 @@ 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 | +| 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: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 -testFailures -| testUseQueries2.vue:6:66:6:76 | // $ Source | Missing result: Source | -| testUseQueries2.vue:35:32:35:46 | | Missing result: Alert | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/testUseQueries2.vue b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/testUseQueries2.vue index 5ed896cf99d1..571d245b2335 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/testUseQueries2.vue +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/testUseQueries2.vue @@ -8,6 +8,11 @@ const fetchContent = async () => { return data; }; +async function fetchPost() { + const response = await fetch("${id}"); // $ MISSING: Source + return response.json(); +} + export default { data() { const results = useQueries({ @@ -19,7 +24,7 @@ export default { }, { queryKey: ["post", 2], - queryFn: () => fetchPost(2), + queryFn: () => fetchPost(), staleTime: Infinity, }, ], From 03330ef24d2920ed73adbbea9d8ff1a22c6c77d3 Mon Sep 17 00:00:00 2001 From: Napalys Date: Thu, 13 Mar 2025 11:59:11 +0100 Subject: [PATCH 05/10] Added test cases for `tanstack-react` `useQueries`. --- .../testReactUseQueries.jsx | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/testReactUseQueries.jsx diff --git a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/testReactUseQueries.jsx b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/testReactUseQueries.jsx new file mode 100644 index 000000000000..cd8fccad0ca1 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/testReactUseQueries.jsx @@ -0,0 +1,42 @@ +import { useQueries } from '@tanstack/react-query'; + +const fetchRepoData = async () => { + const response = await fetch('https://example.com'); // $ MISSING: 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

Loading...

; + if (repoQuery.isError) return

Error: {repoQuery.error.message}

; + + return ( +
+

Content with Dangerous HTML

+
+
+); +} From 3640e5e4252dfe874eaa48f41896bcecc036b4ef Mon Sep 17 00:00:00 2001 From: Napalys Date: Thu, 13 Mar 2025 12:03:01 +0100 Subject: [PATCH 06/10] Added model for `tanstack-react` `useQueries` --- javascript/ql/lib/ext/tanstack.model.yml | 1 + .../DomBasedXssWithResponseThreat/Xss.expected | 12 ++++++++++++ .../testReactUseQueries.jsx | 4 ++-- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/javascript/ql/lib/ext/tanstack.model.yml b/javascript/ql/lib/ext/tanstack.model.yml index f42a9bd17198..b9498a6af326 100644 --- a/javascript/ql/lib/ext/tanstack.model.yml +++ b/javascript/ql/lib/ext/tanstack.model.yml @@ -7,3 +7,4 @@ extensions: - ["@tanstack/angular-query", "Member[injectQuery]", "Argument[0].ReturnValue.Member[queryFn].ReturnValue", "ReturnValue.Member[data].Awaited", "taint"] - ["@tanstack/vue-query", "Member[useQuery]", "Argument[0].Member[queryFn].ReturnValue.Awaited", "ReturnValue.Member[data]", "taint"] - ["@tanstack/vue-query", "Member[useQueries]", "Argument[0].Member[queries].ArrayElement.Member[queryFn].ReturnValue.Awaited", "ReturnValue.AnyMember.Member[data]", "taint"] + - ["@tanstack/react-query", "Member[useQueries]", "Argument[0].Member[queries].ArrayElement.Member[queryFn].ReturnValue.Awaited", "ReturnValue.AnyMember.Member[data]", "taint"] diff --git a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/Xss.expected b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/Xss.expected index 8d1c98ab4a9d..7c5081156a87 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/Xss.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/Xss.expected @@ -13,6 +13,7 @@ | 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 | edges | test.jsx:5:11:5:63 | response | test.jsx:6:24:6:31 | response | provenance | | @@ -66,6 +67,11 @@ 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 | | @@ -139,6 +145,12 @@ 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") | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/testReactUseQueries.jsx b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/testReactUseQueries.jsx index cd8fccad0ca1..d978e309c0f1 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/testReactUseQueries.jsx +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/testReactUseQueries.jsx @@ -1,7 +1,7 @@ import { useQueries } from '@tanstack/react-query'; const fetchRepoData = async () => { - const response = await fetch('https://example.com'); // $ MISSING: Source + const response = await fetch('https://example.com'); // $ Source return response.json(); }; @@ -34,7 +34,7 @@ export default function UseQueriesComponent() {

Content with Dangerous HTML

From 5dff23de6b302bd15429e96a61dc6cca3b81454b Mon Sep 17 00:00:00 2001 From: Napalys Date: Thu, 13 Mar 2025 12:09:07 +0100 Subject: [PATCH 07/10] Added change note. --- javascript/ql/lib/change-notes/2025-03-13-tanstack-vue.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 javascript/ql/lib/change-notes/2025-03-13-tanstack-vue.md diff --git a/javascript/ql/lib/change-notes/2025-03-13-tanstack-vue.md b/javascript/ql/lib/change-notes/2025-03-13-tanstack-vue.md new file mode 100644 index 000000000000..defc6c78bc2a --- /dev/null +++ b/javascript/ql/lib/change-notes/2025-03-13-tanstack-vue.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Added support for the `@tanstack/vue-query` package. From 66737402c20fc83b3d3a654dcccf4b26c901b100 Mon Sep 17 00:00:00 2001 From: Napalys Date: Fri, 14 Mar 2025 10:50:10 +0100 Subject: [PATCH 08/10] Updated test ouput with fixes from main. --- .../DomBasedXssWithResponseThreat/Xss.expected | 11 +++++++++++ .../DomBasedXssWithResponseThreat/testUseQueries2.vue | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/Xss.expected b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/Xss.expected index 7c5081156a87..ad5dec8849d7 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/Xss.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/Xss.expected @@ -15,6 +15,7 @@ | 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 | | @@ -80,6 +81,11 @@ edges | 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 | @@ -159,6 +165,11 @@ nodes | 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 diff --git a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/testUseQueries2.vue b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/testUseQueries2.vue index 571d245b2335..8515e2d33ff8 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/testUseQueries2.vue +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/testUseQueries2.vue @@ -9,7 +9,7 @@ const fetchContent = async () => { }; async function fetchPost() { - const response = await fetch("${id}"); // $ MISSING: Source + const response = await fetch("${id}"); // $ Source return response.json(); } From d40ef0ddae56f30021344cf195f39a0a3f0a7394 Mon Sep 17 00:00:00 2001 From: Napalys Date: Fri, 14 Mar 2025 13:48:15 +0100 Subject: [PATCH 09/10] Changed from `taint` to `value` steps. Co-authored-by: Asgerf --- javascript/ql/lib/ext/tanstack.model.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/javascript/ql/lib/ext/tanstack.model.yml b/javascript/ql/lib/ext/tanstack.model.yml index b9498a6af326..4db3702b2314 100644 --- a/javascript/ql/lib/ext/tanstack.model.yml +++ b/javascript/ql/lib/ext/tanstack.model.yml @@ -3,8 +3,8 @@ 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/vue-query", "Member[useQuery]", "Argument[0].Member[queryFn].ReturnValue.Awaited", "ReturnValue.Member[data]", "taint"] - - ["@tanstack/vue-query", "Member[useQueries]", "Argument[0].Member[queries].ArrayElement.Member[queryFn].ReturnValue.Awaited", "ReturnValue.AnyMember.Member[data]", "taint"] - - ["@tanstack/react-query", "Member[useQueries]", "Argument[0].Member[queries].ArrayElement.Member[queryFn].ReturnValue.Awaited", "ReturnValue.AnyMember.Member[data]", "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"] From 933f3c6f77d7484a5887b07e7022f6f04c0d687d Mon Sep 17 00:00:00 2001 From: Napalys Date: Fri, 14 Mar 2025 13:52:05 +0100 Subject: [PATCH 10/10] Refactor Tanstack integration: remove Tanstack framework and added model as data for it instead. --- javascript/ql/lib/ext/tanstack.model.yml | 1 + javascript/ql/lib/javascript.qll | 1 - .../semmle/javascript/frameworks/Tanstack.qll | 26 ------------------- .../Xss.expected | 4 ++- 4 files changed, 4 insertions(+), 28 deletions(-) delete mode 100644 javascript/ql/lib/semmle/javascript/frameworks/Tanstack.qll diff --git a/javascript/ql/lib/ext/tanstack.model.yml b/javascript/ql/lib/ext/tanstack.model.yml index 4db3702b2314..d2b0bc0d782f 100644 --- a/javascript/ql/lib/ext/tanstack.model.yml +++ b/javascript/ql/lib/ext/tanstack.model.yml @@ -8,3 +8,4 @@ extensions: - ["@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"] diff --git a/javascript/ql/lib/javascript.qll b/javascript/ql/lib/javascript.qll index b3bf7399a621..7bb2b7676105 100644 --- a/javascript/ql/lib/javascript.qll +++ b/javascript/ql/lib/javascript.qll @@ -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 diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Tanstack.qll b/javascript/ql/lib/semmle/javascript/frameworks/Tanstack.qll deleted file mode 100644 index 741079575963..000000000000 --- a/javascript/ql/lib/semmle/javascript/frameworks/Tanstack.qll +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Provides classes and predicates modeling the Tanstack/react-query library. - */ - -private import javascript - -/** - * An additional flow step that propagates data from the return value of the query function, - * defined in a useQuery call from the '@tanstack/react-query' module, to the 'data' property. - */ -private class TanstackStep extends DataFlow::AdditionalFlowStep { - override predicate step(DataFlow::Node node1, DataFlow::Node node2) { - exists(API::CallNode useQuery | - useQuery = useQueryCall() and - node1 = useQuery.getParameter(0).getMember("queryFn").getReturn().getPromised().asSink() and - node2 = useQuery.getReturn().getMember("data").asSource() - ) - } -} - -/** - * Retrieves a call node representing a useQuery invocation from the '@tanstack/react-query' module. - */ -private API::CallNode useQueryCall() { - result = API::moduleImport("@tanstack/react-query").getMember("useQuery").getACall() -} diff --git a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/Xss.expected b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/Xss.expected index ad5dec8849d7..7aa3957d64d8 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/Xss.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXssWithResponseThreat/Xss.expected @@ -24,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 | | @@ -97,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 |