mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Merge pull request #19858 from Napalys/js/execa
JS: moved `execa` out of experimental
This commit is contained in:
4
javascript/ql/lib/change-notes/2025-06-20-execa.md
Normal file
4
javascript/ql/lib/change-notes/2025-06-20-execa.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Enhanced modeling for the `execa` library, adding support for command execution methods `execaCommand`, `execaCommandSync`, `$`, and `$.sync`, as well as file system operations through `inputFile`, `pipeStdout`, `pipeAll`, and `pipeStderr`.
|
||||
@@ -92,6 +92,7 @@ import semmle.javascript.frameworks.DigitalOcean
|
||||
import semmle.javascript.frameworks.DomEvents
|
||||
import semmle.javascript.frameworks.Electron
|
||||
import semmle.javascript.frameworks.EventEmitter
|
||||
import semmle.javascript.frameworks.Execa
|
||||
import semmle.javascript.frameworks.Files
|
||||
import semmle.javascript.frameworks.Firebase
|
||||
import semmle.javascript.frameworks.FormParsers
|
||||
|
||||
@@ -58,6 +58,9 @@ module Execa {
|
||||
or
|
||||
this = API::moduleImport("execa").getMember("execaSync").getACall() and
|
||||
isSync = true
|
||||
or
|
||||
this = API::moduleImport("execa").getACall() and
|
||||
isSync = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,4 +211,86 @@ module Execa {
|
||||
private predicate isExecaShellEnable(API::Node n) {
|
||||
n.getMember("shell").asSink().asExpr().(BooleanLiteral).getValue() = "true"
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `execa.node`
|
||||
*/
|
||||
class ExecaNodeCall extends SystemCommandExecution, API::CallNode {
|
||||
ExecaNodeCall() { this = API::moduleImport("execa").getMember("node").getACall() }
|
||||
|
||||
override DataFlow::Node getACommandArgument() { result = this.getArgument(0) }
|
||||
|
||||
override predicate isShellInterpreted(DataFlow::Node arg) { none() }
|
||||
|
||||
override DataFlow::Node getArgumentList() {
|
||||
result = this.getArgument(1) and
|
||||
not result.asExpr() instanceof ObjectExpr
|
||||
}
|
||||
|
||||
override predicate isSync() { none() }
|
||||
|
||||
override DataFlow::Node getOptionsArg() {
|
||||
result = this.getLastArgument() and
|
||||
result.asExpr() instanceof ObjectExpr
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `execa.stdout`, `execa.stderr`, or `execa.sync`
|
||||
*/
|
||||
class ExecaStreamCall extends SystemCommandExecution, API::CallNode {
|
||||
string methodName;
|
||||
|
||||
ExecaStreamCall() {
|
||||
methodName in ["stdout", "stderr", "sync"] and
|
||||
this = API::moduleImport("execa").getMember(methodName).getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getACommandArgument() { result = this.getArgument(0) }
|
||||
|
||||
override predicate isShellInterpreted(DataFlow::Node arg) {
|
||||
arg = this.getArgument(0) and
|
||||
isExecaShellEnable(this.getParameter([1, 2]))
|
||||
}
|
||||
|
||||
override DataFlow::Node getArgumentList() {
|
||||
result = this.getArgument(1) and
|
||||
not result.asExpr() instanceof ObjectExpr
|
||||
}
|
||||
|
||||
override predicate isSync() { methodName = "sync" }
|
||||
|
||||
override DataFlow::Node getOptionsArg() {
|
||||
result = this.getLastArgument() and
|
||||
result.asExpr() instanceof ObjectExpr
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `execa.shell` or `execa.shellSync`
|
||||
*/
|
||||
class ExecaShellCall extends SystemCommandExecution, API::CallNode {
|
||||
boolean sync;
|
||||
|
||||
ExecaShellCall() {
|
||||
this = API::moduleImport("execa").getMember("shell").getACall() and
|
||||
sync = false
|
||||
or
|
||||
this = API::moduleImport("execa").getMember("shellSync").getACall() and
|
||||
sync = true
|
||||
}
|
||||
|
||||
override DataFlow::Node getACommandArgument() { result = this.getArgument(0) }
|
||||
|
||||
override predicate isShellInterpreted(DataFlow::Node arg) { arg = this.getACommandArgument() }
|
||||
|
||||
override DataFlow::Node getArgumentList() { none() }
|
||||
|
||||
override predicate isSync() { sync = true }
|
||||
|
||||
override DataFlow::Node getOptionsArg() {
|
||||
result = this.getArgument(1) and
|
||||
result.asExpr() instanceof ObjectExpr
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,17 +16,6 @@ private predicate execApi(
|
||||
cmdArg = 0 and
|
||||
shell = false and
|
||||
optionsArg = -1
|
||||
or
|
||||
mod = "execa" and
|
||||
optionsArg = -1 and
|
||||
(
|
||||
shell = false and
|
||||
fn = ["node", "stdout", "stderr", "sync"]
|
||||
or
|
||||
shell = true and
|
||||
fn = ["command", "commandSync", "shell", "shellSync"]
|
||||
) and
|
||||
cmdArg = 0
|
||||
)
|
||||
}
|
||||
|
||||
@@ -38,8 +27,6 @@ private predicate execApi(string mod, int cmdArg, int optionsArg, boolean shell)
|
||||
mod = "cross-spawn-async" and cmdArg = 0 and optionsArg = -1
|
||||
or
|
||||
mod = "exec-async" and cmdArg = 0 and optionsArg = -1
|
||||
or
|
||||
mod = "execa" and cmdArg = 0 and optionsArg = -1
|
||||
)
|
||||
or
|
||||
shell = true and
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
passingPositiveTests
|
||||
| PASSED | CommandInjection | tests.js:11:46:11:70 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:12:43:12:67 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:13:63:13:87 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:14:62:14:86 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:15:60:15:84 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:17:45:17:69 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:18:42:18:66 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:19:62:19:86 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:20:63:20:87 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:21:60:21:84 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:23:43:23:67 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:24:40:24:64 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:25:40:25:64 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:26:60:26:84 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:28:41:28:65 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:29:58:29:82 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:31:51:31:75 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:32:68:32:92 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:34:49:34:73 | // test ... jection |
|
||||
| PASSED | CommandInjection | tests.js:35:66:35:90 | // test ... jection |
|
||||
failingPositiveTests
|
||||
@@ -1,36 +0,0 @@
|
||||
import { execa, execaSync, execaCommand, execaCommandSync, $ } from 'execa';
|
||||
import http from 'node:http'
|
||||
import url from 'url'
|
||||
|
||||
http.createServer(async function (req, res) {
|
||||
let cmd = url.parse(req.url, true).query["cmd"][0];
|
||||
let arg1 = url.parse(req.url, true).query["arg1"];
|
||||
let arg2 = url.parse(req.url, true).query["arg2"];
|
||||
let arg3 = url.parse(req.url, true).query["arg3"];
|
||||
|
||||
await $`${cmd} ${arg1} ${arg2} ${arg3}`; // test: CommandInjection
|
||||
await $`ssh ${arg1} ${arg2} ${arg3}`; // test: CommandInjection
|
||||
$({ shell: false }).sync`${cmd} ${arg1} ${arg2} ${arg3}`; // test: CommandInjection
|
||||
$({ shell: true }).sync`${cmd} ${arg1} ${arg2} ${arg3}`; // test: CommandInjection
|
||||
$({ shell: false }).sync`ssh ${arg1} ${arg2} ${arg3}`; // test: CommandInjection
|
||||
|
||||
$.sync`${cmd} ${arg1} ${arg2} ${arg3}`; // test: CommandInjection
|
||||
$.sync`ssh ${arg1} ${arg2} ${arg3}`; // test: CommandInjection
|
||||
await $({ shell: true })`${cmd} ${arg1} ${arg2} ${arg3}` // test: CommandInjection
|
||||
await $({ shell: false })`${cmd} ${arg1} ${arg2} ${arg3}` // test: CommandInjection
|
||||
await $({ shell: false })`ssh ${arg1} ${arg2} ${arg3}` // test: CommandInjection
|
||||
|
||||
await execa(cmd, [arg1, arg2, arg3]); // test: CommandInjection
|
||||
await execa(cmd, { shell: true }); // test: CommandInjection
|
||||
await execa(cmd, { shell: true }); // test: CommandInjection
|
||||
await execa(cmd, [arg1, arg2, arg3], { shell: true }); // test: CommandInjection
|
||||
|
||||
execaSync(cmd, [arg1, arg2, arg3]); // test: CommandInjection
|
||||
execaSync(cmd, [arg1, arg2, arg3], { shell: true }); // test: CommandInjection
|
||||
|
||||
await execaCommand(cmd + arg1 + arg2 + arg3); // test: CommandInjection
|
||||
await execaCommand(cmd + arg1 + arg2 + arg3, { shell: true }); // test: CommandInjection
|
||||
|
||||
execaCommandSync(cmd + arg1 + arg2 + arg3); // test: CommandInjection
|
||||
execaCommandSync(cmd + arg1 + arg2 + arg3, { shell: true }); // test: CommandInjection
|
||||
});
|
||||
@@ -1,38 +0,0 @@
|
||||
import javascript
|
||||
|
||||
class InlineTest extends LineComment {
|
||||
string tests;
|
||||
|
||||
InlineTest() { tests = this.getText().regexpCapture("\\s*test:(.*)", 1) }
|
||||
|
||||
string getPositiveTest() {
|
||||
result = tests.trim().splitAt(",").trim() and not result.matches("!%")
|
||||
}
|
||||
|
||||
predicate hasPositiveTest(string test) { test = this.getPositiveTest() }
|
||||
|
||||
predicate inNode(DataFlow::Node n) {
|
||||
this.getLocation().getFile() = n.getFile() and
|
||||
this.getLocation().getStartLine() = n.getStartLine()
|
||||
}
|
||||
}
|
||||
|
||||
import experimental.semmle.javascript.Execa
|
||||
|
||||
query predicate passingPositiveTests(string res, string expectation, InlineTest t) {
|
||||
res = "PASSED" and
|
||||
t.hasPositiveTest(expectation) and
|
||||
expectation = "CommandInjection" and
|
||||
exists(SystemCommandExecution n |
|
||||
t.inNode(n.getArgumentList()) or t.inNode(n.getACommandArgument())
|
||||
)
|
||||
}
|
||||
|
||||
query predicate failingPositiveTests(string res, string expectation, InlineTest t) {
|
||||
res = "FAILED" and
|
||||
t.hasPositiveTest(expectation) and
|
||||
expectation = "CommandInjection" and
|
||||
not exists(SystemCommandExecution n |
|
||||
t.inNode(n.getArgumentList()) or t.inNode(n.getACommandArgument())
|
||||
)
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
passingPositiveTests
|
||||
| PASSED | PathInjection | tests.js:9:43:9:64 | // test ... jection |
|
||||
| PASSED | PathInjection | tests.js:12:50:12:71 | // test ... jection |
|
||||
| PASSED | PathInjection | tests.js:15:61:15:82 | // test ... jection |
|
||||
| PASSED | PathInjection | tests.js:18:73:18:94 | // test ... jection |
|
||||
failingPositiveTests
|
||||
@@ -1,34 +0,0 @@
|
||||
import javascript
|
||||
|
||||
class InlineTest extends LineComment {
|
||||
string tests;
|
||||
|
||||
InlineTest() { tests = this.getText().regexpCapture("\\s*test:(.*)", 1) }
|
||||
|
||||
string getPositiveTest() {
|
||||
result = tests.trim().splitAt(",").trim() and not result.matches("!%")
|
||||
}
|
||||
|
||||
predicate hasPositiveTest(string test) { test = this.getPositiveTest() }
|
||||
|
||||
predicate inNode(DataFlow::Node n) {
|
||||
this.getLocation().getFile() = n.getFile() and
|
||||
this.getLocation().getStartLine() = n.getStartLine()
|
||||
}
|
||||
}
|
||||
|
||||
import experimental.semmle.javascript.Execa
|
||||
|
||||
query predicate passingPositiveTests(string res, string expectation, InlineTest t) {
|
||||
res = "PASSED" and
|
||||
t.hasPositiveTest(expectation) and
|
||||
expectation = "PathInjection" and
|
||||
exists(FileSystemReadAccess n | t.inNode(n.getAPathArgument()))
|
||||
}
|
||||
|
||||
query predicate failingPositiveTests(string res, string expectation, InlineTest t) {
|
||||
res = "FAILED" and
|
||||
t.hasPositiveTest(expectation) and
|
||||
expectation = "PathInjection" and
|
||||
not exists(FileSystemReadAccess n | t.inNode(n.getAPathArgument()))
|
||||
}
|
||||
@@ -48,6 +48,10 @@
|
||||
| TaintedPath.js:214:29:214:42 | improperEscape | TaintedPath.js:212:24:212:30 | req.url | TaintedPath.js:214:29:214:42 | improperEscape | This path depends on a $@. | TaintedPath.js:212:24:212:30 | req.url | user-provided value |
|
||||
| TaintedPath.js:216:29:216:43 | improperEscape2 | TaintedPath.js:212:24:212:30 | req.url | TaintedPath.js:216:29:216:43 | improperEscape2 | This path depends on a $@. | TaintedPath.js:212:24:212:30 | req.url | user-provided value |
|
||||
| examples/TaintedPath.js:10:29:10:43 | ROOT + filePath | examples/TaintedPath.js:8:28:8:34 | req.url | examples/TaintedPath.js:10:29:10:43 | ROOT + filePath | This path depends on a $@. | examples/TaintedPath.js:8:28:8:34 | req.url | user-provided value |
|
||||
| execa.js:9:26:9:33 | filePath | execa.js:6:30:6:36 | req.url | execa.js:9:26:9:33 | filePath | This path depends on a $@. | execa.js:6:30:6:36 | req.url | user-provided value |
|
||||
| execa.js:12:37:12:44 | filePath | execa.js:6:30:6:36 | req.url | execa.js:12:37:12:44 | filePath | This path depends on a $@. | execa.js:6:30:6:36 | req.url | user-provided value |
|
||||
| execa.js:15:50:15:57 | filePath | execa.js:6:30:6:36 | req.url | execa.js:15:50:15:57 | filePath | This path depends on a $@. | execa.js:6:30:6:36 | req.url | user-provided value |
|
||||
| execa.js:18:62:18:69 | filePath | execa.js:6:30:6:36 | req.url | execa.js:18:62:18:69 | filePath | This path depends on a $@. | execa.js:6:30:6:36 | req.url | user-provided value |
|
||||
| express.js:8:20:8:32 | req.query.bar | express.js:8:20:8:32 | req.query.bar | express.js:8:20:8:32 | req.query.bar | This path depends on a $@. | express.js:8:20:8:32 | req.query.bar | user-provided value |
|
||||
| handlebars.js:11:32:11:39 | filePath | handlebars.js:29:46:29:60 | req.params.path | handlebars.js:11:32:11:39 | filePath | This path depends on a $@. | handlebars.js:29:46:29:60 | req.params.path | user-provided value |
|
||||
| handlebars.js:15:25:15:32 | filePath | handlebars.js:43:15:43:29 | req.params.path | handlebars.js:15:25:15:32 | filePath | This path depends on a $@. | handlebars.js:43:15:43:29 | req.params.path | user-provided value |
|
||||
@@ -399,6 +403,15 @@ edges
|
||||
| examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path | examples/TaintedPath.js:8:7:8:52 | filePath | provenance | |
|
||||
| examples/TaintedPath.js:8:28:8:34 | req.url | examples/TaintedPath.js:8:18:8:41 | url.par ... , true) | provenance | Config |
|
||||
| examples/TaintedPath.js:10:36:10:43 | filePath | examples/TaintedPath.js:10:29:10:43 | ROOT + filePath | provenance | Config |
|
||||
| execa.js:6:9:6:64 | filePath | execa.js:9:26:9:33 | filePath | provenance | |
|
||||
| execa.js:6:9:6:64 | filePath | execa.js:12:37:12:44 | filePath | provenance | |
|
||||
| execa.js:6:9:6:64 | filePath | execa.js:15:50:15:57 | filePath | provenance | |
|
||||
| execa.js:6:9:6:64 | filePath | execa.js:18:62:18:69 | filePath | provenance | |
|
||||
| execa.js:6:20:6:43 | url.par ... , true) | execa.js:6:20:6:49 | url.par ... ).query | provenance | Config |
|
||||
| execa.js:6:20:6:49 | url.par ... ).query | execa.js:6:20:6:61 | url.par ... ePath"] | provenance | Config |
|
||||
| execa.js:6:20:6:61 | url.par ... ePath"] | execa.js:6:20:6:64 | url.par ... th"][0] | provenance | Config |
|
||||
| execa.js:6:20:6:64 | url.par ... th"][0] | execa.js:6:9:6:64 | filePath | provenance | |
|
||||
| execa.js:6:30:6:36 | req.url | execa.js:6:20:6:43 | url.par ... , true) | provenance | Config |
|
||||
| handlebars.js:10:51:10:58 | filePath | handlebars.js:11:32:11:39 | filePath | provenance | |
|
||||
| handlebars.js:13:73:13:80 | filePath | handlebars.js:15:25:15:32 | filePath | provenance | |
|
||||
| handlebars.js:29:46:29:60 | req.params.path | handlebars.js:10:51:10:58 | filePath | provenance | |
|
||||
@@ -944,6 +957,16 @@ nodes
|
||||
| examples/TaintedPath.js:8:28:8:34 | req.url | semmle.label | req.url |
|
||||
| examples/TaintedPath.js:10:29:10:43 | ROOT + filePath | semmle.label | ROOT + filePath |
|
||||
| examples/TaintedPath.js:10:36:10:43 | filePath | semmle.label | filePath |
|
||||
| execa.js:6:9:6:64 | filePath | semmle.label | filePath |
|
||||
| execa.js:6:20:6:43 | url.par ... , true) | semmle.label | url.par ... , true) |
|
||||
| execa.js:6:20:6:49 | url.par ... ).query | semmle.label | url.par ... ).query |
|
||||
| execa.js:6:20:6:61 | url.par ... ePath"] | semmle.label | url.par ... ePath"] |
|
||||
| execa.js:6:20:6:64 | url.par ... th"][0] | semmle.label | url.par ... th"][0] |
|
||||
| execa.js:6:30:6:36 | req.url | semmle.label | req.url |
|
||||
| execa.js:9:26:9:33 | filePath | semmle.label | filePath |
|
||||
| execa.js:12:37:12:44 | filePath | semmle.label | filePath |
|
||||
| execa.js:15:50:15:57 | filePath | semmle.label | filePath |
|
||||
| execa.js:18:62:18:69 | filePath | semmle.label | filePath |
|
||||
| express.js:8:20:8:32 | req.query.bar | semmle.label | req.query.bar |
|
||||
| handlebars.js:10:51:10:58 | filePath | semmle.label | filePath |
|
||||
| handlebars.js:11:32:11:39 | filePath | semmle.label | filePath |
|
||||
|
||||
@@ -3,17 +3,17 @@ import http from 'node:http'
|
||||
import url from 'url'
|
||||
|
||||
http.createServer(async function (req, res) {
|
||||
let filePath = url.parse(req.url, true).query["filePath"][0];
|
||||
let filePath = url.parse(req.url, true).query["filePath"][0]; // $Source
|
||||
|
||||
// Piping to stdin from a file
|
||||
await $({ inputFile: filePath })`cat` // test: PathInjection
|
||||
await $({ inputFile: filePath })`cat` // $Alert
|
||||
|
||||
// Piping to stdin from a file
|
||||
await execa('cat', { inputFile: filePath }); // test: PathInjection
|
||||
await execa('cat', { inputFile: filePath }); // $Alert
|
||||
|
||||
// Piping Stdout to file
|
||||
await execa('echo', ['example3']).pipeStdout(filePath); // test: PathInjection
|
||||
await execa('echo', ['example3']).pipeStdout(filePath); // $Alert
|
||||
|
||||
// Piping all of command output to file
|
||||
await execa('echo', ['example4'], { all: true }).pipeAll(filePath); // test: PathInjection
|
||||
});
|
||||
await execa('echo', ['example4'], { all: true }).pipeAll(filePath); // $Alert
|
||||
});
|
||||
@@ -24,6 +24,33 @@
|
||||
| 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 |
|
||||
| execa.js:11:15:11:17 | cmd | execa.js:6:25:6:31 | req.url | execa.js:11:15:11:17 | cmd | This command line depends on a $@. | execa.js:6:25:6:31 | req.url | user-provided value |
|
||||
| execa.js:13:32:13:34 | cmd | execa.js:6:25:6:31 | req.url | execa.js:13:32:13:34 | cmd | This command line depends on a $@. | execa.js:6:25:6:31 | req.url | user-provided value |
|
||||
| execa.js:14:31:14:33 | cmd | execa.js:6:25:6:31 | req.url | execa.js:14:31:14:33 | cmd | This command line depends on a $@. | execa.js:6:25:6:31 | req.url | user-provided value |
|
||||
| execa.js:17:14:17:16 | cmd | execa.js:6:25:6:31 | req.url | execa.js:17:14:17:16 | cmd | This command line depends on a $@. | execa.js:6:25:6:31 | req.url | user-provided value |
|
||||
| execa.js:19:32:19:34 | cmd | execa.js:6:25:6:31 | req.url | execa.js:19:32:19:34 | cmd | This command line depends on a $@. | execa.js:6:25:6:31 | req.url | user-provided value |
|
||||
| execa.js:20:33:20:35 | cmd | execa.js:6:25:6:31 | req.url | execa.js:20:33:20:35 | cmd | This command line depends on a $@. | execa.js:6:25:6:31 | req.url | user-provided value |
|
||||
| execa.js:23:17:23:19 | cmd | execa.js:6:25:6:31 | req.url | execa.js:23:17:23:19 | cmd | This command line depends on a $@. | execa.js:6:25:6:31 | req.url | user-provided value |
|
||||
| execa.js:24:17:24:19 | cmd | execa.js:6:25:6:31 | req.url | execa.js:24:17:24:19 | cmd | This command line depends on a $@. | execa.js:6:25:6:31 | req.url | user-provided value |
|
||||
| execa.js:25:17:25:19 | cmd | execa.js:6:25:6:31 | req.url | execa.js:25:17:25:19 | cmd | This command line depends on a $@. | execa.js:6:25:6:31 | req.url | user-provided value |
|
||||
| execa.js:27:15:27:17 | cmd | execa.js:6:25:6:31 | req.url | execa.js:27:15:27:17 | cmd | This command line depends on a $@. | execa.js:6:25:6:31 | req.url | user-provided value |
|
||||
| execa.js:28:15:28:17 | cmd | execa.js:6:25:6:31 | req.url | execa.js:28:15:28:17 | cmd | This command line depends on a $@. | execa.js:6:25:6:31 | req.url | user-provided value |
|
||||
| execa.js:30:24:30:47 | cmd + a ... + arg3 | execa.js:6:25:6:31 | req.url | execa.js:30:24:30:47 | cmd + a ... + arg3 | This command line depends on a $@. | execa.js:6:25:6:31 | req.url | user-provided value |
|
||||
| execa.js:30:24:30:47 | cmd + a ... + arg3 | execa.js:7:26:7:32 | req.url | execa.js:30:24:30:47 | cmd + a ... + arg3 | This command line depends on a $@. | execa.js:7:26:7:32 | req.url | user-provided value |
|
||||
| execa.js:30:24:30:47 | cmd + a ... + arg3 | execa.js:8:26:8:32 | req.url | execa.js:30:24:30:47 | cmd + a ... + arg3 | This command line depends on a $@. | execa.js:8:26:8:32 | req.url | user-provided value |
|
||||
| execa.js:30:24:30:47 | cmd + a ... + arg3 | execa.js:9:26:9:32 | req.url | execa.js:30:24:30:47 | cmd + a ... + arg3 | This command line depends on a $@. | execa.js:9:26:9:32 | req.url | user-provided value |
|
||||
| execa.js:31:24:31:47 | cmd + a ... + arg3 | execa.js:6:25:6:31 | req.url | execa.js:31:24:31:47 | cmd + a ... + arg3 | This command line depends on a $@. | execa.js:6:25:6:31 | req.url | user-provided value |
|
||||
| execa.js:31:24:31:47 | cmd + a ... + arg3 | execa.js:7:26:7:32 | req.url | execa.js:31:24:31:47 | cmd + a ... + arg3 | This command line depends on a $@. | execa.js:7:26:7:32 | req.url | user-provided value |
|
||||
| execa.js:31:24:31:47 | cmd + a ... + arg3 | execa.js:8:26:8:32 | req.url | execa.js:31:24:31:47 | cmd + a ... + arg3 | This command line depends on a $@. | execa.js:8:26:8:32 | req.url | user-provided value |
|
||||
| execa.js:31:24:31:47 | cmd + a ... + arg3 | execa.js:9:26:9:32 | req.url | execa.js:31:24:31:47 | cmd + a ... + arg3 | This command line depends on a $@. | execa.js:9:26:9:32 | req.url | user-provided value |
|
||||
| execa.js:33:22:33:45 | cmd + a ... + arg3 | execa.js:6:25:6:31 | req.url | execa.js:33:22:33:45 | cmd + a ... + arg3 | This command line depends on a $@. | execa.js:6:25:6:31 | req.url | user-provided value |
|
||||
| execa.js:33:22:33:45 | cmd + a ... + arg3 | execa.js:7:26:7:32 | req.url | execa.js:33:22:33:45 | cmd + a ... + arg3 | This command line depends on a $@. | execa.js:7:26:7:32 | req.url | user-provided value |
|
||||
| execa.js:33:22:33:45 | cmd + a ... + arg3 | execa.js:8:26:8:32 | req.url | execa.js:33:22:33:45 | cmd + a ... + arg3 | This command line depends on a $@. | execa.js:8:26:8:32 | req.url | user-provided value |
|
||||
| execa.js:33:22:33:45 | cmd + a ... + arg3 | execa.js:9:26:9:32 | req.url | execa.js:33:22:33:45 | cmd + a ... + arg3 | This command line depends on a $@. | execa.js:9:26:9:32 | req.url | user-provided value |
|
||||
| execa.js:34:22:34:45 | cmd + a ... + arg3 | execa.js:6:25:6:31 | req.url | execa.js:34:22:34:45 | cmd + a ... + arg3 | This command line depends on a $@. | execa.js:6:25:6:31 | req.url | user-provided value |
|
||||
| execa.js:34:22:34:45 | cmd + a ... + arg3 | execa.js:7:26:7:32 | req.url | execa.js:34:22:34:45 | cmd + a ... + arg3 | This command line depends on a $@. | execa.js:7:26:7:32 | req.url | user-provided value |
|
||||
| execa.js:34:22:34:45 | cmd + a ... + arg3 | execa.js:8:26:8:32 | req.url | execa.js:34:22:34:45 | cmd + a ... + arg3 | This command line depends on a $@. | execa.js:8:26:8:32 | req.url | user-provided value |
|
||||
| execa.js:34:22:34:45 | cmd + a ... + arg3 | execa.js:9:26:9:32 | req.url | execa.js:34:22:34:45 | cmd + a ... + arg3 | This command line depends on a $@. | execa.js:9:26:9:32 | req.url | user-provided value |
|
||||
| form-parsers.js:9:8:9:39 | "touch ... nalname | form-parsers.js:9:19:9:26 | req.file | form-parsers.js:9:8:9:39 | "touch ... nalname | This command line depends on a $@. | form-parsers.js:9:19:9:26 | req.file | user-provided value |
|
||||
| form-parsers.js:14:10:14:37 | "touch ... nalname | form-parsers.js:13:3:13:11 | req.files | form-parsers.js:14:10:14:37 | "touch ... nalname | This command line depends on a $@. | form-parsers.js:13:3:13:11 | req.files | user-provided value |
|
||||
| form-parsers.js:25:10:25:28 | "touch " + filename | form-parsers.js:24:48:24:55 | filename | form-parsers.js:25:10:25:28 | "touch " + filename | This command line depends on a $@. | form-parsers.js:24:48:24:55 | filename | user-provided value |
|
||||
@@ -112,6 +139,57 @@ edges
|
||||
| execSeries.js:18:34:18:40 | req.url | execSeries.js:18:13:18:47 | require ... , true) | provenance | |
|
||||
| execSeries.js:19:12:19:16 | [cmd] [0] | execSeries.js:13:19:13:26 | commands [0] | provenance | |
|
||||
| execSeries.js:19:13:19:15 | cmd | execSeries.js:19:12:19:16 | [cmd] [0] | provenance | |
|
||||
| execa.js:6:9:6:54 | cmd | execa.js:11:15:11:17 | cmd | provenance | |
|
||||
| execa.js:6:9:6:54 | cmd | execa.js:13:32:13:34 | cmd | provenance | |
|
||||
| execa.js:6:9:6:54 | cmd | execa.js:14:31:14:33 | cmd | provenance | |
|
||||
| execa.js:6:9:6:54 | cmd | execa.js:17:14:17:16 | cmd | provenance | |
|
||||
| execa.js:6:9:6:54 | cmd | execa.js:19:32:19:34 | cmd | provenance | |
|
||||
| execa.js:6:9:6:54 | cmd | execa.js:20:33:20:35 | cmd | provenance | |
|
||||
| execa.js:6:9:6:54 | cmd | execa.js:23:17:23:19 | cmd | provenance | |
|
||||
| execa.js:6:9:6:54 | cmd | execa.js:24:17:24:19 | cmd | provenance | |
|
||||
| execa.js:6:9:6:54 | cmd | execa.js:25:17:25:19 | cmd | provenance | |
|
||||
| execa.js:6:9:6:54 | cmd | execa.js:27:15:27:17 | cmd | provenance | |
|
||||
| execa.js:6:9:6:54 | cmd | execa.js:28:15:28:17 | cmd | provenance | |
|
||||
| execa.js:6:9:6:54 | cmd | execa.js:30:24:30:26 | cmd | provenance | |
|
||||
| execa.js:6:9:6:54 | cmd | execa.js:31:24:31:26 | cmd | provenance | |
|
||||
| execa.js:6:9:6:54 | cmd | execa.js:33:22:33:24 | cmd | provenance | |
|
||||
| execa.js:6:9:6:54 | cmd | execa.js:34:22:34:24 | cmd | provenance | |
|
||||
| execa.js:6:15:6:38 | url.par ... , true) | execa.js:6:9:6:54 | cmd | provenance | |
|
||||
| execa.js:6:25:6:31 | req.url | execa.js:6:15:6:38 | url.par ... , true) | provenance | |
|
||||
| execa.js:7:9:7:53 | arg1 | execa.js:30:30:30:33 | arg1 | provenance | |
|
||||
| execa.js:7:9:7:53 | arg1 | execa.js:31:30:31:33 | arg1 | provenance | |
|
||||
| execa.js:7:9:7:53 | arg1 | execa.js:33:28:33:31 | arg1 | provenance | |
|
||||
| execa.js:7:9:7:53 | arg1 | execa.js:34:28:34:31 | arg1 | provenance | |
|
||||
| execa.js:7:16:7:39 | url.par ... , true) | execa.js:7:9:7:53 | arg1 | provenance | |
|
||||
| execa.js:7:26:7:32 | req.url | execa.js:7:16:7:39 | url.par ... , true) | provenance | |
|
||||
| execa.js:8:9:8:53 | arg2 | execa.js:30:37:30:40 | arg2 | provenance | |
|
||||
| execa.js:8:9:8:53 | arg2 | execa.js:31:37:31:40 | arg2 | provenance | |
|
||||
| execa.js:8:9:8:53 | arg2 | execa.js:33:35:33:38 | arg2 | provenance | |
|
||||
| execa.js:8:9:8:53 | arg2 | execa.js:34:35:34:38 | arg2 | provenance | |
|
||||
| execa.js:8:16:8:39 | url.par ... , true) | execa.js:8:9:8:53 | arg2 | provenance | |
|
||||
| execa.js:8:26:8:32 | req.url | execa.js:8:16:8:39 | url.par ... , true) | provenance | |
|
||||
| execa.js:9:9:9:53 | arg3 | execa.js:30:44:30:47 | arg3 | provenance | |
|
||||
| execa.js:9:9:9:53 | arg3 | execa.js:31:44:31:47 | arg3 | provenance | |
|
||||
| execa.js:9:9:9:53 | arg3 | execa.js:33:42:33:45 | arg3 | provenance | |
|
||||
| execa.js:9:9:9:53 | arg3 | execa.js:34:42:34:45 | arg3 | provenance | |
|
||||
| execa.js:9:16:9:39 | url.par ... , true) | execa.js:9:9:9:53 | arg3 | provenance | |
|
||||
| execa.js:9:26:9:32 | req.url | execa.js:9:16:9:39 | url.par ... , true) | provenance | |
|
||||
| execa.js:30:24:30:26 | cmd | execa.js:30:24:30:47 | cmd + a ... + arg3 | provenance | |
|
||||
| execa.js:30:30:30:33 | arg1 | execa.js:30:24:30:47 | cmd + a ... + arg3 | provenance | |
|
||||
| execa.js:30:37:30:40 | arg2 | execa.js:30:24:30:47 | cmd + a ... + arg3 | provenance | |
|
||||
| execa.js:30:44:30:47 | arg3 | execa.js:30:24:30:47 | cmd + a ... + arg3 | provenance | |
|
||||
| execa.js:31:24:31:26 | cmd | execa.js:31:24:31:47 | cmd + a ... + arg3 | provenance | |
|
||||
| execa.js:31:30:31:33 | arg1 | execa.js:31:24:31:47 | cmd + a ... + arg3 | provenance | |
|
||||
| execa.js:31:37:31:40 | arg2 | execa.js:31:24:31:47 | cmd + a ... + arg3 | provenance | |
|
||||
| execa.js:31:44:31:47 | arg3 | execa.js:31:24:31:47 | cmd + a ... + arg3 | provenance | |
|
||||
| execa.js:33:22:33:24 | cmd | execa.js:33:22:33:45 | cmd + a ... + arg3 | provenance | |
|
||||
| execa.js:33:28:33:31 | arg1 | execa.js:33:22:33:45 | cmd + a ... + arg3 | provenance | |
|
||||
| execa.js:33:35:33:38 | arg2 | execa.js:33:22:33:45 | cmd + a ... + arg3 | provenance | |
|
||||
| execa.js:33:42:33:45 | arg3 | execa.js:33:22:33:45 | cmd + a ... + arg3 | provenance | |
|
||||
| execa.js:34:22:34:24 | cmd | execa.js:34:22:34:45 | cmd + a ... + arg3 | provenance | |
|
||||
| execa.js:34:28:34:31 | arg1 | execa.js:34:22:34:45 | cmd + a ... + arg3 | provenance | |
|
||||
| execa.js:34:35:34:38 | arg2 | execa.js:34:22:34:45 | cmd + a ... + arg3 | provenance | |
|
||||
| execa.js:34:42:34:45 | arg3 | execa.js:34:22:34:45 | cmd + a ... + arg3 | provenance | |
|
||||
| form-parsers.js:9:19:9:26 | req.file | form-parsers.js:9:8:9:39 | "touch ... nalname | provenance | |
|
||||
| form-parsers.js:13:3:13:11 | req.files | form-parsers.js:13:21:13:24 | file | provenance | |
|
||||
| form-parsers.js:13:21:13:24 | file | form-parsers.js:14:21:14:24 | file | provenance | |
|
||||
@@ -216,6 +294,49 @@ nodes
|
||||
| execSeries.js:18:34:18:40 | req.url | semmle.label | req.url |
|
||||
| execSeries.js:19:12:19:16 | [cmd] [0] | semmle.label | [cmd] [0] |
|
||||
| execSeries.js:19:13:19:15 | cmd | semmle.label | cmd |
|
||||
| execa.js:6:9:6:54 | cmd | semmle.label | cmd |
|
||||
| execa.js:6:15:6:38 | url.par ... , true) | semmle.label | url.par ... , true) |
|
||||
| execa.js:6:25:6:31 | req.url | semmle.label | req.url |
|
||||
| execa.js:7:9:7:53 | arg1 | semmle.label | arg1 |
|
||||
| execa.js:7:16:7:39 | url.par ... , true) | semmle.label | url.par ... , true) |
|
||||
| execa.js:7:26:7:32 | req.url | semmle.label | req.url |
|
||||
| execa.js:8:9:8:53 | arg2 | semmle.label | arg2 |
|
||||
| execa.js:8:16:8:39 | url.par ... , true) | semmle.label | url.par ... , true) |
|
||||
| execa.js:8:26:8:32 | req.url | semmle.label | req.url |
|
||||
| execa.js:9:9:9:53 | arg3 | semmle.label | arg3 |
|
||||
| execa.js:9:16:9:39 | url.par ... , true) | semmle.label | url.par ... , true) |
|
||||
| execa.js:9:26:9:32 | req.url | semmle.label | req.url |
|
||||
| execa.js:11:15:11:17 | cmd | semmle.label | cmd |
|
||||
| execa.js:13:32:13:34 | cmd | semmle.label | cmd |
|
||||
| execa.js:14:31:14:33 | cmd | semmle.label | cmd |
|
||||
| execa.js:17:14:17:16 | cmd | semmle.label | cmd |
|
||||
| execa.js:19:32:19:34 | cmd | semmle.label | cmd |
|
||||
| execa.js:20:33:20:35 | cmd | semmle.label | cmd |
|
||||
| execa.js:23:17:23:19 | cmd | semmle.label | cmd |
|
||||
| execa.js:24:17:24:19 | cmd | semmle.label | cmd |
|
||||
| execa.js:25:17:25:19 | cmd | semmle.label | cmd |
|
||||
| execa.js:27:15:27:17 | cmd | semmle.label | cmd |
|
||||
| execa.js:28:15:28:17 | cmd | semmle.label | cmd |
|
||||
| execa.js:30:24:30:26 | cmd | semmle.label | cmd |
|
||||
| execa.js:30:24:30:47 | cmd + a ... + arg3 | semmle.label | cmd + a ... + arg3 |
|
||||
| execa.js:30:30:30:33 | arg1 | semmle.label | arg1 |
|
||||
| execa.js:30:37:30:40 | arg2 | semmle.label | arg2 |
|
||||
| execa.js:30:44:30:47 | arg3 | semmle.label | arg3 |
|
||||
| execa.js:31:24:31:26 | cmd | semmle.label | cmd |
|
||||
| execa.js:31:24:31:47 | cmd + a ... + arg3 | semmle.label | cmd + a ... + arg3 |
|
||||
| execa.js:31:30:31:33 | arg1 | semmle.label | arg1 |
|
||||
| execa.js:31:37:31:40 | arg2 | semmle.label | arg2 |
|
||||
| execa.js:31:44:31:47 | arg3 | semmle.label | arg3 |
|
||||
| execa.js:33:22:33:24 | cmd | semmle.label | cmd |
|
||||
| execa.js:33:22:33:45 | cmd + a ... + arg3 | semmle.label | cmd + a ... + arg3 |
|
||||
| execa.js:33:28:33:31 | arg1 | semmle.label | arg1 |
|
||||
| execa.js:33:35:33:38 | arg2 | semmle.label | arg2 |
|
||||
| execa.js:33:42:33:45 | arg3 | semmle.label | arg3 |
|
||||
| execa.js:34:22:34:24 | cmd | semmle.label | cmd |
|
||||
| execa.js:34:22:34:45 | cmd + a ... + arg3 | semmle.label | cmd + a ... + arg3 |
|
||||
| execa.js:34:28:34:31 | arg1 | semmle.label | arg1 |
|
||||
| execa.js:34:35:34:38 | arg2 | semmle.label | arg2 |
|
||||
| execa.js:34:42:34:45 | arg3 | semmle.label | arg3 |
|
||||
| form-parsers.js:9:8:9:39 | "touch ... nalname | semmle.label | "touch ... nalname |
|
||||
| form-parsers.js:9:19:9:26 | req.file | semmle.label | req.file |
|
||||
| form-parsers.js:13:3:13:11 | req.files | semmle.label | req.files |
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { execa, execaSync, execaCommand, execaCommandSync, $ } from 'execa';
|
||||
import http from 'node:http'
|
||||
import url from 'url'
|
||||
|
||||
http.createServer(async function (req, res) {
|
||||
let cmd = url.parse(req.url, true).query["cmd"][0]; // $Source
|
||||
let arg1 = url.parse(req.url, true).query["arg1"]; // $Source
|
||||
let arg2 = url.parse(req.url, true).query["arg2"]; // $Source
|
||||
let arg3 = url.parse(req.url, true).query["arg3"]; // $Source
|
||||
|
||||
await $`${cmd} ${arg1} ${arg2} ${arg3}`; // $Alert
|
||||
await $`ssh ${arg1} ${arg2} ${arg3}`; // safely escapes variables, preventing shell injection.
|
||||
$({ shell: false }).sync`${cmd} ${arg1} ${arg2} ${arg3}`; // $Alert
|
||||
$({ shell: true }).sync`${cmd} ${arg1} ${arg2} ${arg3}`; // $Alert
|
||||
$({ shell: false }).sync`ssh ${arg1} ${arg2} ${arg3}`; // safely escapes variables, preventing shell injection.
|
||||
|
||||
$.sync`${cmd} ${arg1} ${arg2} ${arg3}`; // $Alert
|
||||
$.sync`ssh ${arg1} ${arg2} ${arg3}`; // safely escapes variables, preventing shell injection.
|
||||
await $({ shell: true })`${cmd} ${arg1} ${arg2} ${arg3}`; // $Alert
|
||||
await $({ shell: false })`${cmd} ${arg1} ${arg2} ${arg3}`; // $Alert
|
||||
await $({ shell: false })`ssh ${arg1} ${arg2} ${arg3}`; // safely escapes variables, preventing shell injection.
|
||||
|
||||
await execa(cmd, [arg1, arg2, arg3]); // $Alert
|
||||
await execa(cmd, { shell: true }); // $Alert
|
||||
await execa(cmd, [arg1, arg2, arg3], { shell: true }); // $Alert
|
||||
|
||||
execaSync(cmd, [arg1, arg2, arg3]); // $Alert
|
||||
execaSync(cmd, [arg1, arg2, arg3], { shell: true }); // $Alert
|
||||
|
||||
await execaCommand(cmd + arg1 + arg2 + arg3); // $Alert
|
||||
await execaCommand(cmd + arg1 + arg2 + arg3, { shell: true }); // $Alert
|
||||
|
||||
execaCommandSync(cmd + arg1 + arg2 + arg3); // $Alert
|
||||
execaCommandSync(cmd + arg1 + arg2 + arg3, { shell: true }); // $Alert
|
||||
});
|
||||
Reference in New Issue
Block a user