From e8eb9be3f603576da02c6b57551c8cad1557b1eb Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Fri, 1 Aug 2025 11:02:59 +0200 Subject: [PATCH 1/7] Add command injection tests for CLI argument parsing libraries --- .../CommandInjection/command-line-libs.js | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/command-line-libs.js diff --git a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/command-line-libs.js b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/command-line-libs.js new file mode 100644 index 000000000000..c5621354d91d --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/command-line-libs.js @@ -0,0 +1,41 @@ +import express from 'express'; +import { Command } from 'commander'; +import { exec } from 'child_process'; +import arg from 'arg'; +const app = express(); +app.use(express.json()); + +app.post('/Command', (req, res) => { + const args = req.body.args || []; // $ MISSING: Source + const program = new Command(); + program.option('--cmd ', 'Command to execute'); + program.parse(args, { from: 'user' }); + const options = program.opts(); + exec(options.cmd); // $ MISSING: Alert +}); + +app.post('/arg', (req, res) => { + const argsArray = req.body.args || []; // $ MISSING: Source + const parsed = arg({ '--cmd': String }, { argv: argsArray }); + exec(parsed['--cmd']); // $ MISSING: Alert +}); + +app.post('/commandLineArgs', (req, res) => { + const commandLineArgs = require('command-line-args'); + const optionDefinitions = [{ name: 'cmd', type: String }]; + const options = commandLineArgs(optionDefinitions, { argv: req.body.args || [] }); // $ MISSING: Source + if (!options.cmd) return res.status(400).send({ error: 'Missing --cmd' }); + exec(options.cmd); // $ MISSING: Alert +}); + +app.post('/yargs', (req, res) => { + const yargs = require('yargs/yargs'); + const args = req.body.args || []; // $ MISSING: Source + const parsed = yargs(args).option('cmd', { + type: 'string', + describe: 'Command to execute', + demandOption: true + }).parse(); + + exec(parsed.cmd); // $ MISSING: Alert +}); From e980798eded71a6a8703ba7e4d164deb9e780ff2 Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Fri, 1 Aug 2025 12:01:30 +0200 Subject: [PATCH 2/7] Added step through yargs/yargs constructor and chained methods. --- .../frameworks/CommandLineArguments.qll | 13 +++++++++++-- .../CommandInjection/CommandInjection.expected | 18 ++++++++++++++++++ .../CommandInjection/command-line-libs.js | 4 ++-- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/CommandLineArguments.qll b/javascript/ql/lib/semmle/javascript/frameworks/CommandLineArguments.qll index 50beb04b8879..8fff526b2bdb 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/CommandLineArguments.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/CommandLineArguments.qll @@ -87,11 +87,18 @@ private class ArgsParseStep extends TaintTracking::SharedTaintStep { override predicate step(DataFlow::Node pred, DataFlow::Node succ) { exists(DataFlow::CallNode call | call = DataFlow::moduleMember("args", "parse").getACall() or - call = DataFlow::moduleImport(["yargs-parser", "minimist", "subarg"]).getACall() + call = + DataFlow::moduleImport(["yargs-parser", "minimist", "subarg", "yargs/yargs", "yargs"]) + .getACall() | succ = call and pred = call.getArgument(0) ) + or + exists(DataFlow::MethodCallNode methodCall | methodCall = yargs() | + pred = methodCall.getReceiver() and + succ = methodCall + ) } } @@ -115,7 +122,9 @@ private API::Node commander() { * Either directly imported as a module, or through some chained method call. */ private DataFlow::SourceNode yargs() { - result = DataFlow::moduleImport("yargs") + result = DataFlow::moduleImport(["yargs", "yargs/yargs"]) + or + result = DataFlow::moduleImport(["yargs", "yargs/yargs"]).getACall() or // script used to generate list of chained methods: https://gist.github.com/erik-krogh/f8afe952c0577f4b563a993e613269ba exists(string method | 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 22394ec4cb89..4c21013e8f05 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 @@ -21,6 +21,7 @@ | child_process-test.js:75:29:75:31 | cmd | child_process-test.js:73:25:73:31 | req.url | child_process-test.js:75:29:75:31 | cmd | This command line depends on a $@. | child_process-test.js:73:25:73:31 | req.url | user-provided value | | child_process-test.js:83:19:83:36 | req.query.fileName | child_process-test.js:83:19:83:36 | req.query.fileName | child_process-test.js:83:19:83:36 | req.query.fileName | This command line depends on a $@. | child_process-test.js:83:19:83:36 | req.query.fileName | user-provided value | | child_process-test.js:94:11:94:35 | "ping " ... ms.host | child_process-test.js:94:21:94:30 | ctx.params | child_process-test.js:94:11:94:35 | "ping " ... ms.host | This command line depends on a $@. | child_process-test.js:94:21:94:30 | ctx.params | user-provided value | +| command-line-libs.js:40:8:40:17 | parsed.cmd | command-line-libs.js:33:16:33:23 | req.body | command-line-libs.js:40:8:40:17 | parsed.cmd | This command line depends on a $@. | command-line-libs.js:33:16:33:23 | req.body | user-provided value | | exec-sh2.js:10:12:10:57 | cp.spaw ... ptions) | exec-sh2.js:14:25:14:31 | req.url | exec-sh2.js:10:40:10:46 | command | This command line depends on a $@. | exec-sh2.js:14:25:14:31 | req.url | user-provided value | | exec-sh.js:15:12:15:61 | cp.spaw ... ptions) | exec-sh.js:19:25:19:31 | req.url | exec-sh.js:15:44:15:50 | command | This command line depends on a $@. | exec-sh.js:19:25:19:31 | req.url | user-provided value | | execSeries.js:14:41:14:47 | command | execSeries.js:18:34:18:40 | req.url | execSeries.js:14:41:14:47 | command | This command line depends on a $@. | execSeries.js:18:34:18:40 | req.url | user-provided value | @@ -116,6 +117,14 @@ edges | child_process-test.js:73:15:73:38 | url.par ... , true) | child_process-test.js:73:9:73:49 | cmd | provenance | | | child_process-test.js:73:25:73:31 | req.url | child_process-test.js:73:15:73:38 | url.par ... , true) | provenance | | | child_process-test.js:94:21:94:30 | ctx.params | child_process-test.js:94:11:94:35 | "ping " ... ms.host | provenance | | +| command-line-libs.js:33:9:33:34 | args | command-line-libs.js:34:24:34:27 | args | provenance | | +| command-line-libs.js:33:16:33:23 | req.body | command-line-libs.js:33:9:33:34 | args | provenance | | +| command-line-libs.js:34:9:38:12 | parsed | command-line-libs.js:40:8:40:13 | parsed | provenance | | +| command-line-libs.js:34:18:34:28 | yargs(args) | command-line-libs.js:34:18:38:4 | yargs(a ... ue\\n }) | provenance | | +| command-line-libs.js:34:18:38:4 | yargs(a ... ue\\n }) | command-line-libs.js:34:18:38:12 | yargs(a ... parse() | provenance | | +| command-line-libs.js:34:18:38:12 | yargs(a ... parse() | command-line-libs.js:34:9:38:12 | parsed | provenance | | +| command-line-libs.js:34:24:34:27 | args | command-line-libs.js:34:18:34:28 | yargs(args) | provenance | | +| command-line-libs.js:40:8:40:13 | parsed | command-line-libs.js:40:8:40:17 | parsed.cmd | provenance | | | exec-sh2.js:9:17:9:23 | command | exec-sh2.js:10:40:10:46 | command | provenance | | | exec-sh2.js:14:9:14:49 | cmd | exec-sh2.js:15:12:15:14 | cmd | provenance | | | exec-sh2.js:14:15:14:38 | url.par ... , true) | exec-sh2.js:14:9:14:49 | cmd | provenance | | @@ -269,6 +278,15 @@ nodes | child_process-test.js:83:19:83:36 | req.query.fileName | semmle.label | req.query.fileName | | child_process-test.js:94:11:94:35 | "ping " ... ms.host | semmle.label | "ping " ... ms.host | | child_process-test.js:94:21:94:30 | ctx.params | semmle.label | ctx.params | +| command-line-libs.js:33:9:33:34 | args | semmle.label | args | +| command-line-libs.js:33:16:33:23 | req.body | semmle.label | req.body | +| command-line-libs.js:34:9:38:12 | parsed | semmle.label | parsed | +| command-line-libs.js:34:18:34:28 | yargs(args) | semmle.label | yargs(args) | +| command-line-libs.js:34:18:38:4 | yargs(a ... ue\\n }) | semmle.label | yargs(a ... ue\\n }) | +| command-line-libs.js:34:18:38:12 | yargs(a ... parse() | semmle.label | yargs(a ... parse() | +| command-line-libs.js:34:24:34:27 | args | semmle.label | args | +| command-line-libs.js:40:8:40:13 | parsed | semmle.label | parsed | +| command-line-libs.js:40:8:40:17 | parsed.cmd | semmle.label | parsed.cmd | | exec-sh2.js:9:17:9:23 | command | semmle.label | command | | exec-sh2.js:10:40:10:46 | command | semmle.label | command | | exec-sh2.js:14:9:14:49 | cmd | semmle.label | cmd | diff --git a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/command-line-libs.js b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/command-line-libs.js index c5621354d91d..58ae097297d8 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/command-line-libs.js +++ b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/command-line-libs.js @@ -30,12 +30,12 @@ app.post('/commandLineArgs', (req, res) => { app.post('/yargs', (req, res) => { const yargs = require('yargs/yargs'); - const args = req.body.args || []; // $ MISSING: Source + const args = req.body.args || []; // $ Source const parsed = yargs(args).option('cmd', { type: 'string', describe: 'Command to execute', demandOption: true }).parse(); - exec(parsed.cmd); // $ MISSING: Alert + exec(parsed.cmd); // $ Alert }); From 6b4e34dd39b994b181b9ac13d2249961e47986e8 Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Fri, 1 Aug 2025 13:12:43 +0200 Subject: [PATCH 3/7] Added a step from parse to opts for commander js --- .../javascript/frameworks/CommandLineArguments.qll | 5 +++++ .../CommandInjection/CommandInjection.expected | 14 ++++++++++++++ .../CWE-078/CommandInjection/command-line-libs.js | 4 ++-- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/CommandLineArguments.qll b/javascript/ql/lib/semmle/javascript/frameworks/CommandLineArguments.qll index 8fff526b2bdb..04825b406264 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/CommandLineArguments.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/CommandLineArguments.qll @@ -95,6 +95,11 @@ private class ArgsParseStep extends TaintTracking::SharedTaintStep { pred = call.getArgument(0) ) or + exists(API::Node commanderNode | commanderNode = commander() | + pred = commanderNode.getMember("parse").getACall().getAnArgument() and + succ = commanderNode.getMember("opts").getACall() + ) + or exists(DataFlow::MethodCallNode methodCall | methodCall = yargs() | pred = methodCall.getReceiver() and succ = methodCall 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 4c21013e8f05..7d95ca256f87 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 @@ -21,6 +21,7 @@ | child_process-test.js:75:29:75:31 | cmd | child_process-test.js:73:25:73:31 | req.url | child_process-test.js:75:29:75:31 | cmd | This command line depends on a $@. | child_process-test.js:73:25:73:31 | req.url | user-provided value | | child_process-test.js:83:19:83:36 | req.query.fileName | child_process-test.js:83:19:83:36 | req.query.fileName | child_process-test.js:83:19:83:36 | req.query.fileName | This command line depends on a $@. | child_process-test.js:83:19:83:36 | req.query.fileName | user-provided value | | child_process-test.js:94:11:94:35 | "ping " ... ms.host | child_process-test.js:94:21:94:30 | ctx.params | child_process-test.js:94:11:94:35 | "ping " ... ms.host | This command line depends on a $@. | child_process-test.js:94:21:94:30 | ctx.params | user-provided value | +| command-line-libs.js:14:8:14:18 | options.cmd | command-line-libs.js:9:16:9:23 | req.body | command-line-libs.js:14:8:14:18 | options.cmd | This command line depends on a $@. | command-line-libs.js:9:16:9:23 | req.body | user-provided value | | command-line-libs.js:40:8:40:17 | parsed.cmd | command-line-libs.js:33:16:33:23 | req.body | command-line-libs.js:40:8:40:17 | parsed.cmd | This command line depends on a $@. | command-line-libs.js:33:16:33:23 | req.body | user-provided value | | exec-sh2.js:10:12:10:57 | cp.spaw ... ptions) | exec-sh2.js:14:25:14:31 | req.url | exec-sh2.js:10:40:10:46 | command | This command line depends on a $@. | exec-sh2.js:14:25:14:31 | req.url | user-provided value | | exec-sh.js:15:12:15:61 | cp.spaw ... ptions) | exec-sh.js:19:25:19:31 | req.url | exec-sh.js:15:44:15:50 | command | This command line depends on a $@. | exec-sh.js:19:25:19:31 | req.url | user-provided value | @@ -117,6 +118,12 @@ edges | child_process-test.js:73:15:73:38 | url.par ... , true) | child_process-test.js:73:9:73:49 | cmd | provenance | | | child_process-test.js:73:25:73:31 | req.url | child_process-test.js:73:15:73:38 | url.par ... , true) | provenance | | | child_process-test.js:94:21:94:30 | ctx.params | child_process-test.js:94:11:94:35 | "ping " ... ms.host | provenance | | +| command-line-libs.js:9:9:9:34 | args | command-line-libs.js:12:17:12:20 | args | provenance | | +| command-line-libs.js:9:16:9:23 | req.body | command-line-libs.js:9:9:9:34 | args | provenance | | +| command-line-libs.js:12:17:12:20 | args | command-line-libs.js:13:19:13:32 | program.opts() | provenance | | +| command-line-libs.js:13:9:13:32 | options | command-line-libs.js:14:8:14:14 | options | provenance | | +| command-line-libs.js:13:19:13:32 | program.opts() | command-line-libs.js:13:9:13:32 | options | provenance | | +| command-line-libs.js:14:8:14:14 | options | command-line-libs.js:14:8:14:18 | options.cmd | provenance | | | command-line-libs.js:33:9:33:34 | args | command-line-libs.js:34:24:34:27 | args | provenance | | | command-line-libs.js:33:16:33:23 | req.body | command-line-libs.js:33:9:33:34 | args | provenance | | | command-line-libs.js:34:9:38:12 | parsed | command-line-libs.js:40:8:40:13 | parsed | provenance | | @@ -278,6 +285,13 @@ nodes | child_process-test.js:83:19:83:36 | req.query.fileName | semmle.label | req.query.fileName | | child_process-test.js:94:11:94:35 | "ping " ... ms.host | semmle.label | "ping " ... ms.host | | child_process-test.js:94:21:94:30 | ctx.params | semmle.label | ctx.params | +| command-line-libs.js:9:9:9:34 | args | semmle.label | args | +| command-line-libs.js:9:16:9:23 | req.body | semmle.label | req.body | +| command-line-libs.js:12:17:12:20 | args | semmle.label | args | +| command-line-libs.js:13:9:13:32 | options | semmle.label | options | +| command-line-libs.js:13:19:13:32 | program.opts() | semmle.label | program.opts() | +| command-line-libs.js:14:8:14:14 | options | semmle.label | options | +| command-line-libs.js:14:8:14:18 | options.cmd | semmle.label | options.cmd | | command-line-libs.js:33:9:33:34 | args | semmle.label | args | | command-line-libs.js:33:16:33:23 | req.body | semmle.label | req.body | | command-line-libs.js:34:9:38:12 | parsed | semmle.label | parsed | diff --git a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/command-line-libs.js b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/command-line-libs.js index 58ae097297d8..249bcb1d3baa 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/command-line-libs.js +++ b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/command-line-libs.js @@ -6,12 +6,12 @@ const app = express(); app.use(express.json()); app.post('/Command', (req, res) => { - const args = req.body.args || []; // $ MISSING: Source + const args = req.body.args || []; // $ Source const program = new Command(); program.option('--cmd ', 'Command to execute'); program.parse(args, { from: 'user' }); const options = program.opts(); - exec(options.cmd); // $ MISSING: Alert + exec(options.cmd); // $ Alert }); app.post('/arg', (req, res) => { From 39170f327c17e656e68a530200bad809853e136b Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Fri, 1 Aug 2025 13:14:39 +0200 Subject: [PATCH 4/7] Added couple more test cases for commander js --- .../CommandInjection.expected | 36 +++++++++---------- .../CommandInjection/command-line-libs.js | 11 +++++- 2 files changed, 28 insertions(+), 19 deletions(-) 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 7d95ca256f87..75a8d6e8dc26 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 @@ -22,7 +22,7 @@ | child_process-test.js:83:19:83:36 | req.query.fileName | child_process-test.js:83:19:83:36 | req.query.fileName | child_process-test.js:83:19:83:36 | req.query.fileName | This command line depends on a $@. | child_process-test.js:83:19:83:36 | req.query.fileName | user-provided value | | child_process-test.js:94:11:94:35 | "ping " ... ms.host | child_process-test.js:94:21:94:30 | ctx.params | child_process-test.js:94:11:94:35 | "ping " ... ms.host | This command line depends on a $@. | child_process-test.js:94:21:94:30 | ctx.params | user-provided value | | command-line-libs.js:14:8:14:18 | options.cmd | command-line-libs.js:9:16:9:23 | req.body | command-line-libs.js:14:8:14:18 | options.cmd | This command line depends on a $@. | command-line-libs.js:9:16:9:23 | req.body | user-provided value | -| command-line-libs.js:40:8:40:17 | parsed.cmd | command-line-libs.js:33:16:33:23 | req.body | command-line-libs.js:40:8:40:17 | parsed.cmd | This command line depends on a $@. | command-line-libs.js:33:16:33:23 | req.body | user-provided value | +| command-line-libs.js:49:8:49:17 | parsed.cmd | command-line-libs.js:42:16:42:23 | req.body | command-line-libs.js:49:8:49:17 | parsed.cmd | This command line depends on a $@. | command-line-libs.js:42:16:42:23 | req.body | user-provided value | | exec-sh2.js:10:12:10:57 | cp.spaw ... ptions) | exec-sh2.js:14:25:14:31 | req.url | exec-sh2.js:10:40:10:46 | command | This command line depends on a $@. | exec-sh2.js:14:25:14:31 | req.url | user-provided value | | exec-sh.js:15:12:15:61 | cp.spaw ... ptions) | exec-sh.js:19:25:19:31 | req.url | exec-sh.js:15:44:15:50 | command | This command line depends on a $@. | exec-sh.js:19:25:19:31 | req.url | user-provided value | | execSeries.js:14:41:14:47 | command | execSeries.js:18:34:18:40 | req.url | execSeries.js:14:41:14:47 | command | This command line depends on a $@. | execSeries.js:18:34:18:40 | req.url | user-provided value | @@ -124,14 +124,14 @@ edges | command-line-libs.js:13:9:13:32 | options | command-line-libs.js:14:8:14:14 | options | provenance | | | command-line-libs.js:13:19:13:32 | program.opts() | command-line-libs.js:13:9:13:32 | options | provenance | | | command-line-libs.js:14:8:14:14 | options | command-line-libs.js:14:8:14:18 | options.cmd | provenance | | -| command-line-libs.js:33:9:33:34 | args | command-line-libs.js:34:24:34:27 | args | provenance | | -| command-line-libs.js:33:16:33:23 | req.body | command-line-libs.js:33:9:33:34 | args | provenance | | -| command-line-libs.js:34:9:38:12 | parsed | command-line-libs.js:40:8:40:13 | parsed | provenance | | -| command-line-libs.js:34:18:34:28 | yargs(args) | command-line-libs.js:34:18:38:4 | yargs(a ... ue\\n }) | provenance | | -| command-line-libs.js:34:18:38:4 | yargs(a ... ue\\n }) | command-line-libs.js:34:18:38:12 | yargs(a ... parse() | provenance | | -| command-line-libs.js:34:18:38:12 | yargs(a ... parse() | command-line-libs.js:34:9:38:12 | parsed | provenance | | -| command-line-libs.js:34:24:34:27 | args | command-line-libs.js:34:18:34:28 | yargs(args) | provenance | | -| command-line-libs.js:40:8:40:13 | parsed | command-line-libs.js:40:8:40:17 | parsed.cmd | provenance | | +| command-line-libs.js:42:9:42:34 | args | command-line-libs.js:43:24:43:27 | args | provenance | | +| command-line-libs.js:42:16:42:23 | req.body | command-line-libs.js:42:9:42:34 | args | provenance | | +| command-line-libs.js:43:9:47:12 | parsed | command-line-libs.js:49:8:49:13 | parsed | provenance | | +| command-line-libs.js:43:18:43:28 | yargs(args) | command-line-libs.js:43:18:47:4 | yargs(a ... ue\\n }) | provenance | | +| command-line-libs.js:43:18:47:4 | yargs(a ... ue\\n }) | command-line-libs.js:43:18:47:12 | yargs(a ... parse() | provenance | | +| command-line-libs.js:43:18:47:12 | yargs(a ... parse() | command-line-libs.js:43:9:47:12 | parsed | provenance | | +| command-line-libs.js:43:24:43:27 | args | command-line-libs.js:43:18:43:28 | yargs(args) | provenance | | +| command-line-libs.js:49:8:49:13 | parsed | command-line-libs.js:49:8:49:17 | parsed.cmd | provenance | | | exec-sh2.js:9:17:9:23 | command | exec-sh2.js:10:40:10:46 | command | provenance | | | exec-sh2.js:14:9:14:49 | cmd | exec-sh2.js:15:12:15:14 | cmd | provenance | | | exec-sh2.js:14:15:14:38 | url.par ... , true) | exec-sh2.js:14:9:14:49 | cmd | provenance | | @@ -292,15 +292,15 @@ nodes | command-line-libs.js:13:19:13:32 | program.opts() | semmle.label | program.opts() | | command-line-libs.js:14:8:14:14 | options | semmle.label | options | | command-line-libs.js:14:8:14:18 | options.cmd | semmle.label | options.cmd | -| command-line-libs.js:33:9:33:34 | args | semmle.label | args | -| command-line-libs.js:33:16:33:23 | req.body | semmle.label | req.body | -| command-line-libs.js:34:9:38:12 | parsed | semmle.label | parsed | -| command-line-libs.js:34:18:34:28 | yargs(args) | semmle.label | yargs(args) | -| command-line-libs.js:34:18:38:4 | yargs(a ... ue\\n }) | semmle.label | yargs(a ... ue\\n }) | -| command-line-libs.js:34:18:38:12 | yargs(a ... parse() | semmle.label | yargs(a ... parse() | -| command-line-libs.js:34:24:34:27 | args | semmle.label | args | -| command-line-libs.js:40:8:40:13 | parsed | semmle.label | parsed | -| command-line-libs.js:40:8:40:17 | parsed.cmd | semmle.label | parsed.cmd | +| command-line-libs.js:42:9:42:34 | args | semmle.label | args | +| command-line-libs.js:42:16:42:23 | req.body | semmle.label | req.body | +| command-line-libs.js:43:9:47:12 | parsed | semmle.label | parsed | +| command-line-libs.js:43:18:43:28 | yargs(args) | semmle.label | yargs(args) | +| command-line-libs.js:43:18:47:4 | yargs(a ... ue\\n }) | semmle.label | yargs(a ... ue\\n }) | +| command-line-libs.js:43:18:47:12 | yargs(a ... parse() | semmle.label | yargs(a ... parse() | +| command-line-libs.js:43:24:43:27 | args | semmle.label | args | +| command-line-libs.js:49:8:49:13 | parsed | semmle.label | parsed | +| command-line-libs.js:49:8:49:17 | parsed.cmd | semmle.label | parsed.cmd | | exec-sh2.js:9:17:9:23 | command | semmle.label | command | | exec-sh2.js:10:40:10:46 | command | semmle.label | command | | exec-sh2.js:14:9:14:49 | cmd | semmle.label | cmd | diff --git a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/command-line-libs.js b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/command-line-libs.js index 249bcb1d3baa..c9739793a483 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/command-line-libs.js +++ b/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/command-line-libs.js @@ -5,13 +5,22 @@ import arg from 'arg'; const app = express(); app.use(express.json()); -app.post('/Command', (req, res) => { +app.post('/Command', async (req, res) => { const args = req.body.args || []; // $ Source const program = new Command(); program.option('--cmd ', 'Command to execute'); program.parse(args, { from: 'user' }); const options = program.opts(); exec(options.cmd); // $ Alert + exec(program.cmd); // $ MISSING: Alert + + const program1 = new Command(); + program1 + .command('run