Skip to content

Commit 0c9c9bb

Browse files
committed
detect library input when the arguments object is converted to an array
1 parent fa9e9dd commit 0c9c9bb

File tree

3 files changed

+66
-4
lines changed

3 files changed

+66
-4
lines changed

javascript/ql/lib/semmle/javascript/PackageExports.qll

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,28 @@ DataFlow::SourceNode getALibraryInputParameter() {
1818
|
1919
result = func.getParameter(any(int arg | arg >= bound))
2020
or
21-
exists(DataFlow::PropRead read |
22-
not read.getPropertyName() = "length" and
23-
result = read and
24-
read.getBase() = func.getFunction().getArgumentsVariable().getAnAccess().flow()
21+
result = getAnArgumentsRead(func.getFunction())
22+
)
23+
}
24+
25+
private DataFlow::SourceNode getAnArgumentsRead(Function func) {
26+
exists(DataFlow::PropRead read |
27+
not read.getPropertyName() = "length" and
28+
result = read
29+
|
30+
read.getBase() = func.getArgumentsVariable().getAnAccess().flow()
31+
or
32+
exists(DataFlow::MethodCallNode call |
33+
call =
34+
DataFlow::globalVarRef("Array")
35+
.getAPropertyRead("prototype")
36+
.getAPropertyRead("slice")
37+
.getAMethodCall("call")
38+
or
39+
call = DataFlow::globalVarRef("Array").getAMethodCall("from")
40+
|
41+
call.getArgument(0) = func.getArgumentsVariable().getAnAccess().flow() and
42+
call.flowsTo(read.getBase())
2543
)
2644
)
2745
}

javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,20 @@ nodes
3838
| lib.js:26:10:26:21 | obj[path[0]] |
3939
| lib.js:26:14:26:17 | path |
4040
| lib.js:26:14:26:20 | path[0] |
41+
| lib.js:32:7:32:20 | path |
42+
| lib.js:32:14:32:20 | args[1] |
43+
| lib.js:32:14:32:20 | args[1] |
44+
| lib.js:34:3:34:14 | obj[path[0]] |
45+
| lib.js:34:3:34:14 | obj[path[0]] |
46+
| lib.js:34:7:34:10 | path |
47+
| lib.js:34:7:34:13 | path[0] |
48+
| lib.js:40:7:40:20 | path |
49+
| lib.js:40:14:40:20 | args[1] |
50+
| lib.js:40:14:40:20 | args[1] |
51+
| lib.js:42:3:42:14 | obj[path[0]] |
52+
| lib.js:42:3:42:14 | obj[path[0]] |
53+
| lib.js:42:7:42:10 | path |
54+
| lib.js:42:7:42:13 | path[0] |
4155
| tst.js:5:9:5:38 | taint |
4256
| tst.js:5:17:5:38 | String( ... y.data) |
4357
| tst.js:5:24:5:37 | req.query.data |
@@ -115,6 +129,18 @@ edges
115129
| lib.js:26:14:26:17 | path | lib.js:26:14:26:20 | path[0] |
116130
| lib.js:26:14:26:20 | path[0] | lib.js:26:10:26:21 | obj[path[0]] |
117131
| lib.js:26:14:26:20 | path[0] | lib.js:26:10:26:21 | obj[path[0]] |
132+
| lib.js:32:7:32:20 | path | lib.js:34:7:34:10 | path |
133+
| lib.js:32:14:32:20 | args[1] | lib.js:32:7:32:20 | path |
134+
| lib.js:32:14:32:20 | args[1] | lib.js:32:7:32:20 | path |
135+
| lib.js:34:7:34:10 | path | lib.js:34:7:34:13 | path[0] |
136+
| lib.js:34:7:34:13 | path[0] | lib.js:34:3:34:14 | obj[path[0]] |
137+
| lib.js:34:7:34:13 | path[0] | lib.js:34:3:34:14 | obj[path[0]] |
138+
| lib.js:40:7:40:20 | path | lib.js:42:7:42:10 | path |
139+
| lib.js:40:14:40:20 | args[1] | lib.js:40:7:40:20 | path |
140+
| lib.js:40:14:40:20 | args[1] | lib.js:40:7:40:20 | path |
141+
| lib.js:42:7:42:10 | path | lib.js:42:7:42:13 | path[0] |
142+
| lib.js:42:7:42:13 | path[0] | lib.js:42:3:42:14 | obj[path[0]] |
143+
| lib.js:42:7:42:13 | path[0] | lib.js:42:3:42:14 | obj[path[0]] |
118144
| tst.js:5:9:5:38 | taint | tst.js:8:12:8:16 | taint |
119145
| tst.js:5:9:5:38 | taint | tst.js:9:12:9:16 | taint |
120146
| tst.js:5:9:5:38 | taint | tst.js:12:25:12:29 | taint |
@@ -156,6 +182,8 @@ edges
156182
| lib.js:15:3:15:14 | obj[path[0]] | lib.js:14:38:14:41 | path | lib.js:15:3:15:14 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:14:38:14:41 | path | library input |
157183
| lib.js:22:3:22:14 | obj[path[0]] | lib.js:20:14:20:25 | arguments[1] | lib.js:22:3:22:14 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:20:14:20:25 | arguments[1] | library input |
158184
| lib.js:26:10:26:21 | obj[path[0]] | lib.js:25:44:25:47 | path | lib.js:26:10:26:21 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:25:44:25:47 | path | library input |
185+
| lib.js:34:3:34:14 | obj[path[0]] | lib.js:32:14:32:20 | args[1] | lib.js:34:3:34:14 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:32:14:32:20 | args[1] | library input |
186+
| lib.js:42:3:42:14 | obj[path[0]] | lib.js:40:14:40:20 | args[1] | lib.js:42:3:42:14 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:40:14:40:20 | args[1] | library input |
159187
| tst.js:8:5:8:17 | object[taint] | tst.js:5:24:5:37 | req.query.data | tst.js:8:5:8:17 | object[taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | user controlled input |
160188
| tst.js:9:5:9:17 | object[taint] | tst.js:5:24:5:37 | req.query.data | tst.js:9:5:9:17 | object[taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | user controlled input |
161189
| tst.js:14:5:14:32 | unsafeG ... taint) | tst.js:5:24:5:37 | req.query.data | tst.js:14:5:14:32 | unsafeG ... taint) | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | user controlled input |

javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,19 @@ module.exports.setWithArgs = function() {
2525
module.exports.usedInTest = function (obj, path, value) {
2626
return obj[path[0]][path[1]] = value; // NOT OK
2727
}
28+
29+
module.exports.setWithArgs2 = function() {
30+
const args = Array.prototype.slice.call(arguments);
31+
var obj = args[0];
32+
var path = args[1];
33+
var value = args[2];
34+
obj[path[0]][path[1]] = value; // NOT OK
35+
}
36+
37+
module.exports.setWithArgs3 = function() {
38+
const args = Array.from(arguments);
39+
var obj = args[0];
40+
var path = args[1];
41+
var value = args[2];
42+
obj[path[0]][path[1]] = value; // NOT OK
43+
}

0 commit comments

Comments
 (0)