Skip to content

Commit 2dedfb3

Browse files
committed
remove paths without unmatched returns from js/prototype-polluting-assignment
1 parent 0c9c9bb commit 2dedfb3

File tree

3 files changed

+75
-1
lines changed

3 files changed

+75
-1
lines changed

javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,14 @@ class Configuration extends TaintTracking::Configuration {
6969
inlbl.isTaint() and
7070
outlbl instanceof ObjectPrototype
7171
)
72+
or
73+
DataFlow::localFieldStep(pred, succ) and inlbl = outlbl
74+
}
75+
76+
// override to require that there is a path without unmatched return steps
77+
override predicate hasFlowPath(DataFlow::SourcePathNode source, DataFlow::SinkPathNode sink) {
78+
super.hasFlowPath(source, sink) and
79+
DataFlow::hasPathWithoutUnmatchedReturn(source, sink)
7280
}
7381

7482
override predicate isLabeledBarrier(DataFlow::Node node, DataFlow::FlowLabel lbl) {

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

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,24 @@ nodes
5252
| lib.js:42:3:42:14 | obj[path[0]] |
5353
| lib.js:42:7:42:10 | path |
5454
| lib.js:42:7:42:13 | path[0] |
55+
| lib.js:45:13:45:13 | s |
56+
| lib.js:45:13:45:13 | s |
57+
| lib.js:46:10:46:10 | s |
58+
| lib.js:52:9:52:22 | path |
59+
| lib.js:52:16:52:22 | id("x") |
60+
| lib.js:55:11:55:22 | obj[path[0]] |
61+
| lib.js:55:11:55:22 | obj[path[0]] |
62+
| lib.js:55:15:55:18 | path |
63+
| lib.js:55:15:55:21 | path[0] |
64+
| lib.js:59:18:59:18 | s |
65+
| lib.js:59:18:59:18 | s |
66+
| lib.js:61:17:61:17 | s |
67+
| lib.js:68:11:68:26 | path |
68+
| lib.js:68:18:68:26 | this.path |
69+
| lib.js:70:13:70:24 | obj[path[0]] |
70+
| lib.js:70:13:70:24 | obj[path[0]] |
71+
| lib.js:70:17:70:20 | path |
72+
| lib.js:70:17:70:23 | path[0] |
5573
| tst.js:5:9:5:38 | taint |
5674
| tst.js:5:17:5:38 | String( ... y.data) |
5775
| tst.js:5:24:5:37 | req.query.data |
@@ -141,6 +159,22 @@ edges
141159
| lib.js:42:7:42:10 | path | lib.js:42:7:42:13 | path[0] |
142160
| lib.js:42:7:42:13 | path[0] | lib.js:42:3:42:14 | obj[path[0]] |
143161
| lib.js:42:7:42:13 | path[0] | lib.js:42:3:42:14 | obj[path[0]] |
162+
| lib.js:45:13:45:13 | s | lib.js:46:10:46:10 | s |
163+
| lib.js:45:13:45:13 | s | lib.js:46:10:46:10 | s |
164+
| lib.js:46:10:46:10 | s | lib.js:52:16:52:22 | id("x") |
165+
| lib.js:52:9:52:22 | path | lib.js:55:15:55:18 | path |
166+
| lib.js:52:16:52:22 | id("x") | lib.js:52:9:52:22 | path |
167+
| lib.js:55:15:55:18 | path | lib.js:55:15:55:21 | path[0] |
168+
| lib.js:55:15:55:21 | path[0] | lib.js:55:11:55:22 | obj[path[0]] |
169+
| lib.js:55:15:55:21 | path[0] | lib.js:55:11:55:22 | obj[path[0]] |
170+
| lib.js:59:18:59:18 | s | lib.js:61:17:61:17 | s |
171+
| lib.js:59:18:59:18 | s | lib.js:61:17:61:17 | s |
172+
| lib.js:61:17:61:17 | s | lib.js:68:18:68:26 | this.path |
173+
| lib.js:68:11:68:26 | path | lib.js:70:17:70:20 | path |
174+
| lib.js:68:18:68:26 | this.path | lib.js:68:11:68:26 | path |
175+
| lib.js:70:17:70:20 | path | lib.js:70:17:70:23 | path[0] |
176+
| lib.js:70:17:70:23 | path[0] | lib.js:70:13:70:24 | obj[path[0]] |
177+
| lib.js:70:17:70:23 | path[0] | lib.js:70:13:70:24 | obj[path[0]] |
144178
| tst.js:5:9:5:38 | taint | tst.js:8:12:8:16 | taint |
145179
| tst.js:5:9:5:38 | taint | tst.js:9:12:9:16 | taint |
146180
| tst.js:5:9:5:38 | taint | tst.js:12:25:12:29 | taint |
@@ -184,6 +218,7 @@ edges
184218
| 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 |
185219
| 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 |
186220
| 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 |
221+
| lib.js:70:13:70:24 | obj[path[0]] | lib.js:59:18:59:18 | s | lib.js:70:13:70:24 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:59:18:59:18 | s | library input |
187222
| 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 |
188223
| 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 |
189224
| 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: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,35 @@ module.exports.setWithArgs3 = function() {
4040
var path = args[1];
4141
var value = args[2];
4242
obj[path[0]][path[1]] = value; // NOT OK
43-
}
43+
}
44+
45+
function id(s) {
46+
return s;
47+
}
48+
49+
module.exports.id = id;
50+
51+
module.exports.notVulnerable = function () {
52+
const path = id("x");
53+
const value = id("y");
54+
const obj = id("z");
55+
return (obj[path[0]][path[1]] = value); // OK
56+
}
57+
58+
class Foo {
59+
constructor(o, s, v) {
60+
this.obj = o;
61+
this.path = s;
62+
this.value = v;
63+
}
64+
65+
doXss() {
66+
// not called here, but still bad.
67+
const obj = this.obj;
68+
const path = this.path;
69+
const value = this.value;
70+
return (obj[path[0]][path[1]] = value); // NOT OK
71+
}
72+
}
73+
74+
module.exports.Foo = Foo;

0 commit comments

Comments
 (0)