diff --git a/change-notes/1.23/analysis-javascript.md b/change-notes/1.23/analysis-javascript.md index d6ae021ea58..edd926f77bd 100644 --- a/change-notes/1.23/analysis-javascript.md +++ b/change-notes/1.23/analysis-javascript.md @@ -8,10 +8,14 @@ * Support for the following frameworks and libraries has been improved: - [firebase](https://www.npmjs.com/package/firebase) + - [get-them-args](https://www.npmjs.com/package/get-them-args) + - [minimist](https://www.npmjs.com/package/minimist) - [mongodb](https://www.npmjs.com/package/mongodb) - [mongoose](https://www.npmjs.com/package/mongoose) - - [parse-torrent](https://github.com/webtorrent/parse-torrent) + - [optimits](https://www.npmjs.com/package/optimist) + - [parse-torrent](https://www.npmjs.com/package/parse-torrent) - [rate-limiter-flexible](https://www.npmjs.com/package/rate-limiter-flexible) + - [yargs](https://www.npmjs.com/package/yargs) * The call graph has been improved to resolve method calls in more cases. This may produce more security alerts. diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/IndirectCommandInjectionCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/IndirectCommandInjectionCustomizations.qll index d7e269f188f..9e730d67c24 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/IndirectCommandInjectionCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/IndirectCommandInjectionCustomizations.qll @@ -39,6 +39,22 @@ module IndirectCommandInjection { } } + /** + * An object containing command-line arguments that were parsed with a default configuration, considered as a flow source for command injection. + */ + class DefaultParsedCommandLineArgumentsAsSource extends Source { + DefaultParsedCommandLineArgumentsAsSource() { + // `require('get-them-args')(...)` => `{ unknown: [], a: ... b: ... }` + this = DataFlow::moduleImport("get-them-args").getACall() or + // `require('minimist')(...)` => `{ _: [], a: ... b: ... }` + this = DataFlow::moduleImport("minimist").getACall() or + // `require('yargs').argv` => `{ _: [], a: ... b: ... }` + this = DataFlow::moduleMember("yargs", "argv") or + // `require('optimist').argv` => `{ _: [], a: ... b: ... }` + this = DataFlow::moduleMember("optimist", "argv") + } + } + /** * A command-line argument that effectively is system-controlled, and therefore not likely to be exploitable when used in the execution of another command. */ diff --git a/javascript/ql/test/query-tests/Security/CWE-078/IndirectCommandInjection.expected b/javascript/ql/test/query-tests/Security/CWE-078/IndirectCommandInjection.expected index 9377069360e..bb7b3160ef4 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/IndirectCommandInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-078/IndirectCommandInjection.expected @@ -48,6 +48,26 @@ nodes | command-line-parameter-command-injection.js:27:14:27:57 | `node $ ... ption"` | | command-line-parameter-command-injection.js:27:32:27:35 | args | | command-line-parameter-command-injection.js:27:32:27:45 | args.join(' ') | +| command-line-parameter-command-injection.js:30:9:30:50 | "cmd.sh ... )().foo | +| command-line-parameter-command-injection.js:30:9:30:50 | "cmd.sh ... )().foo | +| command-line-parameter-command-injection.js:30:21:30:46 | require ... rgs")() | +| command-line-parameter-command-injection.js:30:21:30:46 | require ... rgs")() | +| command-line-parameter-command-injection.js:30:21:30:50 | require ... )().foo | +| command-line-parameter-command-injection.js:31:9:31:45 | "cmd.sh ... )().foo | +| command-line-parameter-command-injection.js:31:9:31:45 | "cmd.sh ... )().foo | +| command-line-parameter-command-injection.js:31:21:31:41 | require ... ist")() | +| command-line-parameter-command-injection.js:31:21:31:41 | require ... ist")() | +| command-line-parameter-command-injection.js:31:21:31:45 | require ... )().foo | +| command-line-parameter-command-injection.js:32:9:32:45 | "cmd.sh ... rgv.foo | +| command-line-parameter-command-injection.js:32:9:32:45 | "cmd.sh ... rgv.foo | +| command-line-parameter-command-injection.js:32:21:32:41 | require ... ").argv | +| command-line-parameter-command-injection.js:32:21:32:41 | require ... ").argv | +| command-line-parameter-command-injection.js:32:21:32:45 | require ... rgv.foo | +| command-line-parameter-command-injection.js:33:9:33:48 | "cmd.sh ... rgv.foo | +| command-line-parameter-command-injection.js:33:9:33:48 | "cmd.sh ... rgv.foo | +| command-line-parameter-command-injection.js:33:21:33:44 | require ... ").argv | +| command-line-parameter-command-injection.js:33:21:33:44 | require ... ").argv | +| command-line-parameter-command-injection.js:33:21:33:48 | require ... rgv.foo | edges | command-line-parameter-command-injection.js:4:10:4:21 | process.argv | command-line-parameter-command-injection.js:4:10:4:21 | process.argv | | command-line-parameter-command-injection.js:8:22:8:33 | process.argv | command-line-parameter-command-injection.js:8:22:8:36 | process.argv[2] | @@ -93,6 +113,22 @@ edges | command-line-parameter-command-injection.js:27:32:27:35 | args | command-line-parameter-command-injection.js:27:32:27:45 | args.join(' ') | | command-line-parameter-command-injection.js:27:32:27:45 | args.join(' ') | command-line-parameter-command-injection.js:27:14:27:57 | `node $ ... ption"` | | command-line-parameter-command-injection.js:27:32:27:45 | args.join(' ') | command-line-parameter-command-injection.js:27:14:27:57 | `node $ ... ption"` | +| command-line-parameter-command-injection.js:30:21:30:46 | require ... rgs")() | command-line-parameter-command-injection.js:30:21:30:50 | require ... )().foo | +| command-line-parameter-command-injection.js:30:21:30:46 | require ... rgs")() | command-line-parameter-command-injection.js:30:21:30:50 | require ... )().foo | +| command-line-parameter-command-injection.js:30:21:30:50 | require ... )().foo | command-line-parameter-command-injection.js:30:9:30:50 | "cmd.sh ... )().foo | +| command-line-parameter-command-injection.js:30:21:30:50 | require ... )().foo | command-line-parameter-command-injection.js:30:9:30:50 | "cmd.sh ... )().foo | +| command-line-parameter-command-injection.js:31:21:31:41 | require ... ist")() | command-line-parameter-command-injection.js:31:21:31:45 | require ... )().foo | +| command-line-parameter-command-injection.js:31:21:31:41 | require ... ist")() | command-line-parameter-command-injection.js:31:21:31:45 | require ... )().foo | +| command-line-parameter-command-injection.js:31:21:31:45 | require ... )().foo | command-line-parameter-command-injection.js:31:9:31:45 | "cmd.sh ... )().foo | +| command-line-parameter-command-injection.js:31:21:31:45 | require ... )().foo | command-line-parameter-command-injection.js:31:9:31:45 | "cmd.sh ... )().foo | +| command-line-parameter-command-injection.js:32:21:32:41 | require ... ").argv | command-line-parameter-command-injection.js:32:21:32:45 | require ... rgv.foo | +| command-line-parameter-command-injection.js:32:21:32:41 | require ... ").argv | command-line-parameter-command-injection.js:32:21:32:45 | require ... rgv.foo | +| command-line-parameter-command-injection.js:32:21:32:45 | require ... rgv.foo | command-line-parameter-command-injection.js:32:9:32:45 | "cmd.sh ... rgv.foo | +| command-line-parameter-command-injection.js:32:21:32:45 | require ... rgv.foo | command-line-parameter-command-injection.js:32:9:32:45 | "cmd.sh ... rgv.foo | +| command-line-parameter-command-injection.js:33:21:33:44 | require ... ").argv | command-line-parameter-command-injection.js:33:21:33:48 | require ... rgv.foo | +| command-line-parameter-command-injection.js:33:21:33:44 | require ... ").argv | command-line-parameter-command-injection.js:33:21:33:48 | require ... rgv.foo | +| command-line-parameter-command-injection.js:33:21:33:48 | require ... rgv.foo | command-line-parameter-command-injection.js:33:9:33:48 | "cmd.sh ... rgv.foo | +| command-line-parameter-command-injection.js:33:21:33:48 | require ... rgv.foo | command-line-parameter-command-injection.js:33:9:33:48 | "cmd.sh ... rgv.foo | #select | command-line-parameter-command-injection.js:4:10:4:21 | process.argv | command-line-parameter-command-injection.js:4:10:4:21 | process.argv | command-line-parameter-command-injection.js:4:10:4:21 | process.argv | This command depends on an unsanitized $@. | command-line-parameter-command-injection.js:4:10:4:21 | process.argv | command-line argument | | command-line-parameter-command-injection.js:8:10:8:36 | "cmd.sh ... argv[2] | command-line-parameter-command-injection.js:8:22:8:33 | process.argv | command-line-parameter-command-injection.js:8:10:8:36 | "cmd.sh ... argv[2] | This command depends on an unsanitized $@. | command-line-parameter-command-injection.js:8:22:8:33 | process.argv | command-line argument | @@ -104,3 +140,7 @@ edges | command-line-parameter-command-injection.js:20:14:20:29 | "cmd.sh " + arg0 | command-line-parameter-command-injection.js:10:13:10:24 | process.argv | command-line-parameter-command-injection.js:20:14:20:29 | "cmd.sh " + arg0 | This command depends on an unsanitized $@. | command-line-parameter-command-injection.js:10:13:10:24 | process.argv | command-line argument | | command-line-parameter-command-injection.js:26:14:26:50 | `node $ ... ption"` | command-line-parameter-command-injection.js:24:15:24:26 | process.argv | command-line-parameter-command-injection.js:26:14:26:50 | `node $ ... ption"` | This command depends on an unsanitized $@. | command-line-parameter-command-injection.js:24:15:24:26 | process.argv | command-line argument | | command-line-parameter-command-injection.js:27:14:27:57 | `node $ ... ption"` | command-line-parameter-command-injection.js:24:15:24:26 | process.argv | command-line-parameter-command-injection.js:27:14:27:57 | `node $ ... ption"` | This command depends on an unsanitized $@. | command-line-parameter-command-injection.js:24:15:24:26 | process.argv | command-line argument | +| command-line-parameter-command-injection.js:30:9:30:50 | "cmd.sh ... )().foo | command-line-parameter-command-injection.js:30:21:30:46 | require ... rgs")() | command-line-parameter-command-injection.js:30:9:30:50 | "cmd.sh ... )().foo | This command depends on an unsanitized $@. | command-line-parameter-command-injection.js:30:21:30:46 | require ... rgs")() | command-line argument | +| command-line-parameter-command-injection.js:31:9:31:45 | "cmd.sh ... )().foo | command-line-parameter-command-injection.js:31:21:31:41 | require ... ist")() | command-line-parameter-command-injection.js:31:9:31:45 | "cmd.sh ... )().foo | This command depends on an unsanitized $@. | command-line-parameter-command-injection.js:31:21:31:41 | require ... ist")() | command-line argument | +| command-line-parameter-command-injection.js:32:9:32:45 | "cmd.sh ... rgv.foo | command-line-parameter-command-injection.js:32:21:32:41 | require ... ").argv | command-line-parameter-command-injection.js:32:9:32:45 | "cmd.sh ... rgv.foo | This command depends on an unsanitized $@. | command-line-parameter-command-injection.js:32:21:32:41 | require ... ").argv | command-line argument | +| command-line-parameter-command-injection.js:33:9:33:48 | "cmd.sh ... rgv.foo | command-line-parameter-command-injection.js:33:21:33:44 | require ... ").argv | command-line-parameter-command-injection.js:33:9:33:48 | "cmd.sh ... rgv.foo | This command depends on an unsanitized $@. | command-line-parameter-command-injection.js:33:21:33:44 | require ... ").argv | command-line argument | diff --git a/javascript/ql/test/query-tests/Security/CWE-078/command-line-parameter-command-injection.js b/javascript/ql/test/query-tests/Security/CWE-078/command-line-parameter-command-injection.js index a6fd9178f8f..85dcd4b6996 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/command-line-parameter-command-injection.js +++ b/javascript/ql/test/query-tests/Security/CWE-078/command-line-parameter-command-injection.js @@ -26,3 +26,8 @@ var cp = require("child_process"); cp.execSync(`node ${script} ${args[0]} --option"`); // NOT OK cp.execSync(`node ${script} ${args.join(' ')} --option"`); // NOT OK }); + +cp.exec("cmd.sh " + require("get-them-args")().foo); // NOT OK +cp.exec("cmd.sh " + require("minimist")().foo); // NOT OK +cp.exec("cmd.sh " + require("yargs").argv.foo); // NOT OK +cp.exec("cmd.sh " + require("optimist").argv.foo); // NOT OK