Skip to content

Commit 1547cd0

Browse files
committed
added inline tests, move to experimental dir
1 parent 2c4d2d3 commit 1547cd0

File tree

10 files changed

+140
-236
lines changed

10 files changed

+140
-236
lines changed

javascript/ql/lib/javascript.qll

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,6 @@ import semmle.javascript.frameworks.Request
123123
import semmle.javascript.frameworks.RxJS
124124
import semmle.javascript.frameworks.ServerLess
125125
import semmle.javascript.frameworks.ShellJS
126-
import semmle.javascript.frameworks.Execa
127126
import semmle.javascript.frameworks.Snapdragon
128127
import semmle.javascript.frameworks.SystemCommandExecutors
129128
import semmle.javascript.frameworks.SQL

javascript/ql/lib/semmle/javascript/frameworks/Execa.qll renamed to javascript/ql/src/experimental/semmle/javascript/Execa.qll

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
*/
44

55
import javascript
6-
import semmle.javascript.security.dataflow.RequestForgeryCustomizations
76

87
/**
98
* Provide model for [Execa](https://github.com/sindresorhus/execa) package
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
passingPositiveTests
2+
| PASSED | CommandInjection | tests.js:11:46:11:70 | // test ... jection |
3+
| PASSED | CommandInjection | tests.js:12:43:12:67 | // test ... jection |
4+
| PASSED | CommandInjection | tests.js:13:63:13:87 | // test ... jection |
5+
| PASSED | CommandInjection | tests.js:14:62:14:86 | // test ... jection |
6+
| PASSED | CommandInjection | tests.js:15:60:15:84 | // test ... jection |
7+
| PASSED | CommandInjection | tests.js:17:45:17:69 | // test ... jection |
8+
| PASSED | CommandInjection | tests.js:18:42:18:66 | // test ... jection |
9+
| PASSED | CommandInjection | tests.js:19:62:19:86 | // test ... jection |
10+
| PASSED | CommandInjection | tests.js:20:63:20:87 | // test ... jection |
11+
| PASSED | CommandInjection | tests.js:21:60:21:84 | // test ... jection |
12+
| PASSED | CommandInjection | tests.js:23:43:23:67 | // test ... jection |
13+
| PASSED | CommandInjection | tests.js:24:40:24:64 | // test ... jection |
14+
| PASSED | CommandInjection | tests.js:25:40:25:64 | // test ... jection |
15+
| PASSED | CommandInjection | tests.js:26:60:26:84 | // test ... jection |
16+
| PASSED | CommandInjection | tests.js:28:41:28:65 | // test ... jection |
17+
| PASSED | CommandInjection | tests.js:29:58:29:82 | // test ... jection |
18+
| PASSED | CommandInjection | tests.js:31:51:31:75 | // test ... jection |
19+
| PASSED | CommandInjection | tests.js:32:68:32:92 | // test ... jection |
20+
| PASSED | CommandInjection | tests.js:34:49:34:73 | // test ... jection |
21+
| PASSED | CommandInjection | tests.js:35:66:35:90 | // test ... jection |
22+
failingPositiveTests
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { execa, execaSync, execaCommand, execaCommandSync, $ } from 'execa';
2+
import http from 'node:http'
3+
import url from 'url'
4+
5+
http.createServer(async function (req, res) {
6+
let cmd = url.parse(req.url, true).query["cmd"][0];
7+
let arg1 = url.parse(req.url, true).query["arg1"];
8+
let arg2 = url.parse(req.url, true).query["arg2"];
9+
let arg3 = url.parse(req.url, true).query["arg3"];
10+
11+
await $`${cmd} ${arg1} ${arg2} ${arg3}`; // test: CommandInjection
12+
await $`ssh ${arg1} ${arg2} ${arg3}`; // test: CommandInjection
13+
$({ shell: false }).sync`${cmd} ${arg1} ${arg2} ${arg3}`; // test: CommandInjection
14+
$({ shell: true }).sync`${cmd} ${arg1} ${arg2} ${arg3}`; // test: CommandInjection
15+
$({ shell: false }).sync`ssh ${arg1} ${arg2} ${arg3}`; // test: CommandInjection
16+
17+
$.sync`${cmd} ${arg1} ${arg2} ${arg3}`; // test: CommandInjection
18+
$.sync`ssh ${arg1} ${arg2} ${arg3}`; // test: CommandInjection
19+
await $({ shell: true })`${cmd} ${arg1} ${arg2} ${arg3}` // test: CommandInjection
20+
await $({ shell: false })`${cmd} ${arg1} ${arg2} ${arg3}` // test: CommandInjection
21+
await $({ shell: false })`ssh ${arg1} ${arg2} ${arg3}` // test: CommandInjection
22+
23+
await execa(cmd, [arg1, arg2, arg3]); // test: CommandInjection
24+
await execa(cmd, { shell: true }); // test: CommandInjection
25+
await execa(cmd, { shell: true }); // test: CommandInjection
26+
await execa(cmd, [arg1, arg2, arg3], { shell: true }); // test: CommandInjection
27+
28+
execaSync(cmd, [arg1, arg2, arg3]); // test: CommandInjection
29+
execaSync(cmd, [arg1, arg2, arg3], { shell: true }); // test: CommandInjection
30+
31+
await execaCommand(cmd + arg1 + arg2 + arg3); // test: CommandInjection
32+
await execaCommand(cmd + arg1 + arg2 + arg3, { shell: true }); // test: CommandInjection
33+
34+
execaCommandSync(cmd + arg1 + arg2 + arg3); // test: CommandInjection
35+
execaCommandSync(cmd + arg1 + arg2 + arg3, { shell: true }); // test: CommandInjection
36+
});
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import javascript
2+
3+
class InlineTest extends LineComment {
4+
string tests;
5+
6+
InlineTest() { tests = this.getText().regexpCapture("\\s*test:(.*)", 1) }
7+
8+
string getPositiveTest() {
9+
result = tests.trim().splitAt(",").trim() and not result.matches("!%")
10+
}
11+
12+
predicate hasPositiveTest(string test) { test = this.getPositiveTest() }
13+
14+
predicate inNode(DataFlow::Node n) {
15+
this.getLocation().getFile() = n.getFile() and
16+
this.getLocation().getStartLine() = n.getStartLine()
17+
}
18+
}
19+
20+
import experimental.semmle.javascript.Execa
21+
22+
query predicate passingPositiveTests(string res, string expectation, InlineTest t) {
23+
res = "PASSED" and
24+
t.hasPositiveTest(expectation) and
25+
expectation = "CommandInjection" and
26+
exists(SystemCommandExecution n |
27+
t.inNode(n.getArgumentList()) or t.inNode(n.getACommandArgument())
28+
)
29+
}
30+
31+
query predicate failingPositiveTests(string res, string expectation, InlineTest t) {
32+
res = "FAILED" and
33+
t.hasPositiveTest(expectation) and
34+
expectation = "CommandInjection" and
35+
not exists(SystemCommandExecution n |
36+
t.inNode(n.getArgumentList()) or t.inNode(n.getACommandArgument())
37+
)
38+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
passingPositiveTests
2+
| PASSED | PathInjection | tests.js:9:43:9:64 | // test ... jection |
3+
| PASSED | PathInjection | tests.js:12:50:12:71 | // test ... jection |
4+
| PASSED | PathInjection | tests.js:15:61:15:82 | // test ... jection |
5+
| PASSED | PathInjection | tests.js:18:73:18:94 | // test ... jection |
6+
failingPositiveTests

javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/execa.js renamed to javascript/ql/test/experimental/Execa/PathInjection/tests.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ http.createServer(async function (req, res) {
66
let filePath = url.parse(req.url, true).query["filePath"][0];
77

88
// Piping to stdin from a file
9-
await $({ inputFile: filePath })`cat` // NOT OK
9+
await $({ inputFile: filePath })`cat` // test: PathInjection
1010

1111
// Piping to stdin from a file
12-
await execa('cat', { inputFile: filePath }); // NOT OK
12+
await execa('cat', { inputFile: filePath }); // test: PathInjection
1313

1414
// Piping Stdout to file
15-
await execa('echo', ['example3']).pipeStdout(filePath); // NOT OK
15+
await execa('echo', ['example3']).pipeStdout(filePath); // test: PathInjection
1616

1717
// Piping all of command output to file
18-
await execa('echo', ['example4'], { all: true }).pipeAll(filePath); // NOT OK
18+
await execa('echo', ['example4'], { all: true }).pipeAll(filePath); // test: PathInjection
1919
});
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import javascript
2+
3+
class InlineTest extends LineComment {
4+
string tests;
5+
6+
InlineTest() { tests = this.getText().regexpCapture("\\s*test:(.*)", 1) }
7+
8+
string getPositiveTest() {
9+
result = tests.trim().splitAt(",").trim() and not result.matches("!%")
10+
}
11+
12+
predicate hasPositiveTest(string test) { test = this.getPositiveTest() }
13+
14+
predicate inNode(DataFlow::Node n) {
15+
this.getLocation().getFile() = n.getFile() and
16+
this.getLocation().getStartLine() = n.getStartLine()
17+
}
18+
}
19+
20+
import experimental.semmle.javascript.Execa
21+
22+
query predicate passingPositiveTests(string res, string expectation, InlineTest t) {
23+
res = "PASSED" and
24+
t.hasPositiveTest(expectation) and
25+
expectation = "PathInjection" and
26+
exists(FileSystemReadAccess n | t.inNode(n.getAPathArgument()))
27+
}
28+
29+
query predicate failingPositiveTests(string res, string expectation, InlineTest t) {
30+
res = "FAILED" and
31+
t.hasPositiveTest(expectation) and
32+
expectation = "PathInjection" and
33+
not exists(FileSystemReadAccess n | t.inNode(n.getAPathArgument()))
34+
}

0 commit comments

Comments
 (0)