Skip to content

Commit cd6f738

Browse files
committed
add mongoose.Types.ObjectId.isValid as a sanitizer-guard for NoSQL injection
1 parent 798f388 commit cd6f738

File tree

4 files changed

+32
-1
lines changed

4 files changed

+32
-1
lines changed

javascript/ql/lib/semmle/javascript/security/TaintedObject.qll

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,22 @@ module TaintedObject {
120120
override predicate sanitizes(boolean outcome, Expr e) { e = x and outcome = polarity }
121121
}
122122

123+
/** A guard that checks whether an input a valid string identifier using `mongoose.Types.ObjectId.isValid` */
124+
class ObjectIdGuard extends SanitizerGuard instanceof API::CallNode {
125+
ObjectIdGuard() {
126+
this =
127+
API::moduleImport("mongoose")
128+
.getMember("Types")
129+
.getMember("ObjectId")
130+
.getMember("isValid")
131+
.getACall()
132+
}
133+
134+
override predicate sanitizes(boolean outcome, Expr e, FlowLabel lbl) {
135+
e = super.getAnArgument().asExpr() and outcome = true and lbl = label()
136+
}
137+
}
138+
123139
/**
124140
* A sanitizer guard that validates an input against a JSON schema.
125141
*/

javascript/ql/test/query-tests/Security/CWE-089/untyped/DatabaseAccesses.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242
| mongoose.js:97:2:97:52 | Documen ... query)) |
4343
| mongoose.js:99:2:99:50 | Documen ... query)) |
4444
| mongoose.js:113:2:113:53 | Documen ... () { }) |
45+
| mongoose.js:134:6:134:55 | Documen ... on(){}) |
46+
| mongoose.js:136:6:136:55 | Documen ... on(){}) |
4547
| mysql.js:8:9:11:47 | connect ... ds) {}) |
4648
| mysql.js:14:9:16:47 | connect ... ds) {}) |
4749
| mysql.js:19:9:20:48 | connect ... ds) {}) |

javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,8 @@ nodes
283283
| mongoose.js:130:16:130:26 | { _id: id } |
284284
| mongoose.js:130:16:130:26 | { _id: id } |
285285
| mongoose.js:130:23:130:24 | id |
286+
| mongoose.js:136:33:136:37 | query |
287+
| mongoose.js:136:33:136:37 | query |
286288
| mongooseJsonParse.js:19:11:19:20 | query |
287289
| mongooseJsonParse.js:19:19:19:20 | {} |
288290
| mongooseJsonParse.js:20:19:20:44 | JSON.pa ... y.data) |
@@ -688,6 +690,8 @@ edges
688690
| mongoose.js:20:11:20:20 | query | mongoose.js:111:14:111:18 | query |
689691
| mongoose.js:20:11:20:20 | query | mongoose.js:113:31:113:35 | query |
690692
| mongoose.js:20:11:20:20 | query | mongoose.js:113:31:113:35 | query |
693+
| mongoose.js:20:11:20:20 | query | mongoose.js:136:33:136:37 | query |
694+
| mongoose.js:20:11:20:20 | query | mongoose.js:136:33:136:37 | query |
691695
| mongoose.js:20:19:20:20 | {} | mongoose.js:20:11:20:20 | query |
692696
| mongoose.js:21:19:21:26 | req.body | mongoose.js:21:19:21:32 | req.body.title |
693697
| mongoose.js:21:19:21:26 | req.body | mongoose.js:21:19:21:32 | req.body.title |
@@ -758,6 +762,8 @@ edges
758762
| mongoose.js:21:19:21:32 | req.body.title | mongoose.js:111:14:111:18 | query |
759763
| mongoose.js:21:19:21:32 | req.body.title | mongoose.js:113:31:113:35 | query |
760764
| mongoose.js:21:19:21:32 | req.body.title | mongoose.js:113:31:113:35 | query |
765+
| mongoose.js:21:19:21:32 | req.body.title | mongoose.js:136:33:136:37 | query |
766+
| mongoose.js:21:19:21:32 | req.body.title | mongoose.js:136:33:136:37 | query |
761767
| mongoose.js:24:25:24:29 | query | mongoose.js:24:24:24:30 | [query] |
762768
| mongoose.js:24:25:24:29 | query | mongoose.js:24:24:24:30 | [query] |
763769
| mongoose.js:115:6:115:22 | id | mongoose.js:123:20:123:21 | id |
@@ -1008,6 +1014,7 @@ edges
10081014
| mongoose.js:128:22:128:25 | cond | mongoose.js:115:32:115:45 | req.query.cond | mongoose.js:128:22:128:25 | cond | This query object depends on a $@. | mongoose.js:115:32:115:45 | req.query.cond | user-provided value |
10091015
| mongoose.js:129:21:129:24 | cond | mongoose.js:115:32:115:45 | req.query.cond | mongoose.js:129:21:129:24 | cond | This query object depends on a $@. | mongoose.js:115:32:115:45 | req.query.cond | user-provided value |
10101016
| mongoose.js:130:16:130:26 | { _id: id } | mongoose.js:115:11:115:22 | req.query.id | mongoose.js:130:16:130:26 | { _id: id } | This query object depends on a $@. | mongoose.js:115:11:115:22 | req.query.id | user-provided value |
1017+
| mongoose.js:136:33:136:37 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:136:33:136:37 | query | This query object depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
10111018
| mongooseJsonParse.js:23:19:23:23 | query | mongooseJsonParse.js:20:30:20:43 | req.query.data | mongooseJsonParse.js:23:19:23:23 | query | This query object depends on a $@. | mongooseJsonParse.js:20:30:20:43 | req.query.data | user-provided value |
10121019
| mongooseModelClient.js:11:16:11:24 | { id: v } | mongooseModelClient.js:10:22:10:29 | req.body | mongooseModelClient.js:11:16:11:24 | { id: v } | This query object depends on a $@. | mongooseModelClient.js:10:22:10:29 | req.body | user-provided value |
10131020
| mongooseModelClient.js:12:16:12:34 | { id: req.body.id } | mongooseModelClient.js:12:22:12:29 | req.body | mongooseModelClient.js:12:16:12:34 | { id: req.body.id } | This query object depends on a $@. | mongooseModelClient.js:12:22:12:29 | req.body | user-provided value |

javascript/ql/test/query-tests/Security/CWE-089/untyped/mongoose.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ app.post('/documents/find', (req, res) => {
104104
new innocent(X, Y, query);
105105

106106
function getQueryConstructor() {
107-
return Mongoose.Query;
107+
return Mongoose.Query;
108108
}
109109

110110
var C = getQueryConstructor();
@@ -129,4 +129,10 @@ app.post('/documents/find', (req, res) => {
129129
Document.updateOne(cond, Y); // NOT OK
130130
Document.find({ _id: id }); // NOT OK
131131
Document.find({ _id: { $eq: id } }); // OK
132+
133+
if (Mongoose.Types.ObjectId.isValid(query)) {
134+
Document.findByIdAndUpdate(query, X, function(){}); // OK - is sanitized
135+
} else {
136+
Document.findByIdAndUpdate(query, X, function(){}); // NOT OK
137+
}
132138
});

0 commit comments

Comments
 (0)