Skip to content

Commit 5515256

Browse files
authored
Merge pull request #7044 from asgerf/js/proto-pollution-fps
Approved by erik-krogh
2 parents f4704f1 + 712614a commit 5515256

File tree

4 files changed

+49
-1
lines changed

4 files changed

+49
-1
lines changed

javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1145,7 +1145,8 @@ private predicate reachableFromInput(
11451145
DataFlow::Configuration cfg, PathSummary summary
11461146
) {
11471147
callInputStep(f, invk, input, nd, cfg) and
1148-
summary = PathSummary::level()
1148+
summary = PathSummary::level() and
1149+
not cfg.isLabeledBarrier(nd, summary.getEndLabel())
11491150
or
11501151
exists(DataFlow::Node mid, PathSummary oldSummary |
11511152
reachableFromInput(f, invk, input, mid, cfg, oldSummary) and

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,23 @@ class Configuration extends TaintTracking::Configuration {
3232
or
3333
// Concatenating with a string will in practice prevent the string `__proto__` from arising.
3434
node instanceof StringOps::ConcatenationRoot
35+
or
36+
node instanceof DataFlow::ThisNode
37+
or
38+
// Stop at .replace() calls that likely prevent __proto__ from arising
39+
exists(StringReplaceCall replace |
40+
node = replace and
41+
replace.getAReplacedString() = ["_", "p", "r", "o", "t"] and
42+
// Replacing with "_" is likely to be exploitable
43+
not replace.getRawReplacement().getStringValue() = "_" and
44+
(
45+
replace.isGlobal()
46+
or
47+
// Non-global replace with a non-empty string can also prevent __proto__ by
48+
// inserting a chunk of text that doesn't fit anywhere in __proto__
49+
not replace.getRawReplacement().getStringValue() = ""
50+
)
51+
)
3552
}
3653

3754
override predicate isAdditionalFlowStep(

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,16 @@ nodes
2323
| tst.js:45:9:45:11 | obj |
2424
| tst.js:48:9:48:11 | obj |
2525
| tst.js:48:9:48:11 | obj |
26+
| tst.js:78:5:78:37 | obj[req ... ', '')] |
27+
| tst.js:78:5:78:37 | obj[req ... ', '')] |
28+
| tst.js:78:9:78:19 | req.query.x |
29+
| tst.js:78:9:78:19 | req.query.x |
30+
| tst.js:78:9:78:36 | req.que ... _', '') |
31+
| tst.js:81:5:81:46 | obj[req ... g, '')] |
32+
| tst.js:81:5:81:46 | obj[req ... g, '')] |
33+
| tst.js:81:9:81:19 | req.query.x |
34+
| tst.js:81:9:81:19 | req.query.x |
35+
| tst.js:81:9:81:45 | req.que ... /g, '') |
2636
edges
2737
| tst.js:5:9:5:38 | taint | tst.js:8:12:8:16 | taint |
2838
| tst.js:5:9:5:38 | taint | tst.js:9:12:9:16 | taint |
@@ -47,6 +57,14 @@ edges
4757
| tst.js:33:23:33:25 | obj | tst.js:45:9:45:11 | obj |
4858
| tst.js:33:23:33:25 | obj | tst.js:48:9:48:11 | obj |
4959
| tst.js:33:23:33:25 | obj | tst.js:48:9:48:11 | obj |
60+
| tst.js:78:9:78:19 | req.query.x | tst.js:78:9:78:36 | req.que ... _', '') |
61+
| tst.js:78:9:78:19 | req.query.x | tst.js:78:9:78:36 | req.que ... _', '') |
62+
| tst.js:78:9:78:36 | req.que ... _', '') | tst.js:78:5:78:37 | obj[req ... ', '')] |
63+
| tst.js:78:9:78:36 | req.que ... _', '') | tst.js:78:5:78:37 | obj[req ... ', '')] |
64+
| tst.js:81:9:81:19 | req.query.x | tst.js:81:9:81:45 | req.que ... /g, '') |
65+
| tst.js:81:9:81:19 | req.query.x | tst.js:81:9:81:45 | req.que ... /g, '') |
66+
| tst.js:81:9:81:45 | req.que ... /g, '') | tst.js:81:5:81:46 | obj[req ... g, '')] |
67+
| tst.js:81:9:81:45 | req.que ... /g, '') | tst.js:81:5:81:46 | obj[req ... g, '')] |
5068
#select
5169
| 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 | here |
5270
| 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 | here |
@@ -55,3 +73,5 @@ edges
5573
| tst.js:39:9:39:11 | obj | tst.js:5:24:5:37 | req.query.data | tst.js:39:9:39:11 | obj | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | here |
5674
| tst.js:45:9:45:11 | obj | tst.js:5:24:5:37 | req.query.data | tst.js:45:9:45:11 | obj | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | here |
5775
| tst.js:48:9:48:11 | obj | tst.js:5:24:5:37 | req.query.data | tst.js:48:9:48:11 | obj | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | here |
76+
| tst.js:78:5:78:37 | obj[req ... ', '')] | tst.js:78:9:78:19 | req.query.x | tst.js:78:5:78:37 | obj[req ... ', '')] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:78:9:78:19 | req.query.x | here |
77+
| tst.js:81:5:81:46 | obj[req ... g, '')] | tst.js:81:9:81:19 | req.query.x | tst.js:81:5:81:46 | obj[req ... g, '')] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:81:9:81:19 | req.query.x | here |

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,13 @@ class Box {
7171
this.foo = 'bar'; // OK - 'this' won't refer to Object.prototype
7272
}
7373
}
74+
75+
app.get('/foo', (req, res) => {
76+
let obj = {};
77+
obj[req.query.x.replace('_', '-')].x = 'foo'; // OK
78+
obj[req.query.x.replace('_', '')].x = 'foo'; // NOT OK
79+
obj[req.query.x.replace(/_/g, '')].x = 'foo'; // OK
80+
obj[req.query.x.replace(/_/g, '-')].x = 'foo'; // OK
81+
obj[req.query.x.replace(/__proto__/g, '')].x = 'foo'; // NOT OK - "__pr__proto__oto__"
82+
obj[req.query.x.replace('o', '0')].x = 'foo'; // OK
83+
});

0 commit comments

Comments
 (0)