Skip to content

Commit d72d734

Browse files
committed
JS: make NosqlInjection use object taint
1 parent b70f70f commit d72d734

File tree

5 files changed

+62
-8
lines changed

5 files changed

+62
-8
lines changed

javascript/ql/src/Security/CWE-089/SqlInjection.actual

Whitespace-only changes.

javascript/ql/src/semmle/javascript/security/dataflow/NosqlInjection.qll

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55

66
import javascript
7+
import semmle.javascript.security.TaintedObject
78

89
module NosqlInjection {
910
/**
@@ -14,7 +15,16 @@ module NosqlInjection {
1415
/**
1516
* A data flow sink for SQL-injection vulnerabilities.
1617
*/
17-
abstract class Sink extends DataFlow::Node { }
18+
abstract class Sink extends DataFlow::Node {
19+
/**
20+
* Gets a flow label relevant for this sink.
21+
*
22+
* Defaults to deeply tainted objects only.
23+
*/
24+
DataFlow::FlowLabel getAFlowLabel() {
25+
result = TaintedObject::label()
26+
}
27+
}
1828

1929
/**
2030
* A sanitizer for SQL-injection vulnerabilities.
@@ -31,21 +41,29 @@ module NosqlInjection {
3141
source instanceof Source
3242
}
3343

34-
override predicate isSink(DataFlow::Node sink) {
35-
sink instanceof Sink
44+
override predicate isSource(DataFlow::Node source, DataFlow::FlowLabel label) {
45+
TaintedObject::isSource(source, label)
46+
}
47+
48+
override predicate isSink(DataFlow::Node sink, DataFlow::FlowLabel label) {
49+
sink.(Sink).getAFlowLabel() = label
3650
}
3751

3852
override predicate isSanitizer(DataFlow::Node node) {
3953
super.isSanitizer(node) or
4054
node instanceof Sanitizer
4155
}
4256

43-
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
57+
override predicate isAdditionalFlowStep(DataFlow::Node src, DataFlow::Node trg, DataFlow::FlowLabel inlbl, DataFlow::FlowLabel outlbl) {
58+
TaintedObject::step(src, trg, inlbl, outlbl)
59+
or
4460
// additional flow step to track taint through NoSQL query objects
61+
inlbl = TaintedObject::label() and
62+
outlbl = TaintedObject::label() and
4563
exists (NoSQL::Query query, DataFlow::SourceNode queryObj |
4664
queryObj.flowsToExpr(query) and
47-
queryObj.flowsTo(succ) and
48-
pred = queryObj.getAPropertyWrite().getRhs()
65+
queryObj.flowsTo(trg) and
66+
src = queryObj.getAPropertyWrite().getRhs()
4967
)
5068
}
5169
}

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
| mongodb.js:18:16:18:20 | query | This query depends on $@. | mongodb.js:13:19:13:26 | req.body | a user-provided value |
2-
| mongodb.js:39:16:39:20 | query | This query depends on $@. | mongodb.js:34:19:34:33 | req.query.title | a user-provided value |
32
| mongoose.js:27:20:27:24 | query | This query depends on $@. | mongoose.js:21:19:21:26 | req.body | a user-provided value |
43
| mongoose.js:30:25:30:29 | query | This query depends on $@. | mongoose.js:21:19:21:26 | req.body | a user-provided value |
54
| mongoose.js:33:24:33:28 | query | This query depends on $@. | mongoose.js:21:19:21:26 | req.body | a user-provided value |

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ app.post('/documents/find', (req, res) => {
1616

1717
// NOT OK: query is tainted by user-provided object value
1818
doc.find(query);
19+
20+
// OK: user-data is coerced to a string
21+
doc.find({ title: '' + query.body.title });
22+
23+
// OK: throws unless user-data is a string
24+
doc.find({ title: query.body.title.substr(1) });
1925
});
2026
});
2127

@@ -36,6 +42,6 @@ app.post('/documents/find', (req, res) => {
3642
let doc = db.collection('doc');
3743

3844
// NOT OK: query is tainted by user-provided object value
39-
doc.find(query);
45+
doc.find(query); // Not currently detected
4046
});
4147
});
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
const express = require('express'),
2+
mongodb = require('mongodb'),
3+
bodyParser = require('body-parser');
4+
5+
const MongoClient = mongodb.MongoClient;
6+
7+
const app = express();
8+
9+
app.use(bodyParser.urlencoded({ extended: false }));
10+
11+
app.post('/documents/find', (req, res) => {
12+
const query = {};
13+
query.title = req.body.title;
14+
MongoClient.connect('mongodb://localhost:27017/test', (err, db) => {
15+
let doc = db.collection('doc');
16+
17+
// OK: req.body is safe
18+
doc.find(query);
19+
});
20+
});
21+
22+
app.post('/documents/find', (req, res) => {
23+
const query = {};
24+
query.title = req.query.title;
25+
MongoClient.connect('mongodb://localhost:27017/test', (err, db) => {
26+
let doc = db.collection('doc');
27+
28+
// NOT OK: regardless of body parser, query value is still tainted
29+
doc.find(query);
30+
});
31+
});

0 commit comments

Comments
 (0)