From 23fdc3534f8e4f88256898aee6116e199ae03a0b Mon Sep 17 00:00:00 2001 From: Napalys Date: Wed, 19 Mar 2025 12:31:54 +0100 Subject: [PATCH 1/6] Added test case `@apollo/server` with `SSRF`. --- .../Security/CWE-918/apollo.serverSide.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 javascript/ql/test/query-tests/Security/CWE-918/apollo.serverSide.ts diff --git a/javascript/ql/test/query-tests/Security/CWE-918/apollo.serverSide.ts b/javascript/ql/test/query-tests/Security/CWE-918/apollo.serverSide.ts new file mode 100644 index 000000000000..9417fa6972ef --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-918/apollo.serverSide.ts @@ -0,0 +1,14 @@ +import { ApolloServer } from '@apollo/server'; +import { get } from 'https'; + +function createApolloServer(typeDefs) { + const resolvers = { + Mutation: { + downloadFiles: async (_, { files }) => { // $ MISSING: Source[js/request-forgery] + files.forEach((file) => { get(file.url, (res) => {}); }); // $ MISSING: Alert[js/request-forgery] Sink[js/request-forgery] + return true; + }, + }, + }; + const server = new ApolloServer({typeDefs, resolvers}); +} From cb18408502dea2dd780adaf63a89fb6a69bcf1b5 Mon Sep 17 00:00:00 2001 From: Napalys Date: Wed, 19 Mar 2025 13:36:06 +0100 Subject: [PATCH 2/6] Added data as model for `ApolloServer`. --- javascript/ql/lib/ext/apollo-server.model.yml | 6 ++++++ .../Security/CWE-918/RequestForgery.expected | 12 ++++++++++++ .../Security/CWE-918/apollo.serverSide.ts | 4 ++-- 3 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 javascript/ql/lib/ext/apollo-server.model.yml diff --git a/javascript/ql/lib/ext/apollo-server.model.yml b/javascript/ql/lib/ext/apollo-server.model.yml new file mode 100644 index 000000000000..40e576c63cf4 --- /dev/null +++ b/javascript/ql/lib/ext/apollo-server.model.yml @@ -0,0 +1,6 @@ +extensions: + - addsTo: + pack: codeql/javascript-all + extensible: sourceModel + data: + - ["@apollo/server", "Member[ApolloServer].Argument[0].AnyMember.AnyMember.AnyMember.Parameter[1]", "remote"] diff --git a/javascript/ql/test/query-tests/Security/CWE-918/RequestForgery.expected b/javascript/ql/test/query-tests/Security/CWE-918/RequestForgery.expected index b62008204c69..a8ed8b29b4be 100644 --- a/javascript/ql/test/query-tests/Security/CWE-918/RequestForgery.expected +++ b/javascript/ql/test/query-tests/Security/CWE-918/RequestForgery.expected @@ -1,4 +1,5 @@ #select +| apollo.serverSide.ts:8:39:8:64 | get(fil ... => {}) | apollo.serverSide.ts:7:36:7:44 | { files } | apollo.serverSide.ts:8:43:8:50 | file.url | The $@ of this request depends on a $@. | apollo.serverSide.ts:8:43:8:50 | file.url | URL | apollo.serverSide.ts:7:36:7:44 | { files } | user-provided value | | serverSide.js:18:5:18:20 | request(tainted) | serverSide.js:14:29:14:35 | req.url | serverSide.js:18:13:18:19 | tainted | The $@ of this request depends on a $@. | serverSide.js:18:13:18:19 | tainted | URL | serverSide.js:14:29:14:35 | req.url | user-provided value | | serverSide.js:20:5:20:24 | request.get(tainted) | serverSide.js:14:29:14:35 | req.url | serverSide.js:20:17:20:23 | tainted | The $@ of this request depends on a $@. | serverSide.js:20:17:20:23 | tainted | URL | serverSide.js:14:29:14:35 | req.url | user-provided value | | serverSide.js:24:5:24:20 | request(options) | serverSide.js:14:29:14:35 | req.url | serverSide.js:23:19:23:25 | tainted | The $@ of this request depends on a $@. | serverSide.js:23:19:23:25 | tainted | URL | serverSide.js:14:29:14:35 | req.url | user-provided value | @@ -24,6 +25,11 @@ | serverSide.js:125:5:128:6 | axios({ ... \\n }) | serverSide.js:123:29:123:35 | req.url | serverSide.js:127:14:127:20 | tainted | The $@ of this request depends on a $@. | serverSide.js:127:14:127:20 | tainted | URL | serverSide.js:123:29:123:35 | req.url | user-provided value | | serverSide.js:131:5:131:20 | axios.get(myUrl) | serverSide.js:123:29:123:35 | req.url | serverSide.js:131:15:131:19 | myUrl | The $@ of this request depends on a $@. | serverSide.js:131:15:131:19 | myUrl | URL | serverSide.js:123:29:123:35 | req.url | user-provided value | edges +| apollo.serverSide.ts:7:36:7:44 | files | apollo.serverSide.ts:8:13:8:17 | files | provenance | | +| apollo.serverSide.ts:7:36:7:44 | { files } | apollo.serverSide.ts:7:36:7:44 | files | provenance | | +| apollo.serverSide.ts:8:13:8:17 | files | apollo.serverSide.ts:8:28:8:31 | file | provenance | | +| apollo.serverSide.ts:8:28:8:31 | file | apollo.serverSide.ts:8:43:8:46 | file | provenance | | +| apollo.serverSide.ts:8:43:8:46 | file | apollo.serverSide.ts:8:43:8:50 | file.url | provenance | | | serverSide.js:14:9:14:52 | tainted | serverSide.js:18:13:18:19 | tainted | provenance | | | serverSide.js:14:9:14:52 | tainted | serverSide.js:20:17:20:23 | tainted | provenance | | | serverSide.js:14:9:14:52 | tainted | serverSide.js:23:19:23:25 | tainted | provenance | | @@ -73,6 +79,12 @@ edges | serverSide.js:130:9:130:45 | myUrl | serverSide.js:131:15:131:19 | myUrl | provenance | | | serverSide.js:130:37:130:43 | tainted | serverSide.js:130:9:130:45 | myUrl | provenance | | nodes +| apollo.serverSide.ts:7:36:7:44 | files | semmle.label | files | +| apollo.serverSide.ts:7:36:7:44 | { files } | semmle.label | { files } | +| apollo.serverSide.ts:8:13:8:17 | files | semmle.label | files | +| apollo.serverSide.ts:8:28:8:31 | file | semmle.label | file | +| apollo.serverSide.ts:8:43:8:46 | file | semmle.label | file | +| apollo.serverSide.ts:8:43:8:50 | file.url | semmle.label | file.url | | serverSide.js:14:9:14:52 | tainted | semmle.label | tainted | | serverSide.js:14:19:14:42 | url.par ... , true) | semmle.label | url.par ... , true) | | serverSide.js:14:29:14:35 | req.url | semmle.label | req.url | diff --git a/javascript/ql/test/query-tests/Security/CWE-918/apollo.serverSide.ts b/javascript/ql/test/query-tests/Security/CWE-918/apollo.serverSide.ts index 9417fa6972ef..81ff939c7653 100644 --- a/javascript/ql/test/query-tests/Security/CWE-918/apollo.serverSide.ts +++ b/javascript/ql/test/query-tests/Security/CWE-918/apollo.serverSide.ts @@ -4,8 +4,8 @@ import { get } from 'https'; function createApolloServer(typeDefs) { const resolvers = { Mutation: { - downloadFiles: async (_, { files }) => { // $ MISSING: Source[js/request-forgery] - files.forEach((file) => { get(file.url, (res) => {}); }); // $ MISSING: Alert[js/request-forgery] Sink[js/request-forgery] + downloadFiles: async (_, { files }) => { // $ Source[js/request-forgery] + files.forEach((file) => { get(file.url, (res) => {}); }); // $ Alert[js/request-forgery] Sink[js/request-forgery] return true; }, }, From 056bf4fde7bc6983a0f7cb414213de061dea4911 Mon Sep 17 00:00:00 2001 From: Napalys Date: Thu, 20 Mar 2025 10:35:51 +0100 Subject: [PATCH 3/6] Added test case with inheretence. --- .../Security/CWE-918/apollo.serverSide.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/javascript/ql/test/query-tests/Security/CWE-918/apollo.serverSide.ts b/javascript/ql/test/query-tests/Security/CWE-918/apollo.serverSide.ts index 81ff939c7653..1d6aabd87fa5 100644 --- a/javascript/ql/test/query-tests/Security/CWE-918/apollo.serverSide.ts +++ b/javascript/ql/test/query-tests/Security/CWE-918/apollo.serverSide.ts @@ -11,4 +11,17 @@ function createApolloServer(typeDefs) { }, }; const server = new ApolloServer({typeDefs, resolvers}); + + const resolvers2 = { + Mutation: { + downloadFiles: async (_, { files }) => { // $ MISSING: Source[js/request-forgery] + files.forEach((file) => { get(file.url, (res) => {}); }); // $ MISSING: Alert[js/request-forgery] Sink[js/request-forgery] + return true; + }, + }, + }; + + class CustomApollo extends ApolloServer {} + + const srv = new CustomApollo({typeDefs, resolvers: resolvers2}); } From 3a243d221d5ed775eb69bfc6ba34f01f68b01d64 Mon Sep 17 00:00:00 2001 From: Napalys Date: Thu, 20 Mar 2025 10:52:09 +0100 Subject: [PATCH 4/6] Added aliases for `@apollo/server`. --- javascript/ql/lib/ext/apollo-server.model.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/javascript/ql/lib/ext/apollo-server.model.yml b/javascript/ql/lib/ext/apollo-server.model.yml index 40e576c63cf4..ffceb6a6d5af 100644 --- a/javascript/ql/lib/ext/apollo-server.model.yml +++ b/javascript/ql/lib/ext/apollo-server.model.yml @@ -3,4 +3,13 @@ extensions: pack: codeql/javascript-all extensible: sourceModel data: - - ["@apollo/server", "Member[ApolloServer].Argument[0].AnyMember.AnyMember.AnyMember.Parameter[1]", "remote"] + - ["@apollo/server", "Member[ApolloServer,ApolloServerBase].Argument[0].AnyMember.AnyMember.AnyMember.Parameter[1]", "remote"] + + - addsTo: + pack: codeql/javascript-all + extensible: typeModel + data: + - ["@apollo/server", "@apollo/server/standalone", ""] + - ["@apollo/server", "apollo-server-express", ""] + - ["@apollo/server", "apollo-server-core", ""] + - ["@apollo/server", "apollo-server", ""] From 7d40e449dba8ea42134579444645927b1d3913b6 Mon Sep 17 00:00:00 2001 From: Napalys Date: Thu, 20 Mar 2025 19:08:24 +0100 Subject: [PATCH 5/6] Added change note. --- javascript/ql/lib/change-notes/2025-03-20-apollo-server.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 javascript/ql/lib/change-notes/2025-03-20-apollo-server.md diff --git a/javascript/ql/lib/change-notes/2025-03-20-apollo-server.md b/javascript/ql/lib/change-notes/2025-03-20-apollo-server.md new file mode 100644 index 000000000000..35111bbd5056 --- /dev/null +++ b/javascript/ql/lib/change-notes/2025-03-20-apollo-server.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Added support for the `ApolloServer` from `@apollo/server` and similar packages. From 57f6225140e7b5f7a35dd8aa7f6c1e2f82268dfe Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Fri, 21 Mar 2025 09:11:25 +0100 Subject: [PATCH 6/6] Update javascript/ql/lib/change-notes/2025-03-20-apollo-server.md Co-authored-by: Asger F --- javascript/ql/lib/change-notes/2025-03-20-apollo-server.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/lib/change-notes/2025-03-20-apollo-server.md b/javascript/ql/lib/change-notes/2025-03-20-apollo-server.md index 35111bbd5056..1976b91ea387 100644 --- a/javascript/ql/lib/change-notes/2025-03-20-apollo-server.md +++ b/javascript/ql/lib/change-notes/2025-03-20-apollo-server.md @@ -1,4 +1,4 @@ --- category: minorAnalysis --- -* Added support for the `ApolloServer` from `@apollo/server` and similar packages. +* Added support for the `ApolloServer` class from `@apollo/server` and similar packages. In particular, the incoming data in a GraphQL resolver is now seen as a source of untrusted user input.