NoSQL Injection detection for MongoDB, Redis, CouchDB, and Elasticsearch
Detects NoSQL injection vulnerabilities where user-controlled input manipulates NoSQL database queries. Unlike SQL injection, NoSQL injection exploits operator injection, JSON structure manipulation, and JavaScript execution within query contexts. Covers MongoDB, Redis, CouchDB, DynamoDB, and Elasticsearch.
Called by sc-orchestrator during Phase 2. Runs when NoSQL databases are detected in the architecture.
**/*.ts, **/*.js, **/*.py, **/*.php, **/*.java, **/*.go, **/*.cs,
**/models/*, **/controllers/*, **/services/*, **/repositories/*,
**/*mongo*, **/*redis*, **/*elastic*, **/*couch*, **/*dynamo*
# MongoDB
"find(", "findOne(", "findOneAndUpdate(", "findOneAndDelete(",
"aggregate(", "updateOne(", "updateMany(", "deleteOne(", "deleteMany(",
"$where", "$regex", "$gt", "$gte", "$lt", "$lte", "$ne", "$in", "$nin",
"$or", "$and", "$not", "$exists", "$expr",
"MongoClient", "mongoose.model", "collection.find"
# Redis
"redis.get(", "redis.set(", "redis.eval(", "redis.send_command(",
"EVAL ", "EVALSHA", "redis.call("
# Elasticsearch
"client.search(", "client.index(", "query_string",
"script_score", "painless", "elasticsearch"
# CouchDB
"_find", "mango", "cloudant"
Sources: HTTP request body (JSON), query parameters, headers, cookies
Sinks:
collection.find(), collection.findOne(), Model.find(), aggregate() — when query object is constructed from user inputEVAL with user input in script, redis.send_command() with dynamic commandsquery_string query with user input, script fields with user input$gt, $ne, $regex, $where) into the query?$where or $function expressions?mongo-sanitize or express-mongo-sanitize?.find() with raw objectsMongoTemplate with Criteria API is safe; raw query strings are not// VULNERABLE: User input directly in query object
app.post('/login', async (req, res) => {
const user = await User.findOne({
username: req.body.username,
password: req.body.password
});
});
// Attack: POST {"username":"admin","password":{"$ne":""}}
// This returns any user where password is not empty — bypasses auth
// SAFE: Validate input types
app.post('/login', async (req, res) => {
if (typeof req.body.username !== 'string' || typeof req.body.password !== 'string') {
return res.status(400).json({ error: 'Invalid input' });
}
const user = await User.findOne({
username: req.body.username,
password: req.body.password
});
});
// VULNERABLE: User input in $where JavaScript expression
const results = await collection.find({
$where: `this.category == '${req.query.category}'`
});
// Attack: ?category=' || true || '
// Executes arbitrary JavaScript on the server
// SAFE: Use standard query operators
const results = await collection.find({
category: req.query.category
});
# VULNERABLE: User input in Redis Lua script
script = f"return redis.call('get', '{user_input}')"
result = redis_client.eval(script, 0)
# SAFE: Use parameterized Redis commands
result = redis_client.get(user_input)
// VULNERABLE: User input in query_string
const results = await client.search({
query: {
query_string: {
query: req.query.search // User can inject Lucene syntax
}
}
});
// SAFE: Use match query instead
const results = await client.search({
query: {
match: {
content: req.query.search
}
}
});
$where/$function with user input{"$ne": ""} as the {parameter} to bypass equality check.mongo-sanitize, or use an ORM that prevents operator injection.