From fda74cbe9e1f1ef89195afa7661041446c0053e6 Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Mon, 28 Jul 2025 17:35:29 +0200 Subject: [PATCH 01/13] Added test cases for v2 and v3 sql injection of dynamodb --- .../Security/CWE-089/untyped/dynamodb.js | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 javascript/ql/test/query-tests/Security/CWE-089/untyped/dynamodb.js diff --git a/javascript/ql/test/query-tests/Security/CWE-089/untyped/dynamodb.js b/javascript/ql/test/query-tests/Security/CWE-089/untyped/dynamodb.js new file mode 100644 index 000000000000..0f2d4bbd5fc5 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-089/untyped/dynamodb.js @@ -0,0 +1,73 @@ +import {DynamoDBClient, ExecuteStatementCommand, BatchExecuteStatementCommand, DynamoDB} from "@aws-sdk/client-dynamodb"; +const express = require('express'); + +const app = express(); +const region = 'us-east-1'; + +app.post('/partiql/v3/execute', async (req, res) => { + const client = new DynamoDBClient({}); + let maliciousInput = req.body.data; // $ MISSING: Source + + const statement = `SELECT * FROM Users WHERE username = '${maliciousInput}'`; + const command = new ExecuteStatementCommand({ + Statement: statement + }); + await client.send(command); // $ MISSING: Alert + + const updateStatement = "UPDATE Users SET status = 'active' WHERE id = " + maliciousInput; + const updateCommand = new ExecuteStatementCommand({ + Statement: updateStatement + }); + await client.send(updateCommand); // $ MISSING: Alert + + + const batchInput = { + Statements: [{ + Statement: `SELECT * FROM Users WHERE username = '${maliciousInput}'` + }, + { + Statement: "UPDATE Users SET role = 'user' WHERE username = bob" + } + ] + }; + + const batchCommand = new BatchExecuteStatementCommand(batchInput); + await client.send(batchCommand); // $ MISSING: Alert + + const batchInput2 = { + Statements: maliciousInput.map(input => ({ + Statement: `SELECT * FROM SensitiveData WHERE username = '${input}'` + })) + }; + + const batchCommand2 = new BatchExecuteStatementCommand(batchInput2); + await client.send(batchCommand2); // $ MISSING: Alert + + const client2 = new DynamoDB({}); + await client2.send(command); // $ MISSING: Alert + await client2.send(batchCommand); // $ MISSING: Alert +}); + +app.post('/partiql/v2/execute', async (req, res) => { + const AWS = require('aws-sdk'); + const dynamodb = new AWS.DynamoDB({ + region: 'us-east-1' + }); + let maliciousInput = req.body.data; // $ MISSING: Source + const params = { + Statement: `SELECT * FROM Users WHERE username = '${maliciousInput}'` + }; + + dynamodb.executeStatement(params, function(err, data) {}); // $ MISSING: Alert + const params2 = { + Statements: [{ + Statement: `SELECT * FROM Users WHERE username = '${maliciousInput}'` + }, + { + Statement: `SELECT * FROM Users WHERE username = '${maliciousInput}'` + } + ] + }; + + dynamodb.batchExecuteStatement(params2, function(err, data) {}); // $ MISSING: Alert +}); From 94fb6060afae4c7113942f876a35046dcf7f8491 Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Mon, 28 Jul 2025 17:41:34 +0200 Subject: [PATCH 02/13] Added modeling of dynamodb v3 for sql injections --- javascript/ql/lib/ext/dynamodb.model.yml | 19 ++++++++++ .../CWE-089/untyped/SqlInjection.expected | 36 +++++++++++++++++++ .../Security/CWE-089/untyped/dynamodb.js | 8 ++--- 3 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 javascript/ql/lib/ext/dynamodb.model.yml diff --git a/javascript/ql/lib/ext/dynamodb.model.yml b/javascript/ql/lib/ext/dynamodb.model.yml new file mode 100644 index 000000000000..218ae7f217e3 --- /dev/null +++ b/javascript/ql/lib/ext/dynamodb.model.yml @@ -0,0 +1,19 @@ +extensions: + - addsTo: + pack: codeql/javascript-all + extensible: sinkModel + data: + - ["DynamoDBClientV3", "ReturnValue.Member[send].Argument[0]", "sql-injection"] + + - addsTo: + pack: codeql/javascript-all + extensible: summaryModel + data: + - ["@aws-sdk/client-dynamodb", "Member[ExecuteStatementCommand]", "Argument[0].Member[Statement]", "ReturnValue", "taint"] + - ["@aws-sdk/client-dynamodb", "Member[BatchExecuteStatementCommand]", "Argument[0].Member[Statements].ArrayElement.Member[Statement]", "ReturnValue", "taint"] + + - addsTo: + pack: codeql/javascript-all + extensible: typeModel + data: + - ["DynamoDBClientV3", "@aws-sdk/client-dynamodb", "Member[DynamoDBClient,DynamoDB]"] diff --git a/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected b/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected index 843d41eb9229..50f75a0803c4 100644 --- a/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected @@ -1,4 +1,7 @@ #select +| dynamodb.js:15:23:15:29 | command | dynamodb.js:9:26:9:33 | req.body | dynamodb.js:15:23:15:29 | command | This query string depends on a $@. | dynamodb.js:9:26:9:33 | req.body | user-provided value | +| dynamodb.js:21:23:21:35 | updateCommand | dynamodb.js:9:26:9:33 | req.body | dynamodb.js:21:23:21:35 | updateCommand | This query string depends on a $@. | dynamodb.js:9:26:9:33 | req.body | user-provided value | +| dynamodb.js:47:24:47:30 | command | dynamodb.js:9:26:9:33 | req.body | dynamodb.js:47:24:47:30 | command | This query string depends on a $@. | dynamodb.js:9:26:9:33 | req.body | user-provided value | | graphql.js:9:34:19:5 | `\\n ... }\\n ` | graphql.js:8:16:8:28 | req.params.id | graphql.js:9:34:19:5 | `\\n ... }\\n ` | This query string depends on a $@. | graphql.js:8:16:8:28 | req.params.id | user-provided value | | graphql.js:26:30:26:40 | `foo ${id}` | graphql.js:25:16:25:28 | req.params.id | graphql.js:26:30:26:40 | `foo ${id}` | This query string depends on a $@. | graphql.js:25:16:25:28 | req.params.id | user-provided value | | graphql.js:29:32:29:42 | `foo ${id}` | graphql.js:25:16:25:28 | req.params.id | graphql.js:29:32:29:42 | `foo ${id}` | This query string depends on a $@. | graphql.js:25:16:25:28 | req.params.id | user-provided value | @@ -137,6 +140,22 @@ | tst4.js:8:10:8:66 | 'SELECT ... d + '"' | tst4.js:8:46:8:60 | $routeParams.id | tst4.js:8:10:8:66 | 'SELECT ... d + '"' | This query string depends on a $@. | tst4.js:8:46:8:60 | $routeParams.id | user-provided value | | tst.js:10:10:10:64 | 'SELECT ... d + '"' | tst.js:10:46:10:58 | req.params.id | tst.js:10:10:10:64 | 'SELECT ... d + '"' | This query string depends on a $@. | tst.js:10:46:10:58 | req.params.id | user-provided value | edges +| dynamodb.js:9:9:9:38 | maliciousInput | dynamodb.js:11:64:11:77 | maliciousInput | provenance | | +| dynamodb.js:9:9:9:38 | maliciousInput | dynamodb.js:17:80:17:93 | maliciousInput | provenance | | +| dynamodb.js:9:26:9:33 | req.body | dynamodb.js:9:9:9:38 | maliciousInput | provenance | | +| dynamodb.js:11:11:11:80 | statement | dynamodb.js:13:20:13:28 | statement | provenance | | +| dynamodb.js:11:64:11:77 | maliciousInput | dynamodb.js:11:11:11:80 | statement | provenance | | +| dynamodb.js:12:11:14:6 | command | dynamodb.js:15:23:15:29 | command | provenance | | +| dynamodb.js:12:11:14:6 | command | dynamodb.js:47:24:47:30 | command | provenance | | +| dynamodb.js:12:21:14:6 | new Exe ... \\n }) | dynamodb.js:12:11:14:6 | command | provenance | | +| dynamodb.js:12:49:14:5 | {\\n ... t\\n } [Statement] | dynamodb.js:12:21:14:6 | new Exe ... \\n }) | provenance | | +| dynamodb.js:13:20:13:28 | statement | dynamodb.js:12:49:14:5 | {\\n ... t\\n } [Statement] | provenance | | +| dynamodb.js:17:11:17:93 | updateStatement | dynamodb.js:19:20:19:34 | updateStatement | provenance | | +| dynamodb.js:17:80:17:93 | maliciousInput | dynamodb.js:17:11:17:93 | updateStatement | provenance | | +| dynamodb.js:18:11:20:6 | updateCommand | dynamodb.js:21:23:21:35 | updateCommand | provenance | | +| dynamodb.js:18:27:20:6 | new Exe ... \\n }) | dynamodb.js:18:11:20:6 | updateCommand | provenance | | +| dynamodb.js:18:55:20:5 | {\\n ... t\\n } [Statement] | dynamodb.js:18:27:20:6 | new Exe ... \\n }) | provenance | | +| dynamodb.js:19:20:19:34 | updateStatement | dynamodb.js:18:55:20:5 | {\\n ... t\\n } [Statement] | provenance | | | graphql.js:8:11:8:28 | id | graphql.js:11:46:11:47 | id | provenance | | | graphql.js:8:16:8:28 | req.params.id | graphql.js:8:11:8:28 | id | provenance | | | graphql.js:11:46:11:47 | id | graphql.js:9:34:19:5 | `\\n ... }\\n ` | provenance | | @@ -518,6 +537,23 @@ edges | tst4.js:8:46:8:60 | $routeParams.id | tst4.js:8:10:8:66 | 'SELECT ... d + '"' | provenance | | | tst.js:10:46:10:58 | req.params.id | tst.js:10:10:10:64 | 'SELECT ... d + '"' | provenance | | nodes +| dynamodb.js:9:9:9:38 | maliciousInput | semmle.label | maliciousInput | +| dynamodb.js:9:26:9:33 | req.body | semmle.label | req.body | +| dynamodb.js:11:11:11:80 | statement | semmle.label | statement | +| dynamodb.js:11:64:11:77 | maliciousInput | semmle.label | maliciousInput | +| dynamodb.js:12:11:14:6 | command | semmle.label | command | +| dynamodb.js:12:21:14:6 | new Exe ... \\n }) | semmle.label | new Exe ... \\n }) | +| dynamodb.js:12:49:14:5 | {\\n ... t\\n } [Statement] | semmle.label | {\\n ... t\\n } [Statement] | +| dynamodb.js:13:20:13:28 | statement | semmle.label | statement | +| dynamodb.js:15:23:15:29 | command | semmle.label | command | +| dynamodb.js:17:11:17:93 | updateStatement | semmle.label | updateStatement | +| dynamodb.js:17:80:17:93 | maliciousInput | semmle.label | maliciousInput | +| dynamodb.js:18:11:20:6 | updateCommand | semmle.label | updateCommand | +| dynamodb.js:18:27:20:6 | new Exe ... \\n }) | semmle.label | new Exe ... \\n }) | +| dynamodb.js:18:55:20:5 | {\\n ... t\\n } [Statement] | semmle.label | {\\n ... t\\n } [Statement] | +| dynamodb.js:19:20:19:34 | updateStatement | semmle.label | updateStatement | +| dynamodb.js:21:23:21:35 | updateCommand | semmle.label | updateCommand | +| dynamodb.js:47:24:47:30 | command | semmle.label | command | | graphql.js:8:11:8:28 | id | semmle.label | id | | graphql.js:8:16:8:28 | req.params.id | semmle.label | req.params.id | | graphql.js:9:34:19:5 | `\\n ... }\\n ` | semmle.label | `\\n ... }\\n ` | diff --git a/javascript/ql/test/query-tests/Security/CWE-089/untyped/dynamodb.js b/javascript/ql/test/query-tests/Security/CWE-089/untyped/dynamodb.js index 0f2d4bbd5fc5..8b4eddc4bd76 100644 --- a/javascript/ql/test/query-tests/Security/CWE-089/untyped/dynamodb.js +++ b/javascript/ql/test/query-tests/Security/CWE-089/untyped/dynamodb.js @@ -6,19 +6,19 @@ const region = 'us-east-1'; app.post('/partiql/v3/execute', async (req, res) => { const client = new DynamoDBClient({}); - let maliciousInput = req.body.data; // $ MISSING: Source + let maliciousInput = req.body.data; // $ Source const statement = `SELECT * FROM Users WHERE username = '${maliciousInput}'`; const command = new ExecuteStatementCommand({ Statement: statement }); - await client.send(command); // $ MISSING: Alert + await client.send(command); // $ Alert const updateStatement = "UPDATE Users SET status = 'active' WHERE id = " + maliciousInput; const updateCommand = new ExecuteStatementCommand({ Statement: updateStatement }); - await client.send(updateCommand); // $ MISSING: Alert + await client.send(updateCommand); // $ Alert const batchInput = { @@ -44,7 +44,7 @@ app.post('/partiql/v3/execute', async (req, res) => { await client.send(batchCommand2); // $ MISSING: Alert const client2 = new DynamoDB({}); - await client2.send(command); // $ MISSING: Alert + await client2.send(command); // $ Alert await client2.send(batchCommand); // $ MISSING: Alert }); From b2d182117c07798d94b9f87a1d397a93e58339e7 Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Mon, 28 Jul 2025 17:53:16 +0200 Subject: [PATCH 03/13] Added modeling for V2 of dynamoDB --- javascript/ql/lib/ext/dynamodb.model.yml | 3 +++ .../CWE-089/untyped/SqlInjection.expected | 18 ++++++++++++++++++ .../Security/CWE-089/untyped/dynamodb.js | 12 ++++++------ 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/javascript/ql/lib/ext/dynamodb.model.yml b/javascript/ql/lib/ext/dynamodb.model.yml index 218ae7f217e3..ddbf6cc3eee1 100644 --- a/javascript/ql/lib/ext/dynamodb.model.yml +++ b/javascript/ql/lib/ext/dynamodb.model.yml @@ -4,6 +4,8 @@ extensions: extensible: sinkModel data: - ["DynamoDBClientV3", "ReturnValue.Member[send].Argument[0]", "sql-injection"] + - ["DynamoDBClientV2", "ReturnValue.Member[executeStatement].Argument[0].Member[Statement]", "sql-injection"] + - ["DynamoDBClientV2", "ReturnValue.Member[batchExecuteStatement].Argument[0].Member[Statements].ArrayElement.Member[Statement]", "sql-injection"] - addsTo: pack: codeql/javascript-all @@ -17,3 +19,4 @@ extensions: extensible: typeModel data: - ["DynamoDBClientV3", "@aws-sdk/client-dynamodb", "Member[DynamoDBClient,DynamoDB]"] + - ["DynamoDBClientV2", "aws-sdk", "Member[DynamoDB]"] diff --git a/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected b/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected index 50f75a0803c4..ce92511226a9 100644 --- a/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected @@ -2,6 +2,9 @@ | dynamodb.js:15:23:15:29 | command | dynamodb.js:9:26:9:33 | req.body | dynamodb.js:15:23:15:29 | command | This query string depends on a $@. | dynamodb.js:9:26:9:33 | req.body | user-provided value | | dynamodb.js:21:23:21:35 | updateCommand | dynamodb.js:9:26:9:33 | req.body | dynamodb.js:21:23:21:35 | updateCommand | This query string depends on a $@. | dynamodb.js:9:26:9:33 | req.body | user-provided value | | dynamodb.js:47:24:47:30 | command | dynamodb.js:9:26:9:33 | req.body | dynamodb.js:47:24:47:30 | command | This query string depends on a $@. | dynamodb.js:9:26:9:33 | req.body | user-provided value | +| dynamodb.js:58:20:58:77 | `SELECT ... nput}'` | dynamodb.js:56:26:56:33 | req.body | dynamodb.js:58:20:58:77 | `SELECT ... nput}'` | This query string depends on a $@. | dynamodb.js:56:26:56:33 | req.body | user-provided value | +| dynamodb.js:64:28:64:85 | `SELECT ... nput}'` | dynamodb.js:56:26:56:33 | req.body | dynamodb.js:64:28:64:85 | `SELECT ... nput}'` | This query string depends on a $@. | dynamodb.js:56:26:56:33 | req.body | user-provided value | +| dynamodb.js:67:28:67:85 | `SELECT ... nput}'` | dynamodb.js:56:26:56:33 | req.body | dynamodb.js:67:28:67:85 | `SELECT ... nput}'` | This query string depends on a $@. | dynamodb.js:56:26:56:33 | req.body | user-provided value | | graphql.js:9:34:19:5 | `\\n ... }\\n ` | graphql.js:8:16:8:28 | req.params.id | graphql.js:9:34:19:5 | `\\n ... }\\n ` | This query string depends on a $@. | graphql.js:8:16:8:28 | req.params.id | user-provided value | | graphql.js:26:30:26:40 | `foo ${id}` | graphql.js:25:16:25:28 | req.params.id | graphql.js:26:30:26:40 | `foo ${id}` | This query string depends on a $@. | graphql.js:25:16:25:28 | req.params.id | user-provided value | | graphql.js:29:32:29:42 | `foo ${id}` | graphql.js:25:16:25:28 | req.params.id | graphql.js:29:32:29:42 | `foo ${id}` | This query string depends on a $@. | graphql.js:25:16:25:28 | req.params.id | user-provided value | @@ -156,6 +159,13 @@ edges | dynamodb.js:18:27:20:6 | new Exe ... \\n }) | dynamodb.js:18:11:20:6 | updateCommand | provenance | | | dynamodb.js:18:55:20:5 | {\\n ... t\\n } [Statement] | dynamodb.js:18:27:20:6 | new Exe ... \\n }) | provenance | | | dynamodb.js:19:20:19:34 | updateStatement | dynamodb.js:18:55:20:5 | {\\n ... t\\n } [Statement] | provenance | | +| dynamodb.js:56:9:56:38 | maliciousInput | dynamodb.js:58:61:58:74 | maliciousInput | provenance | | +| dynamodb.js:56:9:56:38 | maliciousInput | dynamodb.js:64:69:64:82 | maliciousInput | provenance | | +| dynamodb.js:56:9:56:38 | maliciousInput | dynamodb.js:67:69:67:82 | maliciousInput | provenance | | +| dynamodb.js:56:26:56:33 | req.body | dynamodb.js:56:9:56:38 | maliciousInput | provenance | | +| dynamodb.js:58:61:58:74 | maliciousInput | dynamodb.js:58:20:58:77 | `SELECT ... nput}'` | provenance | | +| dynamodb.js:64:69:64:82 | maliciousInput | dynamodb.js:64:28:64:85 | `SELECT ... nput}'` | provenance | | +| dynamodb.js:67:69:67:82 | maliciousInput | dynamodb.js:67:28:67:85 | `SELECT ... nput}'` | provenance | | | graphql.js:8:11:8:28 | id | graphql.js:11:46:11:47 | id | provenance | | | graphql.js:8:16:8:28 | req.params.id | graphql.js:8:11:8:28 | id | provenance | | | graphql.js:11:46:11:47 | id | graphql.js:9:34:19:5 | `\\n ... }\\n ` | provenance | | @@ -554,6 +564,14 @@ nodes | dynamodb.js:19:20:19:34 | updateStatement | semmle.label | updateStatement | | dynamodb.js:21:23:21:35 | updateCommand | semmle.label | updateCommand | | dynamodb.js:47:24:47:30 | command | semmle.label | command | +| dynamodb.js:56:9:56:38 | maliciousInput | semmle.label | maliciousInput | +| dynamodb.js:56:26:56:33 | req.body | semmle.label | req.body | +| dynamodb.js:58:20:58:77 | `SELECT ... nput}'` | semmle.label | `SELECT ... nput}'` | +| dynamodb.js:58:61:58:74 | maliciousInput | semmle.label | maliciousInput | +| dynamodb.js:64:28:64:85 | `SELECT ... nput}'` | semmle.label | `SELECT ... nput}'` | +| dynamodb.js:64:69:64:82 | maliciousInput | semmle.label | maliciousInput | +| dynamodb.js:67:28:67:85 | `SELECT ... nput}'` | semmle.label | `SELECT ... nput}'` | +| dynamodb.js:67:69:67:82 | maliciousInput | semmle.label | maliciousInput | | graphql.js:8:11:8:28 | id | semmle.label | id | | graphql.js:8:16:8:28 | req.params.id | semmle.label | req.params.id | | graphql.js:9:34:19:5 | `\\n ... }\\n ` | semmle.label | `\\n ... }\\n ` | diff --git a/javascript/ql/test/query-tests/Security/CWE-089/untyped/dynamodb.js b/javascript/ql/test/query-tests/Security/CWE-089/untyped/dynamodb.js index 8b4eddc4bd76..ef9e3c3005ce 100644 --- a/javascript/ql/test/query-tests/Security/CWE-089/untyped/dynamodb.js +++ b/javascript/ql/test/query-tests/Security/CWE-089/untyped/dynamodb.js @@ -53,21 +53,21 @@ app.post('/partiql/v2/execute', async (req, res) => { const dynamodb = new AWS.DynamoDB({ region: 'us-east-1' }); - let maliciousInput = req.body.data; // $ MISSING: Source + let maliciousInput = req.body.data; // $ Source const params = { - Statement: `SELECT * FROM Users WHERE username = '${maliciousInput}'` + Statement: `SELECT * FROM Users WHERE username = '${maliciousInput}'` // $ Alert }; - dynamodb.executeStatement(params, function(err, data) {}); // $ MISSING: Alert + dynamodb.executeStatement(params, function(err, data) {}); const params2 = { Statements: [{ - Statement: `SELECT * FROM Users WHERE username = '${maliciousInput}'` + Statement: `SELECT * FROM Users WHERE username = '${maliciousInput}'` // $ Alert }, { - Statement: `SELECT * FROM Users WHERE username = '${maliciousInput}'` + Statement: `SELECT * FROM Users WHERE username = '${maliciousInput}'` // $ Alert } ] }; - dynamodb.batchExecuteStatement(params2, function(err, data) {}); // $ MISSING: Alert + dynamodb.batchExecuteStatement(params2, function(err, data) {}); }); From 5e09416d59f7ddf9c8ce5cea0f53d27554e9dcf9 Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Mon, 28 Jul 2025 17:58:05 +0200 Subject: [PATCH 04/13] Added change note --- javascript/ql/lib/change-notes/2025-07-28-dynamodb.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 javascript/ql/lib/change-notes/2025-07-28-dynamodb.md diff --git a/javascript/ql/lib/change-notes/2025-07-28-dynamodb.md b/javascript/ql/lib/change-notes/2025-07-28-dynamodb.md new file mode 100644 index 000000000000..8cef0a4ec0f6 --- /dev/null +++ b/javascript/ql/lib/change-notes/2025-07-28-dynamodb.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Added support for the `aws-sdk` and `@aws-sdk/client-dynamodb` packages for DynamoDB PartiQL operations. From 44008a86fa446128c5e99a4fa861207a033f08db Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Tue, 29 Jul 2025 09:50:46 +0200 Subject: [PATCH 05/13] Added test cases for client-s v2 and v3 sql injection --- .../Security/CWE-089/untyped/clients3.js | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 javascript/ql/test/query-tests/Security/CWE-089/untyped/clients3.js diff --git a/javascript/ql/test/query-tests/Security/CWE-089/untyped/clients3.js b/javascript/ql/test/query-tests/Security/CWE-089/untyped/clients3.js new file mode 100644 index 000000000000..58cada55e721 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-089/untyped/clients3.js @@ -0,0 +1,44 @@ +const { S3Client, SelectObjectContentCommand } = require("@aws-sdk/client-s3"); +const AWS = require('aws-sdk'); +const express = require('express'); +const bodyParser = require('body-parser'); + +const app = express(); +app.use(bodyParser.json()); + +app.post('/client/v3/execute', async (req, res) => { + let maliciousInput = req.body.filter; // $ MISSING: Source + const client = new S3Client({ region: "us-east-1" }); + const params = { + Bucket: "my-bucket", + Key: "data.csv", + ExpressionType: "SQL", + Expression: "SELECT * FROM S3Object WHERE " + maliciousInput, + }; + await client.send(new SelectObjectContentCommand(params)); // $ MISSING: Alert + res.end(); +}); + +app.post('/client/v2/execute', async (req, res) => { + let maliciousInput = req.body.filter; // $ MISSING: Source + const s3 = new AWS.S3({ region: "us-east-1" }); + const params = { + Bucket: "my-bucket", + Key: "data.csv", + ExpressionType: "SQL", + Expression: "SELECT * FROM S3Object WHERE " + maliciousInput, // $ MISSING: Alert + }; + await s3.selectObjectContent(params).promise(); + res.end(); + + const params1 = { + Bucket: "my-bucket", + Key: "data.csv", + ExpressionType: "SQL", + Expression: "SELECT * FROM S3Object WHERE " + maliciousInput, // $ MISSING: Alert + }; + + s3.selectObjectContent(params1, (err, data) => { + res.end(); + }); +}); From 1af289cd7d440e060c4739ee6aa459723e418a51 Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Tue, 29 Jul 2025 09:52:49 +0200 Subject: [PATCH 06/13] Added modeling of client-s3 v2 and v3 --- javascript/ql/lib/ext/client-s3.model.yml | 20 +++++++++++++ .../CWE-089/untyped/SqlInjection.expected | 29 +++++++++++++++++++ .../Security/CWE-089/untyped/clients3.js | 10 +++---- 3 files changed, 54 insertions(+), 5 deletions(-) create mode 100644 javascript/ql/lib/ext/client-s3.model.yml diff --git a/javascript/ql/lib/ext/client-s3.model.yml b/javascript/ql/lib/ext/client-s3.model.yml new file mode 100644 index 000000000000..2d81b3040e97 --- /dev/null +++ b/javascript/ql/lib/ext/client-s3.model.yml @@ -0,0 +1,20 @@ +extensions: + - addsTo: + pack: codeql/javascript-all + extensible: sinkModel + data: + - ["S3ClientV3", "ReturnValue.Member[send].Argument[0]", "sql-injection"] + - ["S3ClientV2", "ReturnValue.Member[selectObjectContent].Argument[0].Member[Expression]", "sql-injection"] + + - addsTo: + pack: codeql/javascript-all + extensible: summaryModel + data: + - ["@aws-sdk/client-s3", "Member[SelectObjectContentCommand]", "Argument[0].Member[Expression]", "ReturnValue", "taint"] + + - addsTo: + pack: codeql/javascript-all + extensible: typeModel + data: + - ["S3ClientV3", "@aws-sdk/client-s3", "Member[S3Client]"] + - ["S3ClientV2", "aws-sdk", "Member[S3]"] diff --git a/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected b/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected index ce92511226a9..2cfded52f2e5 100644 --- a/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected @@ -1,4 +1,7 @@ #select +| clients3.js:18:23:18:60 | new Sel ... params) | clients3.js:10:26:10:33 | req.body | clients3.js:18:23:18:60 | new Sel ... params) | This query string depends on a $@. | clients3.js:10:26:10:33 | req.body | user-provided value | +| clients3.js:29:21:29:68 | "SELECT ... usInput | clients3.js:23:26:23:33 | req.body | clients3.js:29:21:29:68 | "SELECT ... usInput | This query string depends on a $@. | clients3.js:23:26:23:33 | req.body | user-provided value | +| clients3.js:38:21:38:68 | "SELECT ... usInput | clients3.js:23:26:23:33 | req.body | clients3.js:38:21:38:68 | "SELECT ... usInput | This query string depends on a $@. | clients3.js:23:26:23:33 | req.body | user-provided value | | dynamodb.js:15:23:15:29 | command | dynamodb.js:9:26:9:33 | req.body | dynamodb.js:15:23:15:29 | command | This query string depends on a $@. | dynamodb.js:9:26:9:33 | req.body | user-provided value | | dynamodb.js:21:23:21:35 | updateCommand | dynamodb.js:9:26:9:33 | req.body | dynamodb.js:21:23:21:35 | updateCommand | This query string depends on a $@. | dynamodb.js:9:26:9:33 | req.body | user-provided value | | dynamodb.js:47:24:47:30 | command | dynamodb.js:9:26:9:33 | req.body | dynamodb.js:47:24:47:30 | command | This query string depends on a $@. | dynamodb.js:9:26:9:33 | req.body | user-provided value | @@ -143,6 +146,18 @@ | tst4.js:8:10:8:66 | 'SELECT ... d + '"' | tst4.js:8:46:8:60 | $routeParams.id | tst4.js:8:10:8:66 | 'SELECT ... d + '"' | This query string depends on a $@. | tst4.js:8:46:8:60 | $routeParams.id | user-provided value | | tst.js:10:10:10:64 | 'SELECT ... d + '"' | tst.js:10:46:10:58 | req.params.id | tst.js:10:10:10:64 | 'SELECT ... d + '"' | This query string depends on a $@. | tst.js:10:46:10:58 | req.params.id | user-provided value | edges +| clients3.js:10:9:10:40 | maliciousInput | clients3.js:16:55:16:68 | maliciousInput | provenance | | +| clients3.js:10:26:10:33 | req.body | clients3.js:10:9:10:40 | maliciousInput | provenance | | +| clients3.js:12:11:17:5 | params [Expression] | clients3.js:18:54:18:59 | params [Expression] | provenance | | +| clients3.js:12:20:17:5 | {\\n ... ,\\n } [Expression] | clients3.js:12:11:17:5 | params [Expression] | provenance | | +| clients3.js:16:21:16:68 | "SELECT ... usInput | clients3.js:12:20:17:5 | {\\n ... ,\\n } [Expression] | provenance | | +| clients3.js:16:55:16:68 | maliciousInput | clients3.js:16:21:16:68 | "SELECT ... usInput | provenance | | +| clients3.js:18:54:18:59 | params [Expression] | clients3.js:18:23:18:60 | new Sel ... params) | provenance | | +| clients3.js:23:9:23:40 | maliciousInput | clients3.js:29:55:29:68 | maliciousInput | provenance | | +| clients3.js:23:9:23:40 | maliciousInput | clients3.js:38:55:38:68 | maliciousInput | provenance | | +| clients3.js:23:26:23:33 | req.body | clients3.js:23:9:23:40 | maliciousInput | provenance | | +| clients3.js:29:55:29:68 | maliciousInput | clients3.js:29:21:29:68 | "SELECT ... usInput | provenance | | +| clients3.js:38:55:38:68 | maliciousInput | clients3.js:38:21:38:68 | "SELECT ... usInput | provenance | | | dynamodb.js:9:9:9:38 | maliciousInput | dynamodb.js:11:64:11:77 | maliciousInput | provenance | | | dynamodb.js:9:9:9:38 | maliciousInput | dynamodb.js:17:80:17:93 | maliciousInput | provenance | | | dynamodb.js:9:26:9:33 | req.body | dynamodb.js:9:9:9:38 | maliciousInput | provenance | | @@ -547,6 +562,20 @@ edges | tst4.js:8:46:8:60 | $routeParams.id | tst4.js:8:10:8:66 | 'SELECT ... d + '"' | provenance | | | tst.js:10:46:10:58 | req.params.id | tst.js:10:10:10:64 | 'SELECT ... d + '"' | provenance | | nodes +| clients3.js:10:9:10:40 | maliciousInput | semmle.label | maliciousInput | +| clients3.js:10:26:10:33 | req.body | semmle.label | req.body | +| clients3.js:12:11:17:5 | params [Expression] | semmle.label | params [Expression] | +| clients3.js:12:20:17:5 | {\\n ... ,\\n } [Expression] | semmle.label | {\\n ... ,\\n } [Expression] | +| clients3.js:16:21:16:68 | "SELECT ... usInput | semmle.label | "SELECT ... usInput | +| clients3.js:16:55:16:68 | maliciousInput | semmle.label | maliciousInput | +| clients3.js:18:23:18:60 | new Sel ... params) | semmle.label | new Sel ... params) | +| clients3.js:18:54:18:59 | params [Expression] | semmle.label | params [Expression] | +| clients3.js:23:9:23:40 | maliciousInput | semmle.label | maliciousInput | +| clients3.js:23:26:23:33 | req.body | semmle.label | req.body | +| clients3.js:29:21:29:68 | "SELECT ... usInput | semmle.label | "SELECT ... usInput | +| clients3.js:29:55:29:68 | maliciousInput | semmle.label | maliciousInput | +| clients3.js:38:21:38:68 | "SELECT ... usInput | semmle.label | "SELECT ... usInput | +| clients3.js:38:55:38:68 | maliciousInput | semmle.label | maliciousInput | | dynamodb.js:9:9:9:38 | maliciousInput | semmle.label | maliciousInput | | dynamodb.js:9:26:9:33 | req.body | semmle.label | req.body | | dynamodb.js:11:11:11:80 | statement | semmle.label | statement | diff --git a/javascript/ql/test/query-tests/Security/CWE-089/untyped/clients3.js b/javascript/ql/test/query-tests/Security/CWE-089/untyped/clients3.js index 58cada55e721..9c6bce426575 100644 --- a/javascript/ql/test/query-tests/Security/CWE-089/untyped/clients3.js +++ b/javascript/ql/test/query-tests/Security/CWE-089/untyped/clients3.js @@ -7,7 +7,7 @@ const app = express(); app.use(bodyParser.json()); app.post('/client/v3/execute', async (req, res) => { - let maliciousInput = req.body.filter; // $ MISSING: Source + let maliciousInput = req.body.filter; // $ Source const client = new S3Client({ region: "us-east-1" }); const params = { Bucket: "my-bucket", @@ -15,18 +15,18 @@ app.post('/client/v3/execute', async (req, res) => { ExpressionType: "SQL", Expression: "SELECT * FROM S3Object WHERE " + maliciousInput, }; - await client.send(new SelectObjectContentCommand(params)); // $ MISSING: Alert + await client.send(new SelectObjectContentCommand(params)); // $ Alert res.end(); }); app.post('/client/v2/execute', async (req, res) => { - let maliciousInput = req.body.filter; // $ MISSING: Source + let maliciousInput = req.body.filter; // $ Source const s3 = new AWS.S3({ region: "us-east-1" }); const params = { Bucket: "my-bucket", Key: "data.csv", ExpressionType: "SQL", - Expression: "SELECT * FROM S3Object WHERE " + maliciousInput, // $ MISSING: Alert + Expression: "SELECT * FROM S3Object WHERE " + maliciousInput, // $ Alert }; await s3.selectObjectContent(params).promise(); res.end(); @@ -35,7 +35,7 @@ app.post('/client/v2/execute', async (req, res) => { Bucket: "my-bucket", Key: "data.csv", ExpressionType: "SQL", - Expression: "SELECT * FROM S3Object WHERE " + maliciousInput, // $ MISSING: Alert + Expression: "SELECT * FROM S3Object WHERE " + maliciousInput, // $ Alert }; s3.selectObjectContent(params1, (err, data) => { From c725191fd2b5476fc8b215ddcb689400ecdd439f Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Tue, 29 Jul 2025 13:13:45 +0200 Subject: [PATCH 07/13] Added test cases for athena v2 and v3 for sql injections --- .../Security/CWE-089/untyped/athena.js | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 javascript/ql/test/query-tests/Security/CWE-089/untyped/athena.js diff --git a/javascript/ql/test/query-tests/Security/CWE-089/untyped/athena.js b/javascript/ql/test/query-tests/Security/CWE-089/untyped/athena.js new file mode 100644 index 000000000000..00ef4a9f86fa --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-089/untyped/athena.js @@ -0,0 +1,72 @@ +const { AthenaClient, StartQueryExecutionCommand, CreateNamedQueryCommand, UpdateNamedQueryCommand } = require("@aws-sdk/client-athena"); +const AWS = require('aws-sdk'); +const express = require('express'); +const bodyParser = require('body-parser'); +const app = express(); +app.use(bodyParser.json()); + +app.post('/v3/athena/all', async (req, res) => { + const userQuery = req.body.query; // $ MISSING: Source + + const client = new AthenaClient({ region: "us-east-1" }); + + const params1 = { + QueryString: "SQL" + userQuery, + QueryExecutionContext: { Database: "default" }, + ResultConfiguration: { OutputLocation: "s3://my-results/" } + }; + const p = new StartQueryExecutionCommand(params1); + await client.send(p); // $ MISSING: Alert + + const params2 = { + Name: "user_query", + Database: "default", + QueryString: userQuery, + Description: "User-provided query" + }; + await client.send(new CreateNamedQueryCommand(params2)); // $ MISSING: Alert -- This only stores query to database, not executed + + const params3 = { + NamedQueryId: "namedQueryId", + Name: "user_query_updated", + Database: "default", + QueryString: userQuery, + Description: "Updated user-provided query" + }; + await client.send(new UpdateNamedQueryCommand(params3)); // $ MISSING: Alert -- This only stores query to database, not executed + + res.end(); +}); + + +app.post('/v2/athena/all', async (req, res) => { + const userQuery = req.body.query; // $ MISSING: Source + + const athena = new AWS.Athena({ region: "us-east-1" }); + + const params1 = { + QueryString: userQuery, // $ MISSING: Alert + QueryExecutionContext: { Database: "default" }, + ResultConfiguration: { OutputLocation: "s3://my-results/" } + }; + await athena.startQueryExecution(params1).promise(); + + const params2 = { + Name: "user_query", + Database: "default", + QueryString: userQuery, // $ MISSING: Alert -- This only stores query to database, not executed + Description: "User-provided query" + }; + await athena.createNamedQuery(params2).promise(); + + const params3 = { + NamedQueryId: "namedQueryId", + Name: "user_query_updated", + Database: "default", + QueryString: userQuery, // $ MISSING: Alert -- This only stores query to database, not executed + Description: "Updated user-provided query" + }; + await athena.updateNamedQuery(params3).promise(); + + res.end(); +}); From 238568d789d4ec4922a1e1012881d40d7aeb8db1 Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Tue, 29 Jul 2025 13:45:11 +0200 Subject: [PATCH 08/13] Added modeling of athena v2 and v3 for sql injections --- javascript/ql/lib/ext/athena.model.yml | 21 ++++++++ .../CWE-089/untyped/SqlInjection.expected | 54 +++++++++++++++++++ .../Security/CWE-089/untyped/athena.js | 16 +++--- 3 files changed, 83 insertions(+), 8 deletions(-) create mode 100644 javascript/ql/lib/ext/athena.model.yml diff --git a/javascript/ql/lib/ext/athena.model.yml b/javascript/ql/lib/ext/athena.model.yml new file mode 100644 index 000000000000..5101d7047e3e --- /dev/null +++ b/javascript/ql/lib/ext/athena.model.yml @@ -0,0 +1,21 @@ +extensions: + - addsTo: + pack: codeql/javascript-all + extensible: sinkModel + data: + - ["AthenaClientV3", "ReturnValue.Member[send].Argument[0]", "sql-injection"] + - ["AthenaClientV2", "ReturnValue.Member[startQueryExecution,createNamedQuery,updateNamedQuery].Argument[0].Member[QueryString]", "sql-injection"] + + + - addsTo: + pack: codeql/javascript-all + extensible: summaryModel + data: + - ["@aws-sdk/client-athena", "Member[StartQueryExecutionCommand,CreateNamedQueryCommand,UpdateNamedQueryCommand]", "Argument[0].Member[QueryString]", "ReturnValue", "taint"] + + - addsTo: + pack: codeql/javascript-all + extensible: typeModel + data: + - ["AthenaClientV3", "@aws-sdk/client-athena", "Member[AthenaClient]"] + - ["AthenaClientV2", "aws-sdk", "Member[Athena]"] diff --git a/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected b/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected index 2cfded52f2e5..09f0c9d76c42 100644 --- a/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected @@ -1,4 +1,10 @@ #select +| athena.js:19:23:19:23 | p | athena.js:9:23:9:30 | req.body | athena.js:19:23:19:23 | p | This query string depends on a $@. | athena.js:9:23:9:30 | req.body | user-provided value | +| athena.js:27:23:27:58 | new Cre ... arams2) | athena.js:9:23:9:30 | req.body | athena.js:27:23:27:58 | new Cre ... arams2) | This query string depends on a $@. | athena.js:9:23:9:30 | req.body | user-provided value | +| athena.js:36:23:36:58 | new Upd ... arams3) | athena.js:9:23:9:30 | req.body | athena.js:36:23:36:58 | new Upd ... arams3) | This query string depends on a $@. | athena.js:9:23:9:30 | req.body | user-provided value | +| athena.js:48:22:48:30 | userQuery | athena.js:43:23:43:30 | req.body | athena.js:48:22:48:30 | userQuery | This query string depends on a $@. | athena.js:43:23:43:30 | req.body | user-provided value | +| athena.js:57:22:57:30 | userQuery | athena.js:43:23:43:30 | req.body | athena.js:57:22:57:30 | userQuery | This query string depends on a $@. | athena.js:43:23:43:30 | req.body | user-provided value | +| athena.js:66:22:66:30 | userQuery | athena.js:43:23:43:30 | req.body | athena.js:66:22:66:30 | userQuery | This query string depends on a $@. | athena.js:43:23:43:30 | req.body | user-provided value | | clients3.js:18:23:18:60 | new Sel ... params) | clients3.js:10:26:10:33 | req.body | clients3.js:18:23:18:60 | new Sel ... params) | This query string depends on a $@. | clients3.js:10:26:10:33 | req.body | user-provided value | | clients3.js:29:21:29:68 | "SELECT ... usInput | clients3.js:23:26:23:33 | req.body | clients3.js:29:21:29:68 | "SELECT ... usInput | This query string depends on a $@. | clients3.js:23:26:23:33 | req.body | user-provided value | | clients3.js:38:21:38:68 | "SELECT ... usInput | clients3.js:23:26:23:33 | req.body | clients3.js:38:21:38:68 | "SELECT ... usInput | This query string depends on a $@. | clients3.js:23:26:23:33 | req.body | user-provided value | @@ -146,6 +152,29 @@ | tst4.js:8:10:8:66 | 'SELECT ... d + '"' | tst4.js:8:46:8:60 | $routeParams.id | tst4.js:8:10:8:66 | 'SELECT ... d + '"' | This query string depends on a $@. | tst4.js:8:46:8:60 | $routeParams.id | user-provided value | | tst.js:10:10:10:64 | 'SELECT ... d + '"' | tst.js:10:46:10:58 | req.params.id | tst.js:10:10:10:64 | 'SELECT ... d + '"' | This query string depends on a $@. | tst.js:10:46:10:58 | req.params.id | user-provided value | edges +| athena.js:9:11:9:36 | userQuery | athena.js:14:30:14:38 | userQuery | provenance | | +| athena.js:9:11:9:36 | userQuery | athena.js:24:22:24:30 | userQuery | provenance | | +| athena.js:9:11:9:36 | userQuery | athena.js:33:22:33:30 | userQuery | provenance | | +| athena.js:9:23:9:30 | req.body | athena.js:9:11:9:36 | userQuery | provenance | | +| athena.js:13:11:17:5 | params1 [QueryString] | athena.js:18:46:18:52 | params1 [QueryString] | provenance | | +| athena.js:13:21:17:5 | {\\n ... }\\n } [QueryString] | athena.js:13:11:17:5 | params1 [QueryString] | provenance | | +| athena.js:14:22:14:38 | "SQL" + userQuery | athena.js:13:21:17:5 | {\\n ... }\\n } [QueryString] | provenance | | +| athena.js:14:30:14:38 | userQuery | athena.js:14:22:14:38 | "SQL" + userQuery | provenance | | +| athena.js:18:11:18:53 | p | athena.js:19:23:19:23 | p | provenance | | +| athena.js:18:15:18:53 | new Sta ... arams1) | athena.js:18:11:18:53 | p | provenance | | +| athena.js:18:46:18:52 | params1 [QueryString] | athena.js:18:15:18:53 | new Sta ... arams1) | provenance | | +| athena.js:21:11:26:5 | params2 [QueryString] | athena.js:27:51:27:57 | params2 [QueryString] | provenance | | +| athena.js:21:21:26:5 | {\\n ... "\\n } [QueryString] | athena.js:21:11:26:5 | params2 [QueryString] | provenance | | +| athena.js:24:22:24:30 | userQuery | athena.js:21:21:26:5 | {\\n ... "\\n } [QueryString] | provenance | | +| athena.js:27:51:27:57 | params2 [QueryString] | athena.js:27:23:27:58 | new Cre ... arams2) | provenance | | +| athena.js:29:11:35:5 | params3 [QueryString] | athena.js:36:51:36:57 | params3 [QueryString] | provenance | | +| athena.js:29:21:35:5 | {\\n ... "\\n } [QueryString] | athena.js:29:11:35:5 | params3 [QueryString] | provenance | | +| athena.js:33:22:33:30 | userQuery | athena.js:29:21:35:5 | {\\n ... "\\n } [QueryString] | provenance | | +| athena.js:36:51:36:57 | params3 [QueryString] | athena.js:36:23:36:58 | new Upd ... arams3) | provenance | | +| athena.js:43:11:43:36 | userQuery | athena.js:48:22:48:30 | userQuery | provenance | | +| athena.js:43:11:43:36 | userQuery | athena.js:57:22:57:30 | userQuery | provenance | | +| athena.js:43:11:43:36 | userQuery | athena.js:66:22:66:30 | userQuery | provenance | | +| athena.js:43:23:43:30 | req.body | athena.js:43:11:43:36 | userQuery | provenance | | | clients3.js:10:9:10:40 | maliciousInput | clients3.js:16:55:16:68 | maliciousInput | provenance | | | clients3.js:10:26:10:33 | req.body | clients3.js:10:9:10:40 | maliciousInput | provenance | | | clients3.js:12:11:17:5 | params [Expression] | clients3.js:18:54:18:59 | params [Expression] | provenance | | @@ -562,6 +591,31 @@ edges | tst4.js:8:46:8:60 | $routeParams.id | tst4.js:8:10:8:66 | 'SELECT ... d + '"' | provenance | | | tst.js:10:46:10:58 | req.params.id | tst.js:10:10:10:64 | 'SELECT ... d + '"' | provenance | | nodes +| athena.js:9:11:9:36 | userQuery | semmle.label | userQuery | +| athena.js:9:23:9:30 | req.body | semmle.label | req.body | +| athena.js:13:11:17:5 | params1 [QueryString] | semmle.label | params1 [QueryString] | +| athena.js:13:21:17:5 | {\\n ... }\\n } [QueryString] | semmle.label | {\\n ... }\\n } [QueryString] | +| athena.js:14:22:14:38 | "SQL" + userQuery | semmle.label | "SQL" + userQuery | +| athena.js:14:30:14:38 | userQuery | semmle.label | userQuery | +| athena.js:18:11:18:53 | p | semmle.label | p | +| athena.js:18:15:18:53 | new Sta ... arams1) | semmle.label | new Sta ... arams1) | +| athena.js:18:46:18:52 | params1 [QueryString] | semmle.label | params1 [QueryString] | +| athena.js:19:23:19:23 | p | semmle.label | p | +| athena.js:21:11:26:5 | params2 [QueryString] | semmle.label | params2 [QueryString] | +| athena.js:21:21:26:5 | {\\n ... "\\n } [QueryString] | semmle.label | {\\n ... "\\n } [QueryString] | +| athena.js:24:22:24:30 | userQuery | semmle.label | userQuery | +| athena.js:27:23:27:58 | new Cre ... arams2) | semmle.label | new Cre ... arams2) | +| athena.js:27:51:27:57 | params2 [QueryString] | semmle.label | params2 [QueryString] | +| athena.js:29:11:35:5 | params3 [QueryString] | semmle.label | params3 [QueryString] | +| athena.js:29:21:35:5 | {\\n ... "\\n } [QueryString] | semmle.label | {\\n ... "\\n } [QueryString] | +| athena.js:33:22:33:30 | userQuery | semmle.label | userQuery | +| athena.js:36:23:36:58 | new Upd ... arams3) | semmle.label | new Upd ... arams3) | +| athena.js:36:51:36:57 | params3 [QueryString] | semmle.label | params3 [QueryString] | +| athena.js:43:11:43:36 | userQuery | semmle.label | userQuery | +| athena.js:43:23:43:30 | req.body | semmle.label | req.body | +| athena.js:48:22:48:30 | userQuery | semmle.label | userQuery | +| athena.js:57:22:57:30 | userQuery | semmle.label | userQuery | +| athena.js:66:22:66:30 | userQuery | semmle.label | userQuery | | clients3.js:10:9:10:40 | maliciousInput | semmle.label | maliciousInput | | clients3.js:10:26:10:33 | req.body | semmle.label | req.body | | clients3.js:12:11:17:5 | params [Expression] | semmle.label | params [Expression] | diff --git a/javascript/ql/test/query-tests/Security/CWE-089/untyped/athena.js b/javascript/ql/test/query-tests/Security/CWE-089/untyped/athena.js index 00ef4a9f86fa..2b661d5d4aea 100644 --- a/javascript/ql/test/query-tests/Security/CWE-089/untyped/athena.js +++ b/javascript/ql/test/query-tests/Security/CWE-089/untyped/athena.js @@ -6,7 +6,7 @@ const app = express(); app.use(bodyParser.json()); app.post('/v3/athena/all', async (req, res) => { - const userQuery = req.body.query; // $ MISSING: Source + const userQuery = req.body.query; // $ Source const client = new AthenaClient({ region: "us-east-1" }); @@ -16,7 +16,7 @@ app.post('/v3/athena/all', async (req, res) => { ResultConfiguration: { OutputLocation: "s3://my-results/" } }; const p = new StartQueryExecutionCommand(params1); - await client.send(p); // $ MISSING: Alert + await client.send(p); // $ Alert const params2 = { Name: "user_query", @@ -24,7 +24,7 @@ app.post('/v3/athena/all', async (req, res) => { QueryString: userQuery, Description: "User-provided query" }; - await client.send(new CreateNamedQueryCommand(params2)); // $ MISSING: Alert -- This only stores query to database, not executed + await client.send(new CreateNamedQueryCommand(params2)); // $ Alert -- This only stores query to database, not executed const params3 = { NamedQueryId: "namedQueryId", @@ -33,19 +33,19 @@ app.post('/v3/athena/all', async (req, res) => { QueryString: userQuery, Description: "Updated user-provided query" }; - await client.send(new UpdateNamedQueryCommand(params3)); // $ MISSING: Alert -- This only stores query to database, not executed + await client.send(new UpdateNamedQueryCommand(params3)); // $ Alert -- This only stores query to database, not executed res.end(); }); app.post('/v2/athena/all', async (req, res) => { - const userQuery = req.body.query; // $ MISSING: Source + const userQuery = req.body.query; // $ Source const athena = new AWS.Athena({ region: "us-east-1" }); const params1 = { - QueryString: userQuery, // $ MISSING: Alert + QueryString: userQuery, // $ Alert QueryExecutionContext: { Database: "default" }, ResultConfiguration: { OutputLocation: "s3://my-results/" } }; @@ -54,7 +54,7 @@ app.post('/v2/athena/all', async (req, res) => { const params2 = { Name: "user_query", Database: "default", - QueryString: userQuery, // $ MISSING: Alert -- This only stores query to database, not executed + QueryString: userQuery, // $ Alert -- This only stores query to database, not executed Description: "User-provided query" }; await athena.createNamedQuery(params2).promise(); @@ -63,7 +63,7 @@ app.post('/v2/athena/all', async (req, res) => { NamedQueryId: "namedQueryId", Name: "user_query_updated", Database: "default", - QueryString: userQuery, // $ MISSING: Alert -- This only stores query to database, not executed + QueryString: userQuery, // $ Alert -- This only stores query to database, not executed Description: "Updated user-provided query" }; await athena.updateNamedQuery(params3).promise(); From 0588274945569bf650762e8b3dd1142392a41247 Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Tue, 29 Jul 2025 14:34:51 +0200 Subject: [PATCH 09/13] Added test cases for client-rds-data for sql injections --- .../Security/CWE-089/untyped/rds-client.js | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 javascript/ql/test/query-tests/Security/CWE-089/untyped/rds-client.js diff --git a/javascript/ql/test/query-tests/Security/CWE-089/untyped/rds-client.js b/javascript/ql/test/query-tests/Security/CWE-089/untyped/rds-client.js new file mode 100644 index 000000000000..6899ed0338f3 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-089/untyped/rds-client.js @@ -0,0 +1,68 @@ +const { RDSDataClient, BatchExecuteStatementCommand, ExecuteStatementCommand, ExecuteSqlCommand } = require("@aws-sdk/client-rds-data"); +const express = require('express'); +const bodyParser = require('body-parser'); +const app = express(); +app.use(bodyParser.json()); + +app.post('/v3/rds/all', async (req, res) => { + const userQuery = req.body.query; // $ MISSING: Source + const userQueries = req.body.queries; // $ MISSING: Source + + const client = new RDSDataClient({ region: "us-east-1" }); + + const params1 = { + resourceArn: "arn:aws:rds:us-east-1:123456789012:cluster:my-aurora-cluster", + secretArn: "arn:aws:secretsmanager:us-east-1:123456789012:secret:my-secret", + database: "userDatabase", + sql: userQuery + }; + await client.send(new ExecuteStatementCommand(params1)); // $ MISSING: Alert + + const params2 = { + resourceArn: "arn:aws:rds:us-east-1:123456789012:cluster:my-aurora-cluster", + secretArn: "arn:aws:secretsmanager:us-east-1:123456789012:secret:my-secret", + database: "userDatabase", + parameterSets: userQueries.map(sql => ({ sql })) + }; + await client.send(new BatchExecuteStatementCommand(params2)); // $ MISSING: Alert + + const params = { + resourceArn: "...", + secretArn: "...", + database: "userDatabase", + sqlStatements: userQuery + }; + + await client.send(new ExecuteSqlCommand(params)); // $ MISSING: Alert + + res.end(); +}); + +const AWS = require('aws-sdk'); + +app.post('/v2/rds/all', async (req, res) => { + const userQuery = req.body.query; // $ MISSING: Source + const userQueries = req.body.queries; // $ MISSING: Source + + const rdsData = new AWS.RDSDataService({ region: "us-east-1" }); + + const params1 = { + resourceArn: "arn:aws:rds:us-east-1:123456789012:cluster:my-aurora-cluster", + secretArn: "arn:aws:secretsmanager:us-east-1:123456789012:secret:my-secret", + database: "userDatabase", + sql: userQuery // $ MISSING: Alert + }; + await rdsData.executeStatement(params1).promise(); + + const params2 = { + resourceArn: "arn:aws:rds:us-east-1:123456789012:cluster:my-aurora-cluster", + secretArn: "arn:aws:secretsmanager:us-east-1:123456789012:secret:my-secret", + database: "userDatabase", + parameterSets: userQueries.map(sql => ({ sql })) // $ MISSING: Alert + }; + await rdsData.batchExecuteStatement(params2).promise(); + + res.end(); +}); + +app.listen(3000); From a8a6fdf2109d1192b7da02d4b014151775824db1 Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Tue, 29 Jul 2025 14:50:19 +0200 Subject: [PATCH 10/13] Added modeling of rds v2 and v3 for sql injections --- javascript/ql/lib/ext/rds-client.model.yml | 23 +++++++++++ .../CWE-089/untyped/SqlInjection.expected | 41 +++++++++++++++++++ .../Security/CWE-089/untyped/rds-client.js | 14 +++---- 3 files changed, 71 insertions(+), 7 deletions(-) create mode 100644 javascript/ql/lib/ext/rds-client.model.yml diff --git a/javascript/ql/lib/ext/rds-client.model.yml b/javascript/ql/lib/ext/rds-client.model.yml new file mode 100644 index 000000000000..7f10fc92c9f1 --- /dev/null +++ b/javascript/ql/lib/ext/rds-client.model.yml @@ -0,0 +1,23 @@ +extensions: + - addsTo: + pack: codeql/javascript-all + extensible: sinkModel + data: + - ["RDSDataClientV3", "ReturnValue.Member[send].Argument[0]", "sql-injection"] + - ["RDSDataClientV2", "ReturnValue.Member[executeStatement,batchExecuteStatement].Argument[0].Member[sql]", "sql-injection"] + - ["RDSDataClientV2", "ReturnValue.Member[batchExecuteStatement].Argument[0].Member[parameterSets].ArrayElement.Member[sql]", "sql-injection"] + + - addsTo: + pack: codeql/javascript-all + extensible: summaryModel + data: + - ["@aws-sdk/client-rds-data", "Member[ExecuteStatementCommand,BatchExecuteStatementCommand]", "Argument[0].Member[sql]", "ReturnValue", "taint"] + - ["@aws-sdk/client-rds-data", "Member[BatchExecuteStatementCommand]", "Argument[0].Member[parameterSets].ArrayElement.Member[sql]", "ReturnValue", "taint"] + - ["@aws-sdk/client-rds-data", "Member[ExecuteSqlCommand]", "Argument[0].Member[sqlStatements]", "ReturnValue", "taint"] + + - addsTo: + pack: codeql/javascript-all + extensible: typeModel + data: + - ["RDSDataClientV3", "@aws-sdk/client-rds-data", "Member[RDSDataClient]"] + - ["RDSDataClientV2", "aws-sdk", "Member[RDSDataService]"] diff --git a/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected b/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected index 09f0c9d76c42..888cbee4641e 100644 --- a/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected @@ -137,6 +137,10 @@ | pg-promise.js:60:20:60:24 | query | pg-promise.js:7:16:7:34 | req.params.category | pg-promise.js:60:20:60:24 | query | This query string depends on a $@. | pg-promise.js:7:16:7:34 | req.params.category | user-provided value | | pg-promise.js:63:23:63:27 | query | pg-promise.js:7:16:7:34 | req.params.category | pg-promise.js:63:23:63:27 | query | This query string depends on a $@. | pg-promise.js:7:16:7:34 | req.params.category | user-provided value | | pg-promise.js:64:16:64:20 | query | pg-promise.js:7:16:7:34 | req.params.category | pg-promise.js:64:16:64:20 | query | This query string depends on a $@. | pg-promise.js:7:16:7:34 | req.params.category | user-provided value | +| rds-client.js:19:23:19:58 | new Exe ... arams1) | rds-client.js:8:23:8:30 | req.body | rds-client.js:19:23:19:58 | new Exe ... arams1) | This query string depends on a $@. | rds-client.js:8:23:8:30 | req.body | user-provided value | +| rds-client.js:36:23:36:51 | new Exe ... params) | rds-client.js:8:23:8:30 | req.body | rds-client.js:36:23:36:51 | new Exe ... params) | This query string depends on a $@. | rds-client.js:8:23:8:30 | req.body | user-provided value | +| rds-client.js:53:14:53:22 | userQuery | rds-client.js:44:23:44:30 | req.body | rds-client.js:53:14:53:22 | userQuery | This query string depends on a $@. | rds-client.js:44:23:44:30 | req.body | user-provided value | +| rds-client.js:61:50:61:52 | sql | rds-client.js:45:25:45:32 | req.body | rds-client.js:61:50:61:52 | sql | This query string depends on a $@. | rds-client.js:45:25:45:32 | req.body | user-provided value | | redis.js:10:16:10:27 | req.body.key | redis.js:10:16:10:23 | req.body | redis.js:10:16:10:27 | req.body.key | This query object depends on a $@. | redis.js:10:16:10:23 | req.body | user-provided value | | redis.js:18:16:18:18 | key | redis.js:12:15:12:22 | req.body | redis.js:18:16:18:18 | key | This query object depends on a $@. | redis.js:12:15:12:22 | req.body | user-provided value | | redis.js:19:43:19:45 | key | redis.js:12:15:12:22 | req.body | redis.js:19:43:19:45 | key | This query object depends on a $@. | redis.js:12:15:12:22 | req.body | user-provided value | @@ -563,6 +567,23 @@ edges | pg-promise.js:22:11:22:15 | query | pg-promise.js:60:20:60:24 | query | provenance | | | pg-promise.js:22:11:22:15 | query | pg-promise.js:63:23:63:27 | query | provenance | | | pg-promise.js:22:11:22:15 | query | pg-promise.js:64:16:64:20 | query | provenance | | +| rds-client.js:8:11:8:36 | userQuery | rds-client.js:17:14:17:22 | userQuery | provenance | | +| rds-client.js:8:11:8:36 | userQuery | rds-client.js:33:24:33:32 | userQuery | provenance | | +| rds-client.js:8:23:8:30 | req.body | rds-client.js:8:11:8:36 | userQuery | provenance | | +| rds-client.js:13:11:18:5 | params1 [sql] | rds-client.js:19:51:19:57 | params1 [sql] | provenance | | +| rds-client.js:13:21:18:5 | {\\n ... y\\n } [sql] | rds-client.js:13:11:18:5 | params1 [sql] | provenance | | +| rds-client.js:17:14:17:22 | userQuery | rds-client.js:13:21:18:5 | {\\n ... y\\n } [sql] | provenance | | +| rds-client.js:19:51:19:57 | params1 [sql] | rds-client.js:19:23:19:58 | new Exe ... arams1) | provenance | | +| rds-client.js:29:11:34:5 | params [sqlStatements] | rds-client.js:36:45:36:50 | params [sqlStatements] | provenance | | +| rds-client.js:29:20:34:5 | {\\n ... y\\n } [sqlStatements] | rds-client.js:29:11:34:5 | params [sqlStatements] | provenance | | +| rds-client.js:33:24:33:32 | userQuery | rds-client.js:29:20:34:5 | {\\n ... y\\n } [sqlStatements] | provenance | | +| rds-client.js:36:45:36:50 | params [sqlStatements] | rds-client.js:36:23:36:51 | new Exe ... params) | provenance | | +| rds-client.js:44:11:44:36 | userQuery | rds-client.js:53:14:53:22 | userQuery | provenance | | +| rds-client.js:44:23:44:30 | req.body | rds-client.js:44:11:44:36 | userQuery | provenance | | +| rds-client.js:45:11:45:40 | userQueries | rds-client.js:61:24:61:34 | userQueries | provenance | | +| rds-client.js:45:25:45:32 | req.body | rds-client.js:45:11:45:40 | userQueries | provenance | | +| rds-client.js:61:24:61:34 | userQueries | rds-client.js:61:40:61:42 | sql | provenance | | +| rds-client.js:61:40:61:42 | sql | rds-client.js:61:50:61:52 | sql | provenance | | | redis.js:10:16:10:23 | req.body | redis.js:10:16:10:27 | req.body.key | provenance | Config | | redis.js:12:9:12:26 | key | redis.js:13:16:13:18 | key | provenance | | | redis.js:12:9:12:26 | key | redis.js:18:16:18:18 | key | provenance | | @@ -940,6 +961,26 @@ nodes | pg-promise.js:60:20:60:24 | query | semmle.label | query | | pg-promise.js:63:23:63:27 | query | semmle.label | query | | pg-promise.js:64:16:64:20 | query | semmle.label | query | +| rds-client.js:8:11:8:36 | userQuery | semmle.label | userQuery | +| rds-client.js:8:23:8:30 | req.body | semmle.label | req.body | +| rds-client.js:13:11:18:5 | params1 [sql] | semmle.label | params1 [sql] | +| rds-client.js:13:21:18:5 | {\\n ... y\\n } [sql] | semmle.label | {\\n ... y\\n } [sql] | +| rds-client.js:17:14:17:22 | userQuery | semmle.label | userQuery | +| rds-client.js:19:23:19:58 | new Exe ... arams1) | semmle.label | new Exe ... arams1) | +| rds-client.js:19:51:19:57 | params1 [sql] | semmle.label | params1 [sql] | +| rds-client.js:29:11:34:5 | params [sqlStatements] | semmle.label | params [sqlStatements] | +| rds-client.js:29:20:34:5 | {\\n ... y\\n } [sqlStatements] | semmle.label | {\\n ... y\\n } [sqlStatements] | +| rds-client.js:33:24:33:32 | userQuery | semmle.label | userQuery | +| rds-client.js:36:23:36:51 | new Exe ... params) | semmle.label | new Exe ... params) | +| rds-client.js:36:45:36:50 | params [sqlStatements] | semmle.label | params [sqlStatements] | +| rds-client.js:44:11:44:36 | userQuery | semmle.label | userQuery | +| rds-client.js:44:23:44:30 | req.body | semmle.label | req.body | +| rds-client.js:45:11:45:40 | userQueries | semmle.label | userQueries | +| rds-client.js:45:25:45:32 | req.body | semmle.label | req.body | +| rds-client.js:53:14:53:22 | userQuery | semmle.label | userQuery | +| rds-client.js:61:24:61:34 | userQueries | semmle.label | userQueries | +| rds-client.js:61:40:61:42 | sql | semmle.label | sql | +| rds-client.js:61:50:61:52 | sql | semmle.label | sql | | redis.js:10:16:10:23 | req.body | semmle.label | req.body | | redis.js:10:16:10:27 | req.body.key | semmle.label | req.body.key | | redis.js:12:9:12:26 | key | semmle.label | key | diff --git a/javascript/ql/test/query-tests/Security/CWE-089/untyped/rds-client.js b/javascript/ql/test/query-tests/Security/CWE-089/untyped/rds-client.js index 6899ed0338f3..35d41e145f98 100644 --- a/javascript/ql/test/query-tests/Security/CWE-089/untyped/rds-client.js +++ b/javascript/ql/test/query-tests/Security/CWE-089/untyped/rds-client.js @@ -5,7 +5,7 @@ const app = express(); app.use(bodyParser.json()); app.post('/v3/rds/all', async (req, res) => { - const userQuery = req.body.query; // $ MISSING: Source + const userQuery = req.body.query; // $ Source const userQueries = req.body.queries; // $ MISSING: Source const client = new RDSDataClient({ region: "us-east-1" }); @@ -16,7 +16,7 @@ app.post('/v3/rds/all', async (req, res) => { database: "userDatabase", sql: userQuery }; - await client.send(new ExecuteStatementCommand(params1)); // $ MISSING: Alert + await client.send(new ExecuteStatementCommand(params1)); // $ Alert const params2 = { resourceArn: "arn:aws:rds:us-east-1:123456789012:cluster:my-aurora-cluster", @@ -33,7 +33,7 @@ app.post('/v3/rds/all', async (req, res) => { sqlStatements: userQuery }; - await client.send(new ExecuteSqlCommand(params)); // $ MISSING: Alert + await client.send(new ExecuteSqlCommand(params)); // $ Alert res.end(); }); @@ -41,8 +41,8 @@ app.post('/v3/rds/all', async (req, res) => { const AWS = require('aws-sdk'); app.post('/v2/rds/all', async (req, res) => { - const userQuery = req.body.query; // $ MISSING: Source - const userQueries = req.body.queries; // $ MISSING: Source + const userQuery = req.body.query; // $ Source + const userQueries = req.body.queries; // $ Source const rdsData = new AWS.RDSDataService({ region: "us-east-1" }); @@ -50,7 +50,7 @@ app.post('/v2/rds/all', async (req, res) => { resourceArn: "arn:aws:rds:us-east-1:123456789012:cluster:my-aurora-cluster", secretArn: "arn:aws:secretsmanager:us-east-1:123456789012:secret:my-secret", database: "userDatabase", - sql: userQuery // $ MISSING: Alert + sql: userQuery // $ Alert }; await rdsData.executeStatement(params1).promise(); @@ -58,7 +58,7 @@ app.post('/v2/rds/all', async (req, res) => { resourceArn: "arn:aws:rds:us-east-1:123456789012:cluster:my-aurora-cluster", secretArn: "arn:aws:secretsmanager:us-east-1:123456789012:secret:my-secret", database: "userDatabase", - parameterSets: userQueries.map(sql => ({ sql })) // $ MISSING: Alert + parameterSets: userQueries.map(sql => ({ sql })) // $ Alert }; await rdsData.batchExecuteStatement(params2).promise(); From 0806bd686f0b05223865999995d89eefc630e24e Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Tue, 29 Jul 2025 14:56:28 +0200 Subject: [PATCH 11/13] Updated change note --- javascript/ql/lib/change-notes/2025-07-28-dynamodb.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/lib/change-notes/2025-07-28-dynamodb.md b/javascript/ql/lib/change-notes/2025-07-28-dynamodb.md index 8cef0a4ec0f6..6d247a4fa5cb 100644 --- a/javascript/ql/lib/change-notes/2025-07-28-dynamodb.md +++ b/javascript/ql/lib/change-notes/2025-07-28-dynamodb.md @@ -1,4 +1,4 @@ --- category: minorAnalysis --- -* Added support for the `aws-sdk` and `@aws-sdk/client-dynamodb` packages for DynamoDB PartiQL operations. +* Added support for the `aws-sdk` and `@aws-sdk/client-dynamodb`, `@aws-sdk/client-athena`, `@aws-sdk/client-s3`, and `@aws-sdk/client-rds-data` packages. This enables detection of SQL injection vulnerabilities in DynamoDB PartiQL operations, Athena queries, S3 select expressions, and RDS Data API calls. From 2ca5d8165eb509df4a287c7a30c0e8a7b09afde7 Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Tue, 29 Jul 2025 16:28:35 +0200 Subject: [PATCH 12/13] Added tests and modeling of database-access-result --- .../lib/change-notes/2025-07-28-dynamodb.md | 2 +- javascript/ql/lib/ext/athena.model.yml | 8 ++ javascript/ql/lib/ext/client-s3.model.yml | 8 ++ javascript/ql/lib/ext/dynamodb.model.yml | 8 ++ javascript/ql/lib/ext/rds-client.model.yml | 8 ++ .../XssWithAdditionalSources.expected | 56 +++++++++++++ .../Security/CWE-079/DomBasedXss/aws-db.js | 79 +++++++++++++++++++ 7 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/aws-db.js diff --git a/javascript/ql/lib/change-notes/2025-07-28-dynamodb.md b/javascript/ql/lib/change-notes/2025-07-28-dynamodb.md index 6d247a4fa5cb..bbf5d57163ae 100644 --- a/javascript/ql/lib/change-notes/2025-07-28-dynamodb.md +++ b/javascript/ql/lib/change-notes/2025-07-28-dynamodb.md @@ -1,4 +1,4 @@ --- category: minorAnalysis --- -* Added support for the `aws-sdk` and `@aws-sdk/client-dynamodb`, `@aws-sdk/client-athena`, `@aws-sdk/client-s3`, and `@aws-sdk/client-rds-data` packages. This enables detection of SQL injection vulnerabilities in DynamoDB PartiQL operations, Athena queries, S3 select expressions, and RDS Data API calls. +* Added support for the `aws-sdk` and `@aws-sdk/client-dynamodb`, `@aws-sdk/client-athena`, `@aws-sdk/client-s3`, and `@aws-sdk/client-rds-data` packages. diff --git a/javascript/ql/lib/ext/athena.model.yml b/javascript/ql/lib/ext/athena.model.yml index 5101d7047e3e..225d0926988a 100644 --- a/javascript/ql/lib/ext/athena.model.yml +++ b/javascript/ql/lib/ext/athena.model.yml @@ -19,3 +19,11 @@ extensions: data: - ["AthenaClientV3", "@aws-sdk/client-athena", "Member[AthenaClient]"] - ["AthenaClientV2", "aws-sdk", "Member[Athena]"] + + - addsTo: + pack: codeql/javascript-all + extensible: sourceModel + data: + - ["AthenaClientV3", "ReturnValue.Member[send].ReturnValue.Awaited", "database-access-result"] + - ["AthenaClientV2", "ReturnValue.Member[getQueryResults].ReturnValue.Member[promise].ReturnValue.Awaited", "database-access-result"] + - ["AthenaClientV2", "ReturnValue.Member[getQueryResults].Argument[1].Parameter[1]", "database-access-result"] diff --git a/javascript/ql/lib/ext/client-s3.model.yml b/javascript/ql/lib/ext/client-s3.model.yml index 2d81b3040e97..4deb2d6b7054 100644 --- a/javascript/ql/lib/ext/client-s3.model.yml +++ b/javascript/ql/lib/ext/client-s3.model.yml @@ -18,3 +18,11 @@ extensions: data: - ["S3ClientV3", "@aws-sdk/client-s3", "Member[S3Client]"] - ["S3ClientV2", "aws-sdk", "Member[S3]"] + + - addsTo: + pack: codeql/javascript-all + extensible: sourceModel + data: + - ["S3ClientV3", "ReturnValue.Member[send].ReturnValue.Awaited", "database-access-result"] + - ["S3ClientV2", "ReturnValue.Member[getObject].ReturnValue.Member[promise].ReturnValue.Awaited", "database-access-result"] + - ["S3ClientV2", "ReturnValue.Member[getObject].Argument[1].Parameter[1]", "database-access-result"] diff --git a/javascript/ql/lib/ext/dynamodb.model.yml b/javascript/ql/lib/ext/dynamodb.model.yml index ddbf6cc3eee1..0efba4d4bc45 100644 --- a/javascript/ql/lib/ext/dynamodb.model.yml +++ b/javascript/ql/lib/ext/dynamodb.model.yml @@ -20,3 +20,11 @@ extensions: data: - ["DynamoDBClientV3", "@aws-sdk/client-dynamodb", "Member[DynamoDBClient,DynamoDB]"] - ["DynamoDBClientV2", "aws-sdk", "Member[DynamoDB]"] + + - addsTo: + pack: codeql/javascript-all + extensible: sourceModel + data: + - ["DynamoDBClientV3", "ReturnValue.Member[send].ReturnValue.Awaited", "database-access-result"] + - ["DynamoDBClientV2", "ReturnValue.Member[executeStatement,batchExecuteStatement].ReturnValue.Member[promise].ReturnValue.Awaited", "database-access-result"] + - ["DynamoDBClientV2", "ReturnValue.Member[executeStatement,batchExecuteStatement].Argument[1].Parameter[1]", "database-access-result"] diff --git a/javascript/ql/lib/ext/rds-client.model.yml b/javascript/ql/lib/ext/rds-client.model.yml index 7f10fc92c9f1..2535c88a02c8 100644 --- a/javascript/ql/lib/ext/rds-client.model.yml +++ b/javascript/ql/lib/ext/rds-client.model.yml @@ -21,3 +21,11 @@ extensions: data: - ["RDSDataClientV3", "@aws-sdk/client-rds-data", "Member[RDSDataClient]"] - ["RDSDataClientV2", "aws-sdk", "Member[RDSDataService]"] + + - addsTo: + pack: codeql/javascript-all + extensible: sourceModel + data: + - ["RDSDataClientV3", "ReturnValue.Member[send].ReturnValue.Awaited", "database-access-result"] + - ["RDSDataClientV2", "ReturnValue.Member[executeStatement,batchExecuteStatement].ReturnValue.Member[promise].ReturnValue.Awaited", "database-access-result"] + - ["RDSDataClientV2", "ReturnValue.Member[executeStatement,batchExecuteStatement].Argument[1].Parameter[1]", "database-access-result"] diff --git a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/XssWithAdditionalSources.expected b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/XssWithAdditionalSources.expected index c031b7c1810c..295a830754ce 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/XssWithAdditionalSources.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/XssWithAdditionalSources.expected @@ -32,6 +32,34 @@ nodes | angular-tempate-url.js:13:30:13:31 | ev | semmle.label | ev | | angular-tempate-url.js:14:26:14:27 | ev | semmle.label | ev | | angular-tempate-url.js:14:26:14:32 | ev.data | semmle.label | ev.data | +| aws-db.js:15:31:15:37 | results | semmle.label | results | +| aws-db.js:15:31:15:76 | results ... arValue | semmle.label | results ... arValue | +| aws-db.js:20:31:20:38 | response | semmle.label | response | +| aws-db.js:20:31:20:54 | respons ... tring() | semmle.label | respons ... tring() | +| aws-db.js:24:31:24:39 | response2 | semmle.label | response2 | +| aws-db.js:24:31:24:47 | response2.records | semmle.label | response2.records | +| aws-db.js:28:31:28:39 | response3 | semmle.label | response3 | +| aws-db.js:28:31:28:44 | response3.Item | semmle.label | response3.Item | +| aws-db.js:43:31:43:37 | results | semmle.label | results | +| aws-db.js:43:31:43:76 | results ... arValue | semmle.label | results ... arValue | +| aws-db.js:46:35:46:38 | data | semmle.label | data | +| aws-db.js:46:35:46:77 | data.Re ... arValue | semmle.label | data.Re ... arValue | +| aws-db.js:51:31:51:38 | response | semmle.label | response | +| aws-db.js:51:31:51:54 | respons ... tring() | semmle.label | respons ... tring() | +| aws-db.js:54:35:54:38 | data | semmle.label | data | +| aws-db.js:54:35:54:54 | data.Body.toString() | semmle.label | data.Body.toString() | +| aws-db.js:59:31:59:39 | response1 | semmle.label | response1 | +| aws-db.js:59:31:59:47 | response1.records | semmle.label | response1.records | +| aws-db.js:62:35:62:38 | data | semmle.label | data | +| aws-db.js:62:35:62:46 | data.records | semmle.label | data.records | +| aws-db.js:66:31:66:39 | response2 | semmle.label | response2 | +| aws-db.js:66:31:66:53 | respons ... Results | semmle.label | respons ... Results | +| aws-db.js:69:35:69:38 | data | semmle.label | data | +| aws-db.js:69:35:69:52 | data.updateResults | semmle.label | data.updateResults | +| aws-db.js:74:35:74:38 | data | semmle.label | data | +| aws-db.js:74:35:74:43 | data.Item | semmle.label | data.Item | +| aws-db.js:77:35:77:38 | data | semmle.label | data | +| aws-db.js:77:35:77:43 | data.Item | semmle.label | data.Item | | classnames.js:7:31:7:84 | `` | semmle.label | `` | | classnames.js:7:47:7:69 | classNa ... w.name) | semmle.label | classNa ... w.name) | | classnames.js:7:58:7:68 | window.name | semmle.label | window.name | @@ -724,6 +752,20 @@ edges | angular-tempate-url.js:13:30:13:31 | ev | angular-tempate-url.js:14:26:14:27 | ev | provenance | | | angular-tempate-url.js:14:26:14:27 | ev | angular-tempate-url.js:14:26:14:32 | ev.data | provenance | | | angular-tempate-url.js:14:26:14:32 | ev.data | angular-tempate-url.js:9:26:9:45 | Cookie.get("unsafe") | provenance | | +| aws-db.js:15:31:15:37 | results | aws-db.js:15:31:15:76 | results ... arValue | provenance | | +| aws-db.js:20:31:20:38 | response | aws-db.js:20:31:20:54 | respons ... tring() | provenance | | +| aws-db.js:24:31:24:39 | response2 | aws-db.js:24:31:24:47 | response2.records | provenance | | +| aws-db.js:28:31:28:39 | response3 | aws-db.js:28:31:28:44 | response3.Item | provenance | | +| aws-db.js:43:31:43:37 | results | aws-db.js:43:31:43:76 | results ... arValue | provenance | | +| aws-db.js:46:35:46:38 | data | aws-db.js:46:35:46:77 | data.Re ... arValue | provenance | | +| aws-db.js:51:31:51:38 | response | aws-db.js:51:31:51:54 | respons ... tring() | provenance | | +| aws-db.js:54:35:54:38 | data | aws-db.js:54:35:54:54 | data.Body.toString() | provenance | | +| aws-db.js:59:31:59:39 | response1 | aws-db.js:59:31:59:47 | response1.records | provenance | | +| aws-db.js:62:35:62:38 | data | aws-db.js:62:35:62:46 | data.records | provenance | | +| aws-db.js:66:31:66:39 | response2 | aws-db.js:66:31:66:53 | respons ... Results | provenance | | +| aws-db.js:69:35:69:38 | data | aws-db.js:69:35:69:52 | data.updateResults | provenance | | +| aws-db.js:74:35:74:38 | data | aws-db.js:74:35:74:43 | data.Item | provenance | | +| aws-db.js:77:35:77:38 | data | aws-db.js:77:35:77:43 | data.Item | provenance | | | classnames.js:7:47:7:69 | classNa ... w.name) | classnames.js:7:31:7:84 | `` | provenance | | | classnames.js:7:58:7:68 | window.name | classnames.js:7:47:7:69 | classNa ... w.name) | provenance | | | classnames.js:8:47:8:70 | classNa ... w.name) | classnames.js:8:31:8:85 | `` | provenance | | @@ -1319,6 +1361,20 @@ subpaths | various-concat-obfuscations.js:21:17:21:46 | documen ... h.attrs | various-concat-obfuscations.js:17:24:17:28 | attrs | various-concat-obfuscations.js:18:10:18:105 | '
') | various-concat-obfuscations.js:21:4:21:47 | indirec ... .attrs) | | various-concat-obfuscations.js:21:17:21:46 | documen ... h.attrs | various-concat-obfuscations.js:17:24:17:28 | attrs | various-concat-obfuscations.js:18:10:18:105 | '
') [ArrayElement] | various-concat-obfuscations.js:21:4:21:47 | indirec ... .attrs) | #select +| aws-db.js:15:31:15:76 | results ... arValue | aws-db.js:15:31:15:37 | results | aws-db.js:15:31:15:76 | results ... arValue | Cross-site scripting vulnerability due to $@. | aws-db.js:15:31:15:37 | results | user-provided value | +| aws-db.js:20:31:20:54 | respons ... tring() | aws-db.js:20:31:20:38 | response | aws-db.js:20:31:20:54 | respons ... tring() | Cross-site scripting vulnerability due to $@. | aws-db.js:20:31:20:38 | response | user-provided value | +| aws-db.js:24:31:24:47 | response2.records | aws-db.js:24:31:24:39 | response2 | aws-db.js:24:31:24:47 | response2.records | Cross-site scripting vulnerability due to $@. | aws-db.js:24:31:24:39 | response2 | user-provided value | +| aws-db.js:28:31:28:44 | response3.Item | aws-db.js:28:31:28:39 | response3 | aws-db.js:28:31:28:44 | response3.Item | Cross-site scripting vulnerability due to $@. | aws-db.js:28:31:28:39 | response3 | user-provided value | +| aws-db.js:43:31:43:76 | results ... arValue | aws-db.js:43:31:43:37 | results | aws-db.js:43:31:43:76 | results ... arValue | Cross-site scripting vulnerability due to $@. | aws-db.js:43:31:43:37 | results | user-provided value | +| aws-db.js:46:35:46:77 | data.Re ... arValue | aws-db.js:46:35:46:38 | data | aws-db.js:46:35:46:77 | data.Re ... arValue | Cross-site scripting vulnerability due to $@. | aws-db.js:46:35:46:38 | data | user-provided value | +| aws-db.js:51:31:51:54 | respons ... tring() | aws-db.js:51:31:51:38 | response | aws-db.js:51:31:51:54 | respons ... tring() | Cross-site scripting vulnerability due to $@. | aws-db.js:51:31:51:38 | response | user-provided value | +| aws-db.js:54:35:54:54 | data.Body.toString() | aws-db.js:54:35:54:38 | data | aws-db.js:54:35:54:54 | data.Body.toString() | Cross-site scripting vulnerability due to $@. | aws-db.js:54:35:54:38 | data | user-provided value | +| aws-db.js:59:31:59:47 | response1.records | aws-db.js:59:31:59:39 | response1 | aws-db.js:59:31:59:47 | response1.records | Cross-site scripting vulnerability due to $@. | aws-db.js:59:31:59:39 | response1 | user-provided value | +| aws-db.js:62:35:62:46 | data.records | aws-db.js:62:35:62:38 | data | aws-db.js:62:35:62:46 | data.records | Cross-site scripting vulnerability due to $@. | aws-db.js:62:35:62:38 | data | user-provided value | +| aws-db.js:66:31:66:53 | respons ... Results | aws-db.js:66:31:66:39 | response2 | aws-db.js:66:31:66:53 | respons ... Results | Cross-site scripting vulnerability due to $@. | aws-db.js:66:31:66:39 | response2 | user-provided value | +| aws-db.js:69:35:69:52 | data.updateResults | aws-db.js:69:35:69:38 | data | aws-db.js:69:35:69:52 | data.updateResults | Cross-site scripting vulnerability due to $@. | aws-db.js:69:35:69:38 | data | user-provided value | +| aws-db.js:74:35:74:43 | data.Item | aws-db.js:74:35:74:38 | data | aws-db.js:74:35:74:43 | data.Item | Cross-site scripting vulnerability due to $@. | aws-db.js:74:35:74:38 | data | user-provided value | +| aws-db.js:77:35:77:43 | data.Item | aws-db.js:77:35:77:38 | data | aws-db.js:77:35:77:43 | data.Item | Cross-site scripting vulnerability due to $@. | aws-db.js:77:35:77:38 | data | user-provided value | | hana.js:11:37:11:51 | rows[0].comment | hana.js:11:37:11:40 | rows | hana.js:11:37:11:51 | rows[0].comment | Cross-site scripting vulnerability due to $@. | hana.js:11:37:11:40 | rows | user-provided value | | hana.js:16:37:16:51 | rows[0].comment | hana.js:16:37:16:40 | rows | hana.js:16:37:16:51 | rows[0].comment | Cross-site scripting vulnerability due to $@. | hana.js:16:37:16:40 | rows | user-provided value | | hana.js:19:37:19:51 | rows[0].comment | hana.js:19:37:19:40 | rows | hana.js:19:37:19:51 | rows[0].comment | Cross-site scripting vulnerability due to $@. | hana.js:19:37:19:40 | rows | user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/aws-db.js b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/aws-db.js new file mode 100644 index 000000000000..0f345f7172e0 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/aws-db.js @@ -0,0 +1,79 @@ +const { AthenaClient, GetQueryResultsCommand } = require('@aws-sdk/client-athena'); +const { S3Client, GetObjectCommand } = require("@aws-sdk/client-s3"); +const { RDSDataClient, ExecuteStatementCommand } = require("@aws-sdk/client-rds-data"); +const { DynamoDBClient, GetItemCommand } = require("@aws-sdk/client-dynamodb"); + + +const express = require('express'); +const bodyParser = require('body-parser'); +const app = express(); +app.use(bodyParser.json()); + +app.post('/v3/all', async (req, res) => { + const client = new AthenaClient({ region: "us-east-1" }); + const results = await client.send(new GetQueryResultsCommand({ QueryExecutionId })); + document.body.innerHTML = results.ResultSet.Rows[0].Data[0].VarCharValue; // $ Alert[js/xss-additional-sources-dom-test] + + const s3 = new S3Client({ region: "us-east-1" }); + const command = new GetObjectCommand({ Bucket: bucket, Key: key }); + const response = await s3.send(command); + document.body.innerHTML = response.Body.toString(); // $ Alert[js/xss-additional-sources-dom-test] + + const clientRDS = new RDSDataClient({ region: "us-east-1" }); + const response2 = await clientRDS.send(new ExecuteStatementCommand(command)); + document.body.innerHTML = response2.records; // $ Alert[js/xss-additional-sources-dom-test] + + const clientDyamo = new DynamoDBClient({ region: "us-east-1" }); + const response3 = await clientDyamo.send(new GetItemCommand(command)); + document.body.innerHTML = response3.Item; // $ Alert[js/xss-additional-sources-dom-test] + +}); + + +app.post('/v2/all', async (req, res) => { + const AWS = require('aws-sdk'); + const athena = new AWS.Athena(); + const params = { + QueryString: 'SELECT * FROM my_table', + ResultConfiguration: { OutputLocation: 's3://bucket/prefix/' } + }; + const { QueryExecutionId } = await athena.startQueryExecution(params).promise(); + + const results = await athena.getQueryResults({ QueryExecutionId }).promise(); + document.body.innerHTML = results.ResultSet.Rows[0].Data[0].VarCharValue; // $ Alert[js/xss-additional-sources-dom-test] + + athena.getQueryResults({ QueryExecutionId }, (err, data) => { + document.body.innerHTML = data.ResultSet.Rows[0].Data[0].VarCharValue; // $ Alert[js/xss-additional-sources-dom-test] + }); + + const s3 = new AWS.S3({ region: "us-east-1" }); + const response = await s3.getObject({ Bucket: "bucket", Key: "key" }).promise(); + document.body.innerHTML = response.Body.toString(); // $ Alert[js/xss-additional-sources-dom-test] + + s3.getObject({ Bucket: "bucket", Key: "key" }, (err, data) => { + document.body.innerHTML = data.Body.toString(); // $ Alert[js/xss-additional-sources-dom-test] + }); + + const rdsData = new AWS.RDSDataService({ region: "us-east-1" }); + const response1 = await rdsData.executeStatement(params).promise(); + document.body.innerHTML = response1.records; // $ Alert[js/xss-additional-sources-dom-test] + + rdsData.executeStatement(params, function(err, data) { + document.body.innerHTML = data.records; // $ Alert[js/xss-additional-sources-dom-test] + }); + + const response2 = await rdsData.batchExecuteStatement(params).promise(); + document.body.innerHTML = response2.updateResults; // $ Alert[js/xss-additional-sources-dom-test] + + rdsData.batchExecuteStatement(params, function(err, data) { + document.body.innerHTML = data.updateResults; // $ Alert[js/xss-additional-sources-dom-test] + }); + + const dynamodb = new AWS.DynamoDB({ region: "us-east-1" }); + dynamodb.executeStatement(params, (err, data) => { + document.body.innerHTML = data.Item; // $ Alert[js/xss-additional-sources-dom-test] + }); + dynamodb.executeStatement(params).promise().then(data => { + document.body.innerHTML = data.Item; // $ Alert[js/xss-additional-sources-dom-test] + }); +}); From d93033ba4078504a36e4787a7a863ff445f4bf9c Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Tue, 29 Jul 2025 16:50:42 +0200 Subject: [PATCH 13/13] Unified aws-db modeling into singular file --- javascript/ql/lib/ext/athena.model.yml | 29 ------------- javascript/ql/lib/ext/aws-sdk.model.yml | 49 ++++++++++++++++++++-- javascript/ql/lib/ext/client-s3.model.yml | 28 ------------- javascript/ql/lib/ext/dynamodb.model.yml | 30 ------------- javascript/ql/lib/ext/rds-client.model.yml | 31 -------------- 5 files changed, 46 insertions(+), 121 deletions(-) delete mode 100644 javascript/ql/lib/ext/athena.model.yml delete mode 100644 javascript/ql/lib/ext/client-s3.model.yml delete mode 100644 javascript/ql/lib/ext/dynamodb.model.yml delete mode 100644 javascript/ql/lib/ext/rds-client.model.yml diff --git a/javascript/ql/lib/ext/athena.model.yml b/javascript/ql/lib/ext/athena.model.yml deleted file mode 100644 index 225d0926988a..000000000000 --- a/javascript/ql/lib/ext/athena.model.yml +++ /dev/null @@ -1,29 +0,0 @@ -extensions: - - addsTo: - pack: codeql/javascript-all - extensible: sinkModel - data: - - ["AthenaClientV3", "ReturnValue.Member[send].Argument[0]", "sql-injection"] - - ["AthenaClientV2", "ReturnValue.Member[startQueryExecution,createNamedQuery,updateNamedQuery].Argument[0].Member[QueryString]", "sql-injection"] - - - - addsTo: - pack: codeql/javascript-all - extensible: summaryModel - data: - - ["@aws-sdk/client-athena", "Member[StartQueryExecutionCommand,CreateNamedQueryCommand,UpdateNamedQueryCommand]", "Argument[0].Member[QueryString]", "ReturnValue", "taint"] - - - addsTo: - pack: codeql/javascript-all - extensible: typeModel - data: - - ["AthenaClientV3", "@aws-sdk/client-athena", "Member[AthenaClient]"] - - ["AthenaClientV2", "aws-sdk", "Member[Athena]"] - - - addsTo: - pack: codeql/javascript-all - extensible: sourceModel - data: - - ["AthenaClientV3", "ReturnValue.Member[send].ReturnValue.Awaited", "database-access-result"] - - ["AthenaClientV2", "ReturnValue.Member[getQueryResults].ReturnValue.Member[promise].ReturnValue.Awaited", "database-access-result"] - - ["AthenaClientV2", "ReturnValue.Member[getQueryResults].Argument[1].Parameter[1]", "database-access-result"] diff --git a/javascript/ql/lib/ext/aws-sdk.model.yml b/javascript/ql/lib/ext/aws-sdk.model.yml index eefa87fbe613..d850fec7371c 100644 --- a/javascript/ql/lib/ext/aws-sdk.model.yml +++ b/javascript/ql/lib/ext/aws-sdk.model.yml @@ -3,6 +3,49 @@ extensions: pack: codeql/javascript-all extensible: sinkModel data: - - ["aws-sdk", "AnyMember.Argument[0].Member[secretAccessKey,accessKeyId]", "credentials-key"] - - ["aws-sdk", "AnyMember.Member[secretAccessKey,accessKeyId]", "credentials-key"] - - ["aws-sdk", "Member[Credentials].Argument[0,1]", "credentials-key"] + - ["aws-sdk", "AnyMember.Argument[0].Member[secretAccessKey,accessKeyId]", "credentials-key"] + - ["aws-sdk", "AnyMember.Member[secretAccessKey,accessKeyId]", "credentials-key"] + - ["aws-sdk", "Member[Credentials].Argument[0,1]", "credentials-key"] + - ["AWS-V3-Common", "ReturnValue.Member[send].Argument[0]", "sql-injection"] + - ["AthenaClientV2", "ReturnValue.Member[startQueryExecution,createNamedQuery,updateNamedQuery].Argument[0].Member[QueryString]", "sql-injection"] + - ["S3ClientV2", "ReturnValue.Member[selectObjectContent].Argument[0].Member[Expression]", "sql-injection"] + - ["RDSDataClientV2", "ReturnValue.Member[executeStatement,batchExecuteStatement].Argument[0].Member[sql]", "sql-injection"] + - ["RDSDataClientV2", "ReturnValue.Member[batchExecuteStatement].Argument[0].Member[parameterSets].ArrayElement.Member[sql]", "sql-injection"] + - ["DynamoDBClientV2", "ReturnValue.Member[executeStatement].Argument[0].Member[Statement]", "sql-injection"] + - ["DynamoDBClientV2", "ReturnValue.Member[batchExecuteStatement].Argument[0].Member[Statements].ArrayElement.Member[Statement]", "sql-injection"] + - addsTo: + pack: codeql/javascript-all + extensible: summaryModel + data: + - ["@aws-sdk/client-athena", "Member[StartQueryExecutionCommand,CreateNamedQueryCommand,UpdateNamedQueryCommand]", "Argument[0].Member[QueryString]", "ReturnValue", "taint"] + - ["@aws-sdk/client-s3", "Member[SelectObjectContentCommand]", "Argument[0].Member[Expression]", "ReturnValue", "taint"] + - ["@aws-sdk/client-rds-data", "Member[ExecuteStatementCommand,BatchExecuteStatementCommand]", "Argument[0].Member[sql]", "ReturnValue", "taint"] + - ["@aws-sdk/client-rds-data", "Member[BatchExecuteStatementCommand]", "Argument[0].Member[parameterSets].ArrayElement.Member[sql]", "ReturnValue", "taint"] + - ["@aws-sdk/client-rds-data", "Member[ExecuteSqlCommand]", "Argument[0].Member[sqlStatements]", "ReturnValue", "taint"] + - ["@aws-sdk/client-dynamodb", "Member[ExecuteStatementCommand]", "Argument[0].Member[Statement]", "ReturnValue", "taint"] + - ["@aws-sdk/client-dynamodb", "Member[BatchExecuteStatementCommand]", "Argument[0].Member[Statements].ArrayElement.Member[Statement]", "ReturnValue", "taint"] + - addsTo: + pack: codeql/javascript-all + extensible: typeModel + data: + - ["AthenaClientV2", "aws-sdk", "Member[Athena]"] + - ["S3ClientV2", "aws-sdk", "Member[S3]"] + - ["RDSDataClientV2", "aws-sdk", "Member[RDSDataService]"] + - ["DynamoDBClientV2", "aws-sdk", "Member[DynamoDB]"] + - ["AWS-V3-Common", "@aws-sdk/client-athena", "Member[AthenaClient]"] + - ["AWS-V3-Common", "@aws-sdk/client-s3", "Member[S3Client]"] + - ["AWS-V3-Common", "@aws-sdk/client-dynamodb", "Member[DynamoDBClient,DynamoDB]"] + - ["AWS-V3-Common", "@aws-sdk/client-rds-data", "Member[RDSDataClient]"] + - addsTo: + pack: codeql/javascript-all + extensible: sourceModel + data: + - ["AWS-V3-Common", "ReturnValue.Member[send].ReturnValue.Awaited", "database-access-result"] + - ["AthenaClientV2", "ReturnValue.Member[getQueryResults].ReturnValue.Member[promise].ReturnValue.Awaited", "database-access-result"] + - ["AthenaClientV2", "ReturnValue.Member[getQueryResults].Argument[1].Parameter[1]", "database-access-result"] + - ["S3ClientV2", "ReturnValue.Member[getObject].ReturnValue.Member[promise].ReturnValue.Awaited", "database-access-result"] + - ["S3ClientV2", "ReturnValue.Member[getObject].Argument[1].Parameter[1]", "database-access-result"] + - ["RDSDataClientV2", "ReturnValue.Member[executeStatement,batchExecuteStatement].ReturnValue.Member[promise].ReturnValue.Awaited", "database-access-result"] + - ["RDSDataClientV2", "ReturnValue.Member[executeStatement,batchExecuteStatement].Argument[1].Parameter[1]", "database-access-result"] + - ["DynamoDBClientV2", "ReturnValue.Member[executeStatement,batchExecuteStatement,query,scan,getItem,batchGetItem].ReturnValue.Member[promise].ReturnValue.Awaited", "database-access-result"] + - ["DynamoDBClientV2", "ReturnValue.Member[executeStatement,batchExecuteStatement,query,scan,getItem,batchGetItem].Argument[1].Parameter[1]", "database-access-result"] diff --git a/javascript/ql/lib/ext/client-s3.model.yml b/javascript/ql/lib/ext/client-s3.model.yml deleted file mode 100644 index 4deb2d6b7054..000000000000 --- a/javascript/ql/lib/ext/client-s3.model.yml +++ /dev/null @@ -1,28 +0,0 @@ -extensions: - - addsTo: - pack: codeql/javascript-all - extensible: sinkModel - data: - - ["S3ClientV3", "ReturnValue.Member[send].Argument[0]", "sql-injection"] - - ["S3ClientV2", "ReturnValue.Member[selectObjectContent].Argument[0].Member[Expression]", "sql-injection"] - - - addsTo: - pack: codeql/javascript-all - extensible: summaryModel - data: - - ["@aws-sdk/client-s3", "Member[SelectObjectContentCommand]", "Argument[0].Member[Expression]", "ReturnValue", "taint"] - - - addsTo: - pack: codeql/javascript-all - extensible: typeModel - data: - - ["S3ClientV3", "@aws-sdk/client-s3", "Member[S3Client]"] - - ["S3ClientV2", "aws-sdk", "Member[S3]"] - - - addsTo: - pack: codeql/javascript-all - extensible: sourceModel - data: - - ["S3ClientV3", "ReturnValue.Member[send].ReturnValue.Awaited", "database-access-result"] - - ["S3ClientV2", "ReturnValue.Member[getObject].ReturnValue.Member[promise].ReturnValue.Awaited", "database-access-result"] - - ["S3ClientV2", "ReturnValue.Member[getObject].Argument[1].Parameter[1]", "database-access-result"] diff --git a/javascript/ql/lib/ext/dynamodb.model.yml b/javascript/ql/lib/ext/dynamodb.model.yml deleted file mode 100644 index 0efba4d4bc45..000000000000 --- a/javascript/ql/lib/ext/dynamodb.model.yml +++ /dev/null @@ -1,30 +0,0 @@ -extensions: - - addsTo: - pack: codeql/javascript-all - extensible: sinkModel - data: - - ["DynamoDBClientV3", "ReturnValue.Member[send].Argument[0]", "sql-injection"] - - ["DynamoDBClientV2", "ReturnValue.Member[executeStatement].Argument[0].Member[Statement]", "sql-injection"] - - ["DynamoDBClientV2", "ReturnValue.Member[batchExecuteStatement].Argument[0].Member[Statements].ArrayElement.Member[Statement]", "sql-injection"] - - - addsTo: - pack: codeql/javascript-all - extensible: summaryModel - data: - - ["@aws-sdk/client-dynamodb", "Member[ExecuteStatementCommand]", "Argument[0].Member[Statement]", "ReturnValue", "taint"] - - ["@aws-sdk/client-dynamodb", "Member[BatchExecuteStatementCommand]", "Argument[0].Member[Statements].ArrayElement.Member[Statement]", "ReturnValue", "taint"] - - - addsTo: - pack: codeql/javascript-all - extensible: typeModel - data: - - ["DynamoDBClientV3", "@aws-sdk/client-dynamodb", "Member[DynamoDBClient,DynamoDB]"] - - ["DynamoDBClientV2", "aws-sdk", "Member[DynamoDB]"] - - - addsTo: - pack: codeql/javascript-all - extensible: sourceModel - data: - - ["DynamoDBClientV3", "ReturnValue.Member[send].ReturnValue.Awaited", "database-access-result"] - - ["DynamoDBClientV2", "ReturnValue.Member[executeStatement,batchExecuteStatement].ReturnValue.Member[promise].ReturnValue.Awaited", "database-access-result"] - - ["DynamoDBClientV2", "ReturnValue.Member[executeStatement,batchExecuteStatement].Argument[1].Parameter[1]", "database-access-result"] diff --git a/javascript/ql/lib/ext/rds-client.model.yml b/javascript/ql/lib/ext/rds-client.model.yml deleted file mode 100644 index 2535c88a02c8..000000000000 --- a/javascript/ql/lib/ext/rds-client.model.yml +++ /dev/null @@ -1,31 +0,0 @@ -extensions: - - addsTo: - pack: codeql/javascript-all - extensible: sinkModel - data: - - ["RDSDataClientV3", "ReturnValue.Member[send].Argument[0]", "sql-injection"] - - ["RDSDataClientV2", "ReturnValue.Member[executeStatement,batchExecuteStatement].Argument[0].Member[sql]", "sql-injection"] - - ["RDSDataClientV2", "ReturnValue.Member[batchExecuteStatement].Argument[0].Member[parameterSets].ArrayElement.Member[sql]", "sql-injection"] - - - addsTo: - pack: codeql/javascript-all - extensible: summaryModel - data: - - ["@aws-sdk/client-rds-data", "Member[ExecuteStatementCommand,BatchExecuteStatementCommand]", "Argument[0].Member[sql]", "ReturnValue", "taint"] - - ["@aws-sdk/client-rds-data", "Member[BatchExecuteStatementCommand]", "Argument[0].Member[parameterSets].ArrayElement.Member[sql]", "ReturnValue", "taint"] - - ["@aws-sdk/client-rds-data", "Member[ExecuteSqlCommand]", "Argument[0].Member[sqlStatements]", "ReturnValue", "taint"] - - - addsTo: - pack: codeql/javascript-all - extensible: typeModel - data: - - ["RDSDataClientV3", "@aws-sdk/client-rds-data", "Member[RDSDataClient]"] - - ["RDSDataClientV2", "aws-sdk", "Member[RDSDataService]"] - - - addsTo: - pack: codeql/javascript-all - extensible: sourceModel - data: - - ["RDSDataClientV3", "ReturnValue.Member[send].ReturnValue.Awaited", "database-access-result"] - - ["RDSDataClientV2", "ReturnValue.Member[executeStatement,batchExecuteStatement].ReturnValue.Member[promise].ReturnValue.Awaited", "database-access-result"] - - ["RDSDataClientV2", "ReturnValue.Member[executeStatement,batchExecuteStatement].Argument[1].Parameter[1]", "database-access-result"]