From d6a14e63ba35d15eac27d7400a92ef059e0eefd8 Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Mon, 15 Sep 2025 16:21:12 +0200 Subject: [PATCH 01/11] JS: Add test cases for promisification libraries. --- .../CommandInjection.expected | 15 ++ .../CommandInjection/promisification.js | 153 ++++++++++++++++++ 2 files changed, 168 insertions(+) create mode 100644 javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/promisification.js diff --git a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected index dc9c65822ba0..22bcd24a578e 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected @@ -82,6 +82,9 @@ | other.js:28:27:28:29 | cmd | other.js:5:25:5:31 | req.url | other.js:28:27:28:29 | cmd | This command line depends on a $@. | other.js:5:25:5:31 | req.url | user-provided value | | other.js:30:33:30:35 | cmd | other.js:5:25:5:31 | req.url | other.js:30:33:30:35 | cmd | This command line depends on a $@. | other.js:5:25:5:31 | req.url | user-provided value | | other.js:34:44:34:46 | cmd | other.js:5:25:5:31 | req.url | other.js:34:44:34:46 | cmd | This command line depends on a $@. | other.js:5:25:5:31 | req.url | user-provided value | +| promisification.js:24:22:24:25 | code | promisification.js:21:18:21:25 | req.body | promisification.js:24:22:24:25 | code | This command line depends on a $@. | promisification.js:21:18:21:25 | req.body | user-provided value | +| promisification.js:52:21:52:24 | code | promisification.js:49:18:49:25 | req.body | promisification.js:52:21:52:24 | code | This command line depends on a $@. | promisification.js:49:18:49:25 | req.body | user-provided value | +| promisification.js:55:15:55:18 | code | promisification.js:49:18:49:25 | req.body | promisification.js:55:15:55:18 | code | This command line depends on a $@. | promisification.js:49:18:49:25 | req.body | user-provided value | | third-party-command-injection.js:6:21:6:27 | command | third-party-command-injection.js:5:20:5:26 | command | third-party-command-injection.js:6:21:6:27 | command | This command line depends on a $@. | third-party-command-injection.js:5:20:5:26 | command | user-provided value | edges | actions.js:8:9:8:13 | title | actions.js:9:16:9:20 | title | provenance | | @@ -259,6 +262,11 @@ edges | other.js:5:9:5:11 | cmd | other.js:34:44:34:46 | cmd | provenance | | | other.js:5:15:5:38 | url.par ... , true) | other.js:5:9:5:11 | cmd | provenance | | | other.js:5:25:5:31 | req.url | other.js:5:15:5:38 | url.par ... , true) | provenance | | +| promisification.js:21:11:21:14 | code | promisification.js:24:22:24:25 | code | provenance | | +| promisification.js:21:18:21:25 | req.body | promisification.js:21:11:21:14 | code | provenance | | +| promisification.js:49:11:49:14 | code | promisification.js:52:21:52:24 | code | provenance | | +| promisification.js:49:11:49:14 | code | promisification.js:55:15:55:18 | code | provenance | | +| promisification.js:49:18:49:25 | req.body | promisification.js:49:11:49:14 | code | provenance | | | third-party-command-injection.js:5:20:5:26 | command | third-party-command-injection.js:6:21:6:27 | command | provenance | | nodes | actions.js:8:9:8:13 | title | semmle.label | title | @@ -446,6 +454,13 @@ nodes | other.js:28:27:28:29 | cmd | semmle.label | cmd | | other.js:30:33:30:35 | cmd | semmle.label | cmd | | other.js:34:44:34:46 | cmd | semmle.label | cmd | +| promisification.js:21:11:21:14 | code | semmle.label | code | +| promisification.js:21:18:21:25 | req.body | semmle.label | req.body | +| promisification.js:24:22:24:25 | code | semmle.label | code | +| promisification.js:49:11:49:14 | code | semmle.label | code | +| promisification.js:49:18:49:25 | req.body | semmle.label | req.body | +| promisification.js:52:21:52:24 | code | semmle.label | code | +| promisification.js:55:15:55:18 | code | semmle.label | code | | third-party-command-injection.js:5:20:5:26 | command | semmle.label | command | | third-party-command-injection.js:6:21:6:27 | command | semmle.label | command | subpaths diff --git a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/promisification.js b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/promisification.js new file mode 100644 index 000000000000..09e4285b4b7e --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/promisification.js @@ -0,0 +1,153 @@ +const express = require('express'); +const bodyParser = require('body-parser'); +const cp = require('child_process'); + +const app = express(); +app.use(bodyParser.json()); + +function legacyEval(code) { + cp.exec(code.code); // $ MISSING: Alert +} + +app.post('/eval', async (req, res) => { + const { promisify } = require('util'); + const evalAsync = promisify(legacyEval); + const code = req.body; // $ MISSING: Source + evalAsync(code); +}); + +app.post('/eval', async (req, res) => { + const directPromisify = require('util.promisify'); + const code = req.body; // $ Source + + const promisifiedExec3 = directPromisify(cp.exec); + promisifiedExec3(code); // $ Alert +}); + +app.post('/eval', async (req, res) => { + const promisify2 = require('util.promisify-all'); + const promisifiedCp = promisify2(cp); + const code = req.body; // $ MISSING: Source + promisifiedCp.exec(code); // $ MISSING: Alert +}); + + +app.post('/eval', async (req, res) => { + var garPromisify = require("@gar/promisify"); + const code = req.body; // $ MISSING: Source + + const promisifiedExec = garPromisify(cp.exec); + promisifiedExec(code); // $ MISSING: Alert + + const promisifiedCp = garPromisify(cp); + promisifiedCp.exec(code); // $ MISSING: Alert +}); + +app.post('/eval', async (req, res) => { + require('util.promisify/shim')(); + const util = require('util'); + const code = req.body; // $ Source + + const promisifiedExec = util.promisify(cp.exec); + promisifiedExec(code); // $ Alert + + const execAsync = util.promisify(cp.exec.bind(cp)); + execAsync(code); // $ Alert +}); + + +app.post('/eval', async (req, res) => { + const es6Promisify = require("es6-promisify"); + let cmd = req.body; // $ MISSING: Source + + // Test basic promisification + const promisifiedExec = es6Promisify(cp.exec); + promisifiedExec(cmd); // $ MISSING: Alert + + // Test with method binding + const execBoundAsync = es6Promisify(cp.exec.bind(cp)); + execBoundAsync(cmd); // $ MISSING: Alert + + const promisifiedExecMulti = es6Promisify(cp.exec, { + multiArgs: true + }); + promisifiedExecMulti(cmd); // $ MISSING: Alert + + const promisifiedCp = es6Promisify.promisifyAll(cp); + promisifiedCp.exec(cmd); // $ MISSING: Alert + promisifiedCp.execFile(cmd); // $ MISSING: Alert + promisifiedCp.spawn(cmd); // $ MISSING: Alert + + const lambda = es6Promisify((code, callback) => { + try { + const result = cp.exec(code); // $ MISSING: Alert + callback(null, result); + } catch (err) { + callback(err); + } + }); + lambda(cmd); +}); + + +app.post('/eval', async (req, res) => { + var thenifyAll = require('thenify-all'); + var cpThenifyAll = thenifyAll(require('child_process'), {}, [ + 'exec', + 'execSync', + ]); + const code = req.body; // $ MISSING: Source + cpThenifyAll.exec(code); // $ MISSING: Alert + cpThenifyAll.execSync(code); // $ MISSING: Alert + cpThenifyAll.execFile(code); // $ MISSING: Alert - not promisified, as it is not listed in `thenifyAll` + + + var cpThenifyAll1 = thenifyAll.withCallback(require('child_process'), {}, ['exec']); + cpThenifyAll1.exec(code, function (err, string) {}); // $ MISSING: Alert + + var cpThenifyAll2 = thenifyAll(require('child_process')); + cpThenifyAll2.exec(code); // $ MISSING: Alert +}); + +app.post('/eval', async (req, res) => { + const maybe = require('call-me-maybe'); + const code = req.body; // $ MISSING: Source + + function createExecPromise(cmd) { + return new Promise((resolve) => { + resolve(cmd); + }); + } + + const cmdPromise = createExecPromise(code); + maybe(null, cmdPromise).then(cmd => { + cp.exec(cmd); // $ MISSING: Alert + }); +}); + +app.post('/eval', async (req, res) => { + const utilPromisify = require('util-promisify'); + const code = req.body; // $ MISSING: Source + + const promisifiedExec = utilPromisify(cp.exec); + promisifiedExec(code); // $ MISSING: Alert + + const execAsync = utilPromisify(cp.exec.bind(cp)); + execAsync(code); // $ MISSING: Alert +}); + +app.post('/eval', async (req, res) => { + const {promisify, promisifyAll} = require('@google-cloud/promisify'); + const code = req.body; // $ MISSING: Source + + const promisifiedExec = promisify(cp.exec); + promisifiedExec(code); // $ MISSING: Alert + + const execAsync = promisify(cp.exec.bind(cp)); + execAsync(code); // $ MISSING: Alert + + const promisifiedCp = promisifyAll(cp); + promisifiedCp.exec(code); // $ MISSING: Alert + promisifiedCp.execFile(code); // $ MISSING: Alert + promisifiedCp.spawn(code); // $ MISSING: Alert +}); From 22b61852a1438ba1ca91e6c8dfa8da67a4eacca2 Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Mon, 15 Sep 2025 16:31:14 +0200 Subject: [PATCH 02/11] JS: Add modeling for `thenify-all` --- javascript/ql/lib/semmle/javascript/Promises.qll | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/javascript/ql/lib/semmle/javascript/Promises.qll b/javascript/ql/lib/semmle/javascript/Promises.qll index 51411463efc6..fb80af858f2f 100644 --- a/javascript/ql/lib/semmle/javascript/Promises.qll +++ b/javascript/ql/lib/semmle/javascript/Promises.qll @@ -728,7 +728,8 @@ module Promisify { this = [ DataFlow::moduleMember("bluebird", "promisifyAll"), - DataFlow::moduleImport(["util-promisifyall", "pify"]) + DataFlow::moduleMember("thenify-all", "withCallback"), + DataFlow::moduleImport(["util-promisifyall", "pify", "thenify-all"]) ].getACall() } } From d37425ae3e95c24085625fc7799ccc5e5f085889 Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Mon, 15 Sep 2025 16:51:19 +0200 Subject: [PATCH 03/11] JS: Treat promisify(obj).member as obj.member --- .../ql/lib/semmle/javascript/ApiGraphs.qll | 11 +++++++++++ .../CommandInjection/CommandInjection.expected | 18 ++++++++++++++++++ .../CommandInjection/promisification.js | 12 ++++++------ 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll index 3bb04f2686be..6b2cc3af235e 100644 --- a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll +++ b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll @@ -1115,6 +1115,17 @@ module API { ref = awaited(call) ) or + // Handle promisified object member access: promisify(obj).member should be treated as obj.member (promisified) + exists( + DataFlow::SourceNode promisifiedObj, DataFlow::SourceNode originalObj, string member + | + promisifiedObj instanceof Promisify::PromisifyAllCall and + originalObj.flowsTo(promisifiedObj.(Promisify::PromisifyAllCall).getArgument(0)) and + use(base, originalObj) and + lbl = Label::member(member) and + ref = promisifiedObj.getAPropertyRead(member) + ) + or decoratorDualEdge(base, lbl, ref) or decoratorUseEdge(base, lbl, ref) diff --git a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected index 22bcd24a578e..d68af94417c3 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected @@ -85,6 +85,11 @@ | promisification.js:24:22:24:25 | code | promisification.js:21:18:21:25 | req.body | promisification.js:24:22:24:25 | code | This command line depends on a $@. | promisification.js:21:18:21:25 | req.body | user-provided value | | promisification.js:52:21:52:24 | code | promisification.js:49:18:49:25 | req.body | promisification.js:52:21:52:24 | code | This command line depends on a $@. | promisification.js:49:18:49:25 | req.body | user-provided value | | promisification.js:55:15:55:18 | code | promisification.js:49:18:49:25 | req.body | promisification.js:55:15:55:18 | code | This command line depends on a $@. | promisification.js:49:18:49:25 | req.body | user-provided value | +| promisification.js:100:23:100:26 | code | promisification.js:99:18:99:25 | req.body | promisification.js:100:23:100:26 | code | This command line depends on a $@. | promisification.js:99:18:99:25 | req.body | user-provided value | +| promisification.js:101:27:101:30 | code | promisification.js:99:18:99:25 | req.body | promisification.js:101:27:101:30 | code | This command line depends on a $@. | promisification.js:99:18:99:25 | req.body | user-provided value | +| promisification.js:102:27:102:30 | code | promisification.js:99:18:99:25 | req.body | promisification.js:102:27:102:30 | code | This command line depends on a $@. | promisification.js:99:18:99:25 | req.body | user-provided value | +| promisification.js:106:24:106:27 | code | promisification.js:99:18:99:25 | req.body | promisification.js:106:24:106:27 | code | This command line depends on a $@. | promisification.js:99:18:99:25 | req.body | user-provided value | +| promisification.js:109:24:109:27 | code | promisification.js:99:18:99:25 | req.body | promisification.js:109:24:109:27 | code | This command line depends on a $@. | promisification.js:99:18:99:25 | req.body | user-provided value | | third-party-command-injection.js:6:21:6:27 | command | third-party-command-injection.js:5:20:5:26 | command | third-party-command-injection.js:6:21:6:27 | command | This command line depends on a $@. | third-party-command-injection.js:5:20:5:26 | command | user-provided value | edges | actions.js:8:9:8:13 | title | actions.js:9:16:9:20 | title | provenance | | @@ -267,6 +272,12 @@ edges | promisification.js:49:11:49:14 | code | promisification.js:52:21:52:24 | code | provenance | | | promisification.js:49:11:49:14 | code | promisification.js:55:15:55:18 | code | provenance | | | promisification.js:49:18:49:25 | req.body | promisification.js:49:11:49:14 | code | provenance | | +| promisification.js:99:11:99:14 | code | promisification.js:100:23:100:26 | code | provenance | | +| promisification.js:99:11:99:14 | code | promisification.js:101:27:101:30 | code | provenance | | +| promisification.js:99:11:99:14 | code | promisification.js:102:27:102:30 | code | provenance | | +| promisification.js:99:11:99:14 | code | promisification.js:106:24:106:27 | code | provenance | | +| promisification.js:99:11:99:14 | code | promisification.js:109:24:109:27 | code | provenance | | +| promisification.js:99:18:99:25 | req.body | promisification.js:99:11:99:14 | code | provenance | | | third-party-command-injection.js:5:20:5:26 | command | third-party-command-injection.js:6:21:6:27 | command | provenance | | nodes | actions.js:8:9:8:13 | title | semmle.label | title | @@ -461,6 +472,13 @@ nodes | promisification.js:49:18:49:25 | req.body | semmle.label | req.body | | promisification.js:52:21:52:24 | code | semmle.label | code | | promisification.js:55:15:55:18 | code | semmle.label | code | +| promisification.js:99:11:99:14 | code | semmle.label | code | +| promisification.js:99:18:99:25 | req.body | semmle.label | req.body | +| promisification.js:100:23:100:26 | code | semmle.label | code | +| promisification.js:101:27:101:30 | code | semmle.label | code | +| promisification.js:102:27:102:30 | code | semmle.label | code | +| promisification.js:106:24:106:27 | code | semmle.label | code | +| promisification.js:109:24:109:27 | code | semmle.label | code | | third-party-command-injection.js:5:20:5:26 | command | semmle.label | command | | third-party-command-injection.js:6:21:6:27 | command | semmle.label | command | subpaths diff --git a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/promisification.js b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/promisification.js index 09e4285b4b7e..598cd9bb4711 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/promisification.js +++ b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/promisification.js @@ -96,17 +96,17 @@ app.post('/eval', async (req, res) => { 'exec', 'execSync', ]); - const code = req.body; // $ MISSING: Source - cpThenifyAll.exec(code); // $ MISSING: Alert - cpThenifyAll.execSync(code); // $ MISSING: Alert - cpThenifyAll.execFile(code); // $ MISSING: Alert - not promisified, as it is not listed in `thenifyAll` + const code = req.body; // $ Source + cpThenifyAll.exec(code); // $ Alert + cpThenifyAll.execSync(code); // $ Alert + cpThenifyAll.execFile(code); // $ SPURIOUS: Alert - not promisified, as it is not listed in `thenifyAll`, but it should fine to flag it var cpThenifyAll1 = thenifyAll.withCallback(require('child_process'), {}, ['exec']); - cpThenifyAll1.exec(code, function (err, string) {}); // $ MISSING: Alert + cpThenifyAll1.exec(code, function (err, string) {}); // $ Alert var cpThenifyAll2 = thenifyAll(require('child_process')); - cpThenifyAll2.exec(code); // $ MISSING: Alert + cpThenifyAll2.exec(code); // $ Alert }); app.post('/eval', async (req, res) => { From 312471e9db575802bba7fc49bead23732e84b22e Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Mon, 15 Sep 2025 16:55:27 +0200 Subject: [PATCH 04/11] JS: Add modeling for `@google-cloud/promisify` --- .../ql/lib/semmle/javascript/Promises.qll | 4 +++- .../CommandInjection/CommandInjection.expected | 18 ++++++++++++++++++ .../CommandInjection/promisification.js | 12 ++++++------ 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/Promises.qll b/javascript/ql/lib/semmle/javascript/Promises.qll index fb80af858f2f..91ae38b94183 100644 --- a/javascript/ql/lib/semmle/javascript/Promises.qll +++ b/javascript/ql/lib/semmle/javascript/Promises.qll @@ -727,7 +727,7 @@ module Promisify { PromisifyAllCall() { this = [ - DataFlow::moduleMember("bluebird", "promisifyAll"), + DataFlow::moduleMember(["bluebird", "@google-cloud/promisify"], "promisifyAll"), DataFlow::moduleMember("thenify-all", "withCallback"), DataFlow::moduleImport(["util-promisifyall", "pify", "thenify-all"]) ].getACall() @@ -747,6 +747,8 @@ module Promisify { this = DataFlow::moduleImport("thenify").getACall() or this = DataFlow::moduleMember("thenify", "withCallback").getACall() + or + this = DataFlow::moduleMember("@google-cloud/promisify", "promisify").getACall() } } } diff --git a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected index d68af94417c3..bb89e9d06548 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected @@ -90,6 +90,11 @@ | promisification.js:102:27:102:30 | code | promisification.js:99:18:99:25 | req.body | promisification.js:102:27:102:30 | code | This command line depends on a $@. | promisification.js:99:18:99:25 | req.body | user-provided value | | promisification.js:106:24:106:27 | code | promisification.js:99:18:99:25 | req.body | promisification.js:106:24:106:27 | code | This command line depends on a $@. | promisification.js:99:18:99:25 | req.body | user-provided value | | promisification.js:109:24:109:27 | code | promisification.js:99:18:99:25 | req.body | promisification.js:109:24:109:27 | code | This command line depends on a $@. | promisification.js:99:18:99:25 | req.body | user-provided value | +| promisification.js:144:21:144:24 | code | promisification.js:141:18:141:25 | req.body | promisification.js:144:21:144:24 | code | This command line depends on a $@. | promisification.js:141:18:141:25 | req.body | user-provided value | +| promisification.js:147:15:147:18 | code | promisification.js:141:18:141:25 | req.body | promisification.js:147:15:147:18 | code | This command line depends on a $@. | promisification.js:141:18:141:25 | req.body | user-provided value | +| promisification.js:150:24:150:27 | code | promisification.js:141:18:141:25 | req.body | promisification.js:150:24:150:27 | code | This command line depends on a $@. | promisification.js:141:18:141:25 | req.body | user-provided value | +| promisification.js:151:28:151:31 | code | promisification.js:141:18:141:25 | req.body | promisification.js:151:28:151:31 | code | This command line depends on a $@. | promisification.js:141:18:141:25 | req.body | user-provided value | +| promisification.js:152:25:152:28 | code | promisification.js:141:18:141:25 | req.body | promisification.js:152:25:152:28 | code | This command line depends on a $@. | promisification.js:141:18:141:25 | req.body | user-provided value | | third-party-command-injection.js:6:21:6:27 | command | third-party-command-injection.js:5:20:5:26 | command | third-party-command-injection.js:6:21:6:27 | command | This command line depends on a $@. | third-party-command-injection.js:5:20:5:26 | command | user-provided value | edges | actions.js:8:9:8:13 | title | actions.js:9:16:9:20 | title | provenance | | @@ -278,6 +283,12 @@ edges | promisification.js:99:11:99:14 | code | promisification.js:106:24:106:27 | code | provenance | | | promisification.js:99:11:99:14 | code | promisification.js:109:24:109:27 | code | provenance | | | promisification.js:99:18:99:25 | req.body | promisification.js:99:11:99:14 | code | provenance | | +| promisification.js:141:11:141:14 | code | promisification.js:144:21:144:24 | code | provenance | | +| promisification.js:141:11:141:14 | code | promisification.js:147:15:147:18 | code | provenance | | +| promisification.js:141:11:141:14 | code | promisification.js:150:24:150:27 | code | provenance | | +| promisification.js:141:11:141:14 | code | promisification.js:151:28:151:31 | code | provenance | | +| promisification.js:141:11:141:14 | code | promisification.js:152:25:152:28 | code | provenance | | +| promisification.js:141:18:141:25 | req.body | promisification.js:141:11:141:14 | code | provenance | | | third-party-command-injection.js:5:20:5:26 | command | third-party-command-injection.js:6:21:6:27 | command | provenance | | nodes | actions.js:8:9:8:13 | title | semmle.label | title | @@ -479,6 +490,13 @@ nodes | promisification.js:102:27:102:30 | code | semmle.label | code | | promisification.js:106:24:106:27 | code | semmle.label | code | | promisification.js:109:24:109:27 | code | semmle.label | code | +| promisification.js:141:11:141:14 | code | semmle.label | code | +| promisification.js:141:18:141:25 | req.body | semmle.label | req.body | +| promisification.js:144:21:144:24 | code | semmle.label | code | +| promisification.js:147:15:147:18 | code | semmle.label | code | +| promisification.js:150:24:150:27 | code | semmle.label | code | +| promisification.js:151:28:151:31 | code | semmle.label | code | +| promisification.js:152:25:152:28 | code | semmle.label | code | | third-party-command-injection.js:5:20:5:26 | command | semmle.label | command | | third-party-command-injection.js:6:21:6:27 | command | semmle.label | command | subpaths diff --git a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/promisification.js b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/promisification.js index 598cd9bb4711..5dcefce9896e 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/promisification.js +++ b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/promisification.js @@ -138,16 +138,16 @@ app.post('/eval', async (req, res) => { app.post('/eval', async (req, res) => { const {promisify, promisifyAll} = require('@google-cloud/promisify'); - const code = req.body; // $ MISSING: Source + const code = req.body; // $ Source const promisifiedExec = promisify(cp.exec); - promisifiedExec(code); // $ MISSING: Alert + promisifiedExec(code); // $ Alert const execAsync = promisify(cp.exec.bind(cp)); - execAsync(code); // $ MISSING: Alert + execAsync(code); // $ Alert const promisifiedCp = promisifyAll(cp); - promisifiedCp.exec(code); // $ MISSING: Alert - promisifiedCp.execFile(code); // $ MISSING: Alert - promisifiedCp.spawn(code); // $ MISSING: Alert + promisifiedCp.exec(code); // $ Alert + promisifiedCp.execFile(code); // $ Alert + promisifiedCp.spawn(code); // $ Alert }); From 35c75c00ba2e00dcc0c2f54ee38ac7b5b2918d73 Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Mon, 15 Sep 2025 16:58:11 +0200 Subject: [PATCH 05/11] JS: Add modeling for `@gar/promisify` --- javascript/ql/lib/semmle/javascript/Promises.qll | 4 ++-- .../CWE-078/CommandInjection/CommandInjection.expected | 9 +++++++++ .../Security/CWE-078/CommandInjection/promisification.js | 6 +++--- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/Promises.qll b/javascript/ql/lib/semmle/javascript/Promises.qll index 91ae38b94183..78508cb0b7b4 100644 --- a/javascript/ql/lib/semmle/javascript/Promises.qll +++ b/javascript/ql/lib/semmle/javascript/Promises.qll @@ -729,7 +729,7 @@ module Promisify { [ DataFlow::moduleMember(["bluebird", "@google-cloud/promisify"], "promisifyAll"), DataFlow::moduleMember("thenify-all", "withCallback"), - DataFlow::moduleImport(["util-promisifyall", "pify", "thenify-all"]) + DataFlow::moduleImport(["util-promisifyall", "pify", "thenify-all", "@gar/promisify"]) ].getACall() } } @@ -744,7 +744,7 @@ module Promisify { or this = DataFlow::moduleImport(["pify", "util.promisify"]).getACall() or - this = DataFlow::moduleImport("thenify").getACall() + this = DataFlow::moduleImport(["thenify", "@gar/promisify"]).getACall() or this = DataFlow::moduleMember("thenify", "withCallback").getACall() or diff --git a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected index bb89e9d06548..6fb2234c2c71 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected @@ -83,6 +83,8 @@ | other.js:30:33:30:35 | cmd | other.js:5:25:5:31 | req.url | other.js:30:33:30:35 | cmd | This command line depends on a $@. | other.js:5:25:5:31 | req.url | user-provided value | | other.js:34:44:34:46 | cmd | other.js:5:25:5:31 | req.url | other.js:34:44:34:46 | cmd | This command line depends on a $@. | other.js:5:25:5:31 | req.url | user-provided value | | promisification.js:24:22:24:25 | code | promisification.js:21:18:21:25 | req.body | promisification.js:24:22:24:25 | code | This command line depends on a $@. | promisification.js:21:18:21:25 | req.body | user-provided value | +| promisification.js:40:21:40:24 | code | promisification.js:37:18:37:25 | req.body | promisification.js:40:21:40:24 | code | This command line depends on a $@. | promisification.js:37:18:37:25 | req.body | user-provided value | +| promisification.js:43:24:43:27 | code | promisification.js:37:18:37:25 | req.body | promisification.js:43:24:43:27 | code | This command line depends on a $@. | promisification.js:37:18:37:25 | req.body | user-provided value | | promisification.js:52:21:52:24 | code | promisification.js:49:18:49:25 | req.body | promisification.js:52:21:52:24 | code | This command line depends on a $@. | promisification.js:49:18:49:25 | req.body | user-provided value | | promisification.js:55:15:55:18 | code | promisification.js:49:18:49:25 | req.body | promisification.js:55:15:55:18 | code | This command line depends on a $@. | promisification.js:49:18:49:25 | req.body | user-provided value | | promisification.js:100:23:100:26 | code | promisification.js:99:18:99:25 | req.body | promisification.js:100:23:100:26 | code | This command line depends on a $@. | promisification.js:99:18:99:25 | req.body | user-provided value | @@ -274,6 +276,9 @@ edges | other.js:5:25:5:31 | req.url | other.js:5:15:5:38 | url.par ... , true) | provenance | | | promisification.js:21:11:21:14 | code | promisification.js:24:22:24:25 | code | provenance | | | promisification.js:21:18:21:25 | req.body | promisification.js:21:11:21:14 | code | provenance | | +| promisification.js:37:11:37:14 | code | promisification.js:40:21:40:24 | code | provenance | | +| promisification.js:37:11:37:14 | code | promisification.js:43:24:43:27 | code | provenance | | +| promisification.js:37:18:37:25 | req.body | promisification.js:37:11:37:14 | code | provenance | | | promisification.js:49:11:49:14 | code | promisification.js:52:21:52:24 | code | provenance | | | promisification.js:49:11:49:14 | code | promisification.js:55:15:55:18 | code | provenance | | | promisification.js:49:18:49:25 | req.body | promisification.js:49:11:49:14 | code | provenance | | @@ -479,6 +484,10 @@ nodes | promisification.js:21:11:21:14 | code | semmle.label | code | | promisification.js:21:18:21:25 | req.body | semmle.label | req.body | | promisification.js:24:22:24:25 | code | semmle.label | code | +| promisification.js:37:11:37:14 | code | semmle.label | code | +| promisification.js:37:18:37:25 | req.body | semmle.label | req.body | +| promisification.js:40:21:40:24 | code | semmle.label | code | +| promisification.js:43:24:43:27 | code | semmle.label | code | | promisification.js:49:11:49:14 | code | semmle.label | code | | promisification.js:49:18:49:25 | req.body | semmle.label | req.body | | promisification.js:52:21:52:24 | code | semmle.label | code | diff --git a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/promisification.js b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/promisification.js index 5dcefce9896e..56a1028f9241 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/promisification.js +++ b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/promisification.js @@ -34,13 +34,13 @@ app.post('/eval', async (req, res) => { app.post('/eval', async (req, res) => { var garPromisify = require("@gar/promisify"); - const code = req.body; // $ MISSING: Source + const code = req.body; // $ Source const promisifiedExec = garPromisify(cp.exec); - promisifiedExec(code); // $ MISSING: Alert + promisifiedExec(code); // $ Alert const promisifiedCp = garPromisify(cp); - promisifiedCp.exec(code); // $ MISSING: Alert + promisifiedCp.exec(code); // $ Alert }); app.post('/eval', async (req, res) => { From e002f2088fb79831fee8cff07c27d64dcef24ddd Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Mon, 15 Sep 2025 17:04:34 +0200 Subject: [PATCH 06/11] JS: Add modeling for `es6-promisify` --- .../ql/lib/semmle/javascript/Promises.qll | 5 +++-- .../CommandInjection.expected | 21 +++++++++++++++++++ .../CommandInjection/promisification.js | 14 ++++++------- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/Promises.qll b/javascript/ql/lib/semmle/javascript/Promises.qll index 78508cb0b7b4..3a84a4ee7414 100644 --- a/javascript/ql/lib/semmle/javascript/Promises.qll +++ b/javascript/ql/lib/semmle/javascript/Promises.qll @@ -727,7 +727,8 @@ module Promisify { PromisifyAllCall() { this = [ - DataFlow::moduleMember(["bluebird", "@google-cloud/promisify"], "promisifyAll"), + DataFlow::moduleMember(["bluebird", "@google-cloud/promisify", "es6-promisify"], + "promisifyAll"), DataFlow::moduleMember("thenify-all", "withCallback"), DataFlow::moduleImport(["util-promisifyall", "pify", "thenify-all", "@gar/promisify"]) ].getACall() @@ -744,7 +745,7 @@ module Promisify { or this = DataFlow::moduleImport(["pify", "util.promisify"]).getACall() or - this = DataFlow::moduleImport(["thenify", "@gar/promisify"]).getACall() + this = DataFlow::moduleImport(["thenify", "@gar/promisify", "es6-promisify"]).getACall() or this = DataFlow::moduleMember("thenify", "withCallback").getACall() or diff --git a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected index 6fb2234c2c71..9ac31e026a4c 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected @@ -87,6 +87,12 @@ | promisification.js:43:24:43:27 | code | promisification.js:37:18:37:25 | req.body | promisification.js:43:24:43:27 | code | This command line depends on a $@. | promisification.js:37:18:37:25 | req.body | user-provided value | | promisification.js:52:21:52:24 | code | promisification.js:49:18:49:25 | req.body | promisification.js:52:21:52:24 | code | This command line depends on a $@. | promisification.js:49:18:49:25 | req.body | user-provided value | | promisification.js:55:15:55:18 | code | promisification.js:49:18:49:25 | req.body | promisification.js:55:15:55:18 | code | This command line depends on a $@. | promisification.js:49:18:49:25 | req.body | user-provided value | +| promisification.js:65:21:65:23 | cmd | promisification.js:61:15:61:22 | req.body | promisification.js:65:21:65:23 | cmd | This command line depends on a $@. | promisification.js:61:15:61:22 | req.body | user-provided value | +| promisification.js:69:20:69:22 | cmd | promisification.js:61:15:61:22 | req.body | promisification.js:69:20:69:22 | cmd | This command line depends on a $@. | promisification.js:61:15:61:22 | req.body | user-provided value | +| promisification.js:74:26:74:28 | cmd | promisification.js:61:15:61:22 | req.body | promisification.js:74:26:74:28 | cmd | This command line depends on a $@. | promisification.js:61:15:61:22 | req.body | user-provided value | +| promisification.js:77:24:77:26 | cmd | promisification.js:61:15:61:22 | req.body | promisification.js:77:24:77:26 | cmd | This command line depends on a $@. | promisification.js:61:15:61:22 | req.body | user-provided value | +| promisification.js:78:28:78:30 | cmd | promisification.js:61:15:61:22 | req.body | promisification.js:78:28:78:30 | cmd | This command line depends on a $@. | promisification.js:61:15:61:22 | req.body | user-provided value | +| promisification.js:79:25:79:27 | cmd | promisification.js:61:15:61:22 | req.body | promisification.js:79:25:79:27 | cmd | This command line depends on a $@. | promisification.js:61:15:61:22 | req.body | user-provided value | | promisification.js:100:23:100:26 | code | promisification.js:99:18:99:25 | req.body | promisification.js:100:23:100:26 | code | This command line depends on a $@. | promisification.js:99:18:99:25 | req.body | user-provided value | | promisification.js:101:27:101:30 | code | promisification.js:99:18:99:25 | req.body | promisification.js:101:27:101:30 | code | This command line depends on a $@. | promisification.js:99:18:99:25 | req.body | user-provided value | | promisification.js:102:27:102:30 | code | promisification.js:99:18:99:25 | req.body | promisification.js:102:27:102:30 | code | This command line depends on a $@. | promisification.js:99:18:99:25 | req.body | user-provided value | @@ -282,6 +288,13 @@ edges | promisification.js:49:11:49:14 | code | promisification.js:52:21:52:24 | code | provenance | | | promisification.js:49:11:49:14 | code | promisification.js:55:15:55:18 | code | provenance | | | promisification.js:49:18:49:25 | req.body | promisification.js:49:11:49:14 | code | provenance | | +| promisification.js:61:9:61:11 | cmd | promisification.js:65:21:65:23 | cmd | provenance | | +| promisification.js:61:9:61:11 | cmd | promisification.js:69:20:69:22 | cmd | provenance | | +| promisification.js:61:9:61:11 | cmd | promisification.js:74:26:74:28 | cmd | provenance | | +| promisification.js:61:9:61:11 | cmd | promisification.js:77:24:77:26 | cmd | provenance | | +| promisification.js:61:9:61:11 | cmd | promisification.js:78:28:78:30 | cmd | provenance | | +| promisification.js:61:9:61:11 | cmd | promisification.js:79:25:79:27 | cmd | provenance | | +| promisification.js:61:15:61:22 | req.body | promisification.js:61:9:61:11 | cmd | provenance | | | promisification.js:99:11:99:14 | code | promisification.js:100:23:100:26 | code | provenance | | | promisification.js:99:11:99:14 | code | promisification.js:101:27:101:30 | code | provenance | | | promisification.js:99:11:99:14 | code | promisification.js:102:27:102:30 | code | provenance | | @@ -492,6 +505,14 @@ nodes | promisification.js:49:18:49:25 | req.body | semmle.label | req.body | | promisification.js:52:21:52:24 | code | semmle.label | code | | promisification.js:55:15:55:18 | code | semmle.label | code | +| promisification.js:61:9:61:11 | cmd | semmle.label | cmd | +| promisification.js:61:15:61:22 | req.body | semmle.label | req.body | +| promisification.js:65:21:65:23 | cmd | semmle.label | cmd | +| promisification.js:69:20:69:22 | cmd | semmle.label | cmd | +| promisification.js:74:26:74:28 | cmd | semmle.label | cmd | +| promisification.js:77:24:77:26 | cmd | semmle.label | cmd | +| promisification.js:78:28:78:30 | cmd | semmle.label | cmd | +| promisification.js:79:25:79:27 | cmd | semmle.label | cmd | | promisification.js:99:11:99:14 | code | semmle.label | code | | promisification.js:99:18:99:25 | req.body | semmle.label | req.body | | promisification.js:100:23:100:26 | code | semmle.label | code | diff --git a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/promisification.js b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/promisification.js index 56a1028f9241..9cb634808cb7 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/promisification.js +++ b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/promisification.js @@ -58,25 +58,25 @@ app.post('/eval', async (req, res) => { app.post('/eval', async (req, res) => { const es6Promisify = require("es6-promisify"); - let cmd = req.body; // $ MISSING: Source + let cmd = req.body; // $ Source // Test basic promisification const promisifiedExec = es6Promisify(cp.exec); - promisifiedExec(cmd); // $ MISSING: Alert + promisifiedExec(cmd); // $ Alert // Test with method binding const execBoundAsync = es6Promisify(cp.exec.bind(cp)); - execBoundAsync(cmd); // $ MISSING: Alert + execBoundAsync(cmd); // $ Alert const promisifiedExecMulti = es6Promisify(cp.exec, { multiArgs: true }); - promisifiedExecMulti(cmd); // $ MISSING: Alert + promisifiedExecMulti(cmd); // $ Alert const promisifiedCp = es6Promisify.promisifyAll(cp); - promisifiedCp.exec(cmd); // $ MISSING: Alert - promisifiedCp.execFile(cmd); // $ MISSING: Alert - promisifiedCp.spawn(cmd); // $ MISSING: Alert + promisifiedCp.exec(cmd); // $ Alert + promisifiedCp.execFile(cmd); // $ Alert + promisifiedCp.spawn(cmd); // $ Alert const lambda = es6Promisify((code, callback) => { try { From 2c6db00cbc8a08e707f0dc54fe0095bea39b997e Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Mon, 15 Sep 2025 17:09:28 +0200 Subject: [PATCH 07/11] JS: Add modeling for util promisify* --- javascript/ql/lib/semmle/javascript/Promises.qll | 6 ++++-- .../CommandInjection/CommandInjection.expected | 15 +++++++++++++++ .../CWE-078/CommandInjection/promisification.js | 10 +++++----- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/Promises.qll b/javascript/ql/lib/semmle/javascript/Promises.qll index 3a84a4ee7414..2feb92e2e553 100644 --- a/javascript/ql/lib/semmle/javascript/Promises.qll +++ b/javascript/ql/lib/semmle/javascript/Promises.qll @@ -730,7 +730,9 @@ module Promisify { DataFlow::moduleMember(["bluebird", "@google-cloud/promisify", "es6-promisify"], "promisifyAll"), DataFlow::moduleMember("thenify-all", "withCallback"), - DataFlow::moduleImport(["util-promisifyall", "pify", "thenify-all", "@gar/promisify"]) + DataFlow::moduleImport([ + "util-promisifyall", "pify", "thenify-all", "@gar/promisify", "util.promisify-all" + ]) ].getACall() } } @@ -743,7 +745,7 @@ module Promisify { PromisifyCall() { this = DataFlow::moduleImport(["util", "bluebird"]).getAMemberCall("promisify") or - this = DataFlow::moduleImport(["pify", "util.promisify"]).getACall() + this = DataFlow::moduleImport(["pify", "util.promisify", "util-promisify"]).getACall() or this = DataFlow::moduleImport(["thenify", "@gar/promisify", "es6-promisify"]).getACall() or diff --git a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected index 9ac31e026a4c..83e06f5a8204 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected @@ -83,6 +83,7 @@ | other.js:30:33:30:35 | cmd | other.js:5:25:5:31 | req.url | other.js:30:33:30:35 | cmd | This command line depends on a $@. | other.js:5:25:5:31 | req.url | user-provided value | | other.js:34:44:34:46 | cmd | other.js:5:25:5:31 | req.url | other.js:34:44:34:46 | cmd | This command line depends on a $@. | other.js:5:25:5:31 | req.url | user-provided value | | promisification.js:24:22:24:25 | code | promisification.js:21:18:21:25 | req.body | promisification.js:24:22:24:25 | code | This command line depends on a $@. | promisification.js:21:18:21:25 | req.body | user-provided value | +| promisification.js:31:24:31:27 | code | promisification.js:30:18:30:25 | req.body | promisification.js:31:24:31:27 | code | This command line depends on a $@. | promisification.js:30:18:30:25 | req.body | user-provided value | | promisification.js:40:21:40:24 | code | promisification.js:37:18:37:25 | req.body | promisification.js:40:21:40:24 | code | This command line depends on a $@. | promisification.js:37:18:37:25 | req.body | user-provided value | | promisification.js:43:24:43:27 | code | promisification.js:37:18:37:25 | req.body | promisification.js:43:24:43:27 | code | This command line depends on a $@. | promisification.js:37:18:37:25 | req.body | user-provided value | | promisification.js:52:21:52:24 | code | promisification.js:49:18:49:25 | req.body | promisification.js:52:21:52:24 | code | This command line depends on a $@. | promisification.js:49:18:49:25 | req.body | user-provided value | @@ -98,6 +99,8 @@ | promisification.js:102:27:102:30 | code | promisification.js:99:18:99:25 | req.body | promisification.js:102:27:102:30 | code | This command line depends on a $@. | promisification.js:99:18:99:25 | req.body | user-provided value | | promisification.js:106:24:106:27 | code | promisification.js:99:18:99:25 | req.body | promisification.js:106:24:106:27 | code | This command line depends on a $@. | promisification.js:99:18:99:25 | req.body | user-provided value | | promisification.js:109:24:109:27 | code | promisification.js:99:18:99:25 | req.body | promisification.js:109:24:109:27 | code | This command line depends on a $@. | promisification.js:99:18:99:25 | req.body | user-provided value | +| promisification.js:133:21:133:24 | code | promisification.js:130:18:130:25 | req.body | promisification.js:133:21:133:24 | code | This command line depends on a $@. | promisification.js:130:18:130:25 | req.body | user-provided value | +| promisification.js:136:15:136:18 | code | promisification.js:130:18:130:25 | req.body | promisification.js:136:15:136:18 | code | This command line depends on a $@. | promisification.js:130:18:130:25 | req.body | user-provided value | | promisification.js:144:21:144:24 | code | promisification.js:141:18:141:25 | req.body | promisification.js:144:21:144:24 | code | This command line depends on a $@. | promisification.js:141:18:141:25 | req.body | user-provided value | | promisification.js:147:15:147:18 | code | promisification.js:141:18:141:25 | req.body | promisification.js:147:15:147:18 | code | This command line depends on a $@. | promisification.js:141:18:141:25 | req.body | user-provided value | | promisification.js:150:24:150:27 | code | promisification.js:141:18:141:25 | req.body | promisification.js:150:24:150:27 | code | This command line depends on a $@. | promisification.js:141:18:141:25 | req.body | user-provided value | @@ -282,6 +285,8 @@ edges | other.js:5:25:5:31 | req.url | other.js:5:15:5:38 | url.par ... , true) | provenance | | | promisification.js:21:11:21:14 | code | promisification.js:24:22:24:25 | code | provenance | | | promisification.js:21:18:21:25 | req.body | promisification.js:21:11:21:14 | code | provenance | | +| promisification.js:30:11:30:14 | code | promisification.js:31:24:31:27 | code | provenance | | +| promisification.js:30:18:30:25 | req.body | promisification.js:30:11:30:14 | code | provenance | | | promisification.js:37:11:37:14 | code | promisification.js:40:21:40:24 | code | provenance | | | promisification.js:37:11:37:14 | code | promisification.js:43:24:43:27 | code | provenance | | | promisification.js:37:18:37:25 | req.body | promisification.js:37:11:37:14 | code | provenance | | @@ -301,6 +306,9 @@ edges | promisification.js:99:11:99:14 | code | promisification.js:106:24:106:27 | code | provenance | | | promisification.js:99:11:99:14 | code | promisification.js:109:24:109:27 | code | provenance | | | promisification.js:99:18:99:25 | req.body | promisification.js:99:11:99:14 | code | provenance | | +| promisification.js:130:11:130:14 | code | promisification.js:133:21:133:24 | code | provenance | | +| promisification.js:130:11:130:14 | code | promisification.js:136:15:136:18 | code | provenance | | +| promisification.js:130:18:130:25 | req.body | promisification.js:130:11:130:14 | code | provenance | | | promisification.js:141:11:141:14 | code | promisification.js:144:21:144:24 | code | provenance | | | promisification.js:141:11:141:14 | code | promisification.js:147:15:147:18 | code | provenance | | | promisification.js:141:11:141:14 | code | promisification.js:150:24:150:27 | code | provenance | | @@ -497,6 +505,9 @@ nodes | promisification.js:21:11:21:14 | code | semmle.label | code | | promisification.js:21:18:21:25 | req.body | semmle.label | req.body | | promisification.js:24:22:24:25 | code | semmle.label | code | +| promisification.js:30:11:30:14 | code | semmle.label | code | +| promisification.js:30:18:30:25 | req.body | semmle.label | req.body | +| promisification.js:31:24:31:27 | code | semmle.label | code | | promisification.js:37:11:37:14 | code | semmle.label | code | | promisification.js:37:18:37:25 | req.body | semmle.label | req.body | | promisification.js:40:21:40:24 | code | semmle.label | code | @@ -520,6 +531,10 @@ nodes | promisification.js:102:27:102:30 | code | semmle.label | code | | promisification.js:106:24:106:27 | code | semmle.label | code | | promisification.js:109:24:109:27 | code | semmle.label | code | +| promisification.js:130:11:130:14 | code | semmle.label | code | +| promisification.js:130:18:130:25 | req.body | semmle.label | req.body | +| promisification.js:133:21:133:24 | code | semmle.label | code | +| promisification.js:136:15:136:18 | code | semmle.label | code | | promisification.js:141:11:141:14 | code | semmle.label | code | | promisification.js:141:18:141:25 | req.body | semmle.label | req.body | | promisification.js:144:21:144:24 | code | semmle.label | code | diff --git a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/promisification.js b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/promisification.js index 9cb634808cb7..d12a167c5b09 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/promisification.js +++ b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/promisification.js @@ -27,8 +27,8 @@ app.post('/eval', async (req, res) => { app.post('/eval', async (req, res) => { const promisify2 = require('util.promisify-all'); const promisifiedCp = promisify2(cp); - const code = req.body; // $ MISSING: Source - promisifiedCp.exec(code); // $ MISSING: Alert + const code = req.body; // $ Source + promisifiedCp.exec(code); // $ Alert }); @@ -127,13 +127,13 @@ app.post('/eval', async (req, res) => { app.post('/eval', async (req, res) => { const utilPromisify = require('util-promisify'); - const code = req.body; // $ MISSING: Source + const code = req.body; // $ Source const promisifiedExec = utilPromisify(cp.exec); - promisifiedExec(code); // $ MISSING: Alert + promisifiedExec(code); // $ Alert const execAsync = utilPromisify(cp.exec.bind(cp)); - execAsync(code); // $ MISSING: Alert + execAsync(code); // $ Alert }); app.post('/eval', async (req, res) => { From 0d23ab07db3afe5ab58184d551f069ea79174485 Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Mon, 15 Sep 2025 17:13:13 +0200 Subject: [PATCH 08/11] JS: Add data flow modeling for promisified user-defined functions --- .../semmle/javascript/dataflow/DataFlow.qll | 1 + .../javascript/dataflow/PromisifyFlow.qll | 29 +++++++++++++++++++ .../CommandInjection.expected | 19 ++++++++++++ .../CommandInjection/promisification.js | 6 ++-- 4 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 javascript/ql/lib/semmle/javascript/dataflow/PromisifyFlow.qll diff --git a/javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll b/javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll index edc591ceff46..071d6d084333 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll @@ -1912,6 +1912,7 @@ module DataFlow { deprecated import Configuration import TypeTracking import AdditionalFlowSteps + import PromisifyFlow import internal.FunctionWrapperSteps import internal.sharedlib.DataFlow import internal.BarrierGuards diff --git a/javascript/ql/lib/semmle/javascript/dataflow/PromisifyFlow.qll b/javascript/ql/lib/semmle/javascript/dataflow/PromisifyFlow.qll new file mode 100644 index 000000000000..4389a3d031a6 --- /dev/null +++ b/javascript/ql/lib/semmle/javascript/dataflow/PromisifyFlow.qll @@ -0,0 +1,29 @@ +/** + * Provides data flow steps for promisified user-defined function calls. + * This ensures that when you call a promisified user-defined function, + * arguments flow to the original function's parameters. + */ + +private import javascript +private import semmle.javascript.dataflow.AdditionalFlowSteps + +/** + * A data flow step from arguments of promisified user-defined function calls to + * the parameters of the original function. + */ +class PromisifiedUserFunctionArgumentFlow extends AdditionalFlowStep { + override predicate step(DataFlow::Node pred, DataFlow::Node succ) { + exists( + DataFlow::CallNode promisifiedCall, Promisify::PromisifyCall promisify, + DataFlow::FunctionNode originalFunc, int i + | + // The promisified call flows from a promisify result + promisify.flowsTo(promisifiedCall.getCalleeNode()) and + // The original function was promisified + originalFunc.flowsTo(promisify.getArgument(0)) and + // Argument i of the promisified call flows to parameter i of the original function + pred = promisifiedCall.getArgument(i) and + succ = originalFunc.getParameter(i) + ) + } +} diff --git a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected index 83e06f5a8204..50e6765d6218 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected @@ -82,6 +82,7 @@ | other.js:28:27:28:29 | cmd | other.js:5:25:5:31 | req.url | other.js:28:27:28:29 | cmd | This command line depends on a $@. | other.js:5:25:5:31 | req.url | user-provided value | | other.js:30:33:30:35 | cmd | other.js:5:25:5:31 | req.url | other.js:30:33:30:35 | cmd | This command line depends on a $@. | other.js:5:25:5:31 | req.url | user-provided value | | other.js:34:44:34:46 | cmd | other.js:5:25:5:31 | req.url | other.js:34:44:34:46 | cmd | This command line depends on a $@. | other.js:5:25:5:31 | req.url | user-provided value | +| promisification.js:9:13:9:21 | code.code | promisification.js:15:18:15:25 | req.body | promisification.js:9:13:9:21 | code.code | This command line depends on a $@. | promisification.js:15:18:15:25 | req.body | user-provided value | | promisification.js:24:22:24:25 | code | promisification.js:21:18:21:25 | req.body | promisification.js:24:22:24:25 | code | This command line depends on a $@. | promisification.js:21:18:21:25 | req.body | user-provided value | | promisification.js:31:24:31:27 | code | promisification.js:30:18:30:25 | req.body | promisification.js:31:24:31:27 | code | This command line depends on a $@. | promisification.js:30:18:30:25 | req.body | user-provided value | | promisification.js:40:21:40:24 | code | promisification.js:37:18:37:25 | req.body | promisification.js:40:21:40:24 | code | This command line depends on a $@. | promisification.js:37:18:37:25 | req.body | user-provided value | @@ -94,6 +95,7 @@ | promisification.js:77:24:77:26 | cmd | promisification.js:61:15:61:22 | req.body | promisification.js:77:24:77:26 | cmd | This command line depends on a $@. | promisification.js:61:15:61:22 | req.body | user-provided value | | promisification.js:78:28:78:30 | cmd | promisification.js:61:15:61:22 | req.body | promisification.js:78:28:78:30 | cmd | This command line depends on a $@. | promisification.js:61:15:61:22 | req.body | user-provided value | | promisification.js:79:25:79:27 | cmd | promisification.js:61:15:61:22 | req.body | promisification.js:79:25:79:27 | cmd | This command line depends on a $@. | promisification.js:61:15:61:22 | req.body | user-provided value | +| promisification.js:83:36:83:39 | code | promisification.js:61:15:61:22 | req.body | promisification.js:83:36:83:39 | code | This command line depends on a $@. | promisification.js:61:15:61:22 | req.body | user-provided value | | promisification.js:100:23:100:26 | code | promisification.js:99:18:99:25 | req.body | promisification.js:100:23:100:26 | code | This command line depends on a $@. | promisification.js:99:18:99:25 | req.body | user-provided value | | promisification.js:101:27:101:30 | code | promisification.js:99:18:99:25 | req.body | promisification.js:101:27:101:30 | code | This command line depends on a $@. | promisification.js:99:18:99:25 | req.body | user-provided value | | promisification.js:102:27:102:30 | code | promisification.js:99:18:99:25 | req.body | promisification.js:102:27:102:30 | code | This command line depends on a $@. | promisification.js:99:18:99:25 | req.body | user-provided value | @@ -283,6 +285,11 @@ edges | other.js:5:9:5:11 | cmd | other.js:34:44:34:46 | cmd | provenance | | | other.js:5:15:5:38 | url.par ... , true) | other.js:5:9:5:11 | cmd | provenance | | | other.js:5:25:5:31 | req.url | other.js:5:15:5:38 | url.par ... , true) | provenance | | +| promisification.js:8:21:8:24 | code | promisification.js:9:13:9:16 | code | provenance | | +| promisification.js:9:13:9:16 | code | promisification.js:9:13:9:21 | code.code | provenance | | +| promisification.js:15:11:15:14 | code | promisification.js:16:15:16:18 | code | provenance | | +| promisification.js:15:18:15:25 | req.body | promisification.js:15:11:15:14 | code | provenance | | +| promisification.js:16:15:16:18 | code | promisification.js:8:21:8:24 | code | provenance | | | promisification.js:21:11:21:14 | code | promisification.js:24:22:24:25 | code | provenance | | | promisification.js:21:18:21:25 | req.body | promisification.js:21:11:21:14 | code | provenance | | | promisification.js:30:11:30:14 | code | promisification.js:31:24:31:27 | code | provenance | | @@ -299,7 +306,10 @@ edges | promisification.js:61:9:61:11 | cmd | promisification.js:77:24:77:26 | cmd | provenance | | | promisification.js:61:9:61:11 | cmd | promisification.js:78:28:78:30 | cmd | provenance | | | promisification.js:61:9:61:11 | cmd | promisification.js:79:25:79:27 | cmd | provenance | | +| promisification.js:61:9:61:11 | cmd | promisification.js:89:12:89:14 | cmd | provenance | | | promisification.js:61:15:61:22 | req.body | promisification.js:61:9:61:11 | cmd | provenance | | +| promisification.js:81:34:81:37 | code | promisification.js:83:36:83:39 | code | provenance | | +| promisification.js:89:12:89:14 | cmd | promisification.js:81:34:81:37 | code | provenance | | | promisification.js:99:11:99:14 | code | promisification.js:100:23:100:26 | code | provenance | | | promisification.js:99:11:99:14 | code | promisification.js:101:27:101:30 | code | provenance | | | promisification.js:99:11:99:14 | code | promisification.js:102:27:102:30 | code | provenance | | @@ -502,6 +512,12 @@ nodes | other.js:28:27:28:29 | cmd | semmle.label | cmd | | other.js:30:33:30:35 | cmd | semmle.label | cmd | | other.js:34:44:34:46 | cmd | semmle.label | cmd | +| promisification.js:8:21:8:24 | code | semmle.label | code | +| promisification.js:9:13:9:16 | code | semmle.label | code | +| promisification.js:9:13:9:21 | code.code | semmle.label | code.code | +| promisification.js:15:11:15:14 | code | semmle.label | code | +| promisification.js:15:18:15:25 | req.body | semmle.label | req.body | +| promisification.js:16:15:16:18 | code | semmle.label | code | | promisification.js:21:11:21:14 | code | semmle.label | code | | promisification.js:21:18:21:25 | req.body | semmle.label | req.body | | promisification.js:24:22:24:25 | code | semmle.label | code | @@ -524,6 +540,9 @@ nodes | promisification.js:77:24:77:26 | cmd | semmle.label | cmd | | promisification.js:78:28:78:30 | cmd | semmle.label | cmd | | promisification.js:79:25:79:27 | cmd | semmle.label | cmd | +| promisification.js:81:34:81:37 | code | semmle.label | code | +| promisification.js:83:36:83:39 | code | semmle.label | code | +| promisification.js:89:12:89:14 | cmd | semmle.label | cmd | | promisification.js:99:11:99:14 | code | semmle.label | code | | promisification.js:99:18:99:25 | req.body | semmle.label | req.body | | promisification.js:100:23:100:26 | code | semmle.label | code | diff --git a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/promisification.js b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/promisification.js index d12a167c5b09..79051f1672ec 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/promisification.js +++ b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/promisification.js @@ -6,13 +6,13 @@ const app = express(); app.use(bodyParser.json()); function legacyEval(code) { - cp.exec(code.code); // $ MISSING: Alert + cp.exec(code.code); // $ Alert } app.post('/eval', async (req, res) => { const { promisify } = require('util'); const evalAsync = promisify(legacyEval); - const code = req.body; // $ MISSING: Source + const code = req.body; // $ Source evalAsync(code); }); @@ -80,7 +80,7 @@ app.post('/eval', async (req, res) => { const lambda = es6Promisify((code, callback) => { try { - const result = cp.exec(code); // $ MISSING: Alert + const result = cp.exec(code); // $ Alert callback(null, result); } catch (err) { callback(err); From 3a75500f54bde24b64146bd141bd83e8f3f49c92 Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Mon, 15 Sep 2025 17:15:31 +0200 Subject: [PATCH 09/11] JS: Add modeling for `call-me-maybe` --- javascript/ql/lib/ext/call-me-maybe.model.yml | 6 ++++ .../CommandInjection.expected | 30 +++++++++++++++++++ .../CommandInjection/promisification.js | 4 +-- 3 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 javascript/ql/lib/ext/call-me-maybe.model.yml diff --git a/javascript/ql/lib/ext/call-me-maybe.model.yml b/javascript/ql/lib/ext/call-me-maybe.model.yml new file mode 100644 index 000000000000..ceda3e8b0922 --- /dev/null +++ b/javascript/ql/lib/ext/call-me-maybe.model.yml @@ -0,0 +1,6 @@ +extensions: + - addsTo: + pack: codeql/javascript-all + extensible: summaryModel + data: + - ["call-me-maybe", "", "Argument[1]", "ReturnValue", "value"] diff --git a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected index 50e6765d6218..f1d547bdfb1c 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected @@ -101,6 +101,7 @@ | promisification.js:102:27:102:30 | code | promisification.js:99:18:99:25 | req.body | promisification.js:102:27:102:30 | code | This command line depends on a $@. | promisification.js:99:18:99:25 | req.body | user-provided value | | promisification.js:106:24:106:27 | code | promisification.js:99:18:99:25 | req.body | promisification.js:106:24:106:27 | code | This command line depends on a $@. | promisification.js:99:18:99:25 | req.body | user-provided value | | promisification.js:109:24:109:27 | code | promisification.js:99:18:99:25 | req.body | promisification.js:109:24:109:27 | code | This command line depends on a $@. | promisification.js:99:18:99:25 | req.body | user-provided value | +| promisification.js:124:17:124:19 | cmd | promisification.js:114:18:114:25 | req.body | promisification.js:124:17:124:19 | cmd | This command line depends on a $@. | promisification.js:114:18:114:25 | req.body | user-provided value | | promisification.js:133:21:133:24 | code | promisification.js:130:18:130:25 | req.body | promisification.js:133:21:133:24 | code | This command line depends on a $@. | promisification.js:130:18:130:25 | req.body | user-provided value | | promisification.js:136:15:136:18 | code | promisification.js:130:18:130:25 | req.body | promisification.js:136:15:136:18 | code | This command line depends on a $@. | promisification.js:130:18:130:25 | req.body | user-provided value | | promisification.js:144:21:144:24 | code | promisification.js:141:18:141:25 | req.body | promisification.js:144:21:144:24 | code | This command line depends on a $@. | promisification.js:141:18:141:25 | req.body | user-provided value | @@ -316,6 +317,19 @@ edges | promisification.js:99:11:99:14 | code | promisification.js:106:24:106:27 | code | provenance | | | promisification.js:99:11:99:14 | code | promisification.js:109:24:109:27 | code | provenance | | | promisification.js:99:18:99:25 | req.body | promisification.js:99:11:99:14 | code | provenance | | +| promisification.js:114:11:114:14 | code | promisification.js:122:42:122:45 | code | provenance | | +| promisification.js:114:18:114:25 | req.body | promisification.js:114:11:114:14 | code | provenance | | +| promisification.js:116:32:116:34 | cmd | promisification.js:117:16:119:10 | new Pro ... }) [PromiseValue] | provenance | | +| promisification.js:116:32:116:34 | cmd | promisification.js:118:21:118:23 | cmd | provenance | | +| promisification.js:118:13:118:19 | [post update] resolve [resolve-value] | promisification.js:117:29:117:35 | resolve [Return] [resolve-value] | provenance | | +| promisification.js:118:21:118:23 | cmd | promisification.js:118:13:118:19 | [post update] resolve [resolve-value] | provenance | | +| promisification.js:122:11:122:20 | cmdPromise [PromiseValue] | promisification.js:123:17:123:26 | cmdPromise [PromiseValue] | provenance | | +| promisification.js:122:24:122:46 | createE ... e(code) [PromiseValue] | promisification.js:122:11:122:20 | cmdPromise [PromiseValue] | provenance | | +| promisification.js:122:42:122:45 | code | promisification.js:116:32:116:34 | cmd | provenance | | +| promisification.js:122:42:122:45 | code | promisification.js:122:24:122:46 | createE ... e(code) [PromiseValue] | provenance | | +| promisification.js:123:5:123:27 | maybe(n ... romise) [PromiseValue] | promisification.js:123:34:123:36 | cmd | provenance | | +| promisification.js:123:17:123:26 | cmdPromise [PromiseValue] | promisification.js:123:5:123:27 | maybe(n ... romise) [PromiseValue] | provenance | | +| promisification.js:123:34:123:36 | cmd | promisification.js:124:17:124:19 | cmd | provenance | | | promisification.js:130:11:130:14 | code | promisification.js:133:21:133:24 | code | provenance | | | promisification.js:130:11:130:14 | code | promisification.js:136:15:136:18 | code | provenance | | | promisification.js:130:18:130:25 | req.body | promisification.js:130:11:130:14 | code | provenance | | @@ -550,6 +564,20 @@ nodes | promisification.js:102:27:102:30 | code | semmle.label | code | | promisification.js:106:24:106:27 | code | semmle.label | code | | promisification.js:109:24:109:27 | code | semmle.label | code | +| promisification.js:114:11:114:14 | code | semmle.label | code | +| promisification.js:114:18:114:25 | req.body | semmle.label | req.body | +| promisification.js:116:32:116:34 | cmd | semmle.label | cmd | +| promisification.js:117:16:119:10 | new Pro ... }) [PromiseValue] | semmle.label | new Pro ... }) [PromiseValue] | +| promisification.js:117:29:117:35 | resolve [Return] [resolve-value] | semmle.label | resolve [Return] [resolve-value] | +| promisification.js:118:13:118:19 | [post update] resolve [resolve-value] | semmle.label | [post update] resolve [resolve-value] | +| promisification.js:118:21:118:23 | cmd | semmle.label | cmd | +| promisification.js:122:11:122:20 | cmdPromise [PromiseValue] | semmle.label | cmdPromise [PromiseValue] | +| promisification.js:122:24:122:46 | createE ... e(code) [PromiseValue] | semmle.label | createE ... e(code) [PromiseValue] | +| promisification.js:122:42:122:45 | code | semmle.label | code | +| promisification.js:123:5:123:27 | maybe(n ... romise) [PromiseValue] | semmle.label | maybe(n ... romise) [PromiseValue] | +| promisification.js:123:17:123:26 | cmdPromise [PromiseValue] | semmle.label | cmdPromise [PromiseValue] | +| promisification.js:123:34:123:36 | cmd | semmle.label | cmd | +| promisification.js:124:17:124:19 | cmd | semmle.label | cmd | | promisification.js:130:11:130:14 | code | semmle.label | code | | promisification.js:130:18:130:25 | req.body | semmle.label | req.body | | promisification.js:133:21:133:24 | code | semmle.label | code | @@ -564,3 +592,5 @@ nodes | third-party-command-injection.js:5:20:5:26 | command | semmle.label | command | | third-party-command-injection.js:6:21:6:27 | command | semmle.label | command | subpaths +| promisification.js:116:32:116:34 | cmd | promisification.js:118:21:118:23 | cmd | promisification.js:117:29:117:35 | resolve [Return] [resolve-value] | promisification.js:117:16:119:10 | new Pro ... }) [PromiseValue] | +| promisification.js:122:42:122:45 | code | promisification.js:116:32:116:34 | cmd | promisification.js:117:16:119:10 | new Pro ... }) [PromiseValue] | promisification.js:122:24:122:46 | createE ... e(code) [PromiseValue] | diff --git a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/promisification.js b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/promisification.js index 79051f1672ec..b062c8b7ee2b 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/promisification.js +++ b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/promisification.js @@ -111,7 +111,7 @@ app.post('/eval', async (req, res) => { app.post('/eval', async (req, res) => { const maybe = require('call-me-maybe'); - const code = req.body; // $ MISSING: Source + const code = req.body; // $ Source function createExecPromise(cmd) { return new Promise((resolve) => { @@ -121,7 +121,7 @@ app.post('/eval', async (req, res) => { const cmdPromise = createExecPromise(code); maybe(null, cmdPromise).then(cmd => { - cp.exec(cmd); // $ MISSING: Alert + cp.exec(cmd); // $ Alert }); }); From 278a1efb4bfbe33c505e450deb06c741cdc7eba1 Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Mon, 15 Sep 2025 18:21:45 +0200 Subject: [PATCH 10/11] JS: Add change note --- .../ql/lib/change-notes/2025-09-15-promisifications.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 javascript/ql/lib/change-notes/2025-09-15-promisifications.md diff --git a/javascript/ql/lib/change-notes/2025-09-15-promisifications.md b/javascript/ql/lib/change-notes/2025-09-15-promisifications.md new file mode 100644 index 000000000000..8864ab23a022 --- /dev/null +++ b/javascript/ql/lib/change-notes/2025-09-15-promisifications.md @@ -0,0 +1,5 @@ +--- +category: minorAnalysis +--- +* Added modeling for promisification libraries `@gar/promisify`, `es6-promisify`, `util.promisify`, `thenify-all`, `call-me-maybe`, `@google-cloud/promisify`, and `util-promisify`. +* Data flow is now tracked through promisified user-defined functions. From 49ccb8ce2bf610885180d56dd757d9c3fa50bc92 Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Tue, 16 Sep 2025 13:13:15 +0200 Subject: [PATCH 11/11] JS: Simplify exist clause to use `Promisify::PromisifyAllCall` instead of `DataFlow::SourceNode` --- javascript/ql/lib/semmle/javascript/ApiGraphs.qll | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll index 6b2cc3af235e..833f309021ba 100644 --- a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll +++ b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll @@ -1117,10 +1117,10 @@ module API { or // Handle promisified object member access: promisify(obj).member should be treated as obj.member (promisified) exists( - DataFlow::SourceNode promisifiedObj, DataFlow::SourceNode originalObj, string member + Promisify::PromisifyAllCall promisifiedObj, DataFlow::SourceNode originalObj, + string member | - promisifiedObj instanceof Promisify::PromisifyAllCall and - originalObj.flowsTo(promisifiedObj.(Promisify::PromisifyAllCall).getArgument(0)) and + originalObj.flowsTo(promisifiedObj.getArgument(0)) and use(base, originalObj) and lbl = Label::member(member) and ref = promisifiedObj.getAPropertyRead(member)