Skip to main content
interlace
Plugin: mongodb-securityRules

no-operator-injection

Detects MongoDB operator injection attacks where user input is passed directly as query values, allowing attackers to...

Detects MongoDB operator injection attacks where user input is passed directly as query values, allowing attackers to inject operators like $ne, $gt, $or to bypass authentication or exfiltrate data.

⚠️ This rule errors by default in the recommended config.

Quick Summary

AspectDetails
CWE ReferenceCWE-943 (NoSQL Injection)
OWASPA03:2021 - Injection
SeverityCritical (CVSS: 9.1)
CategorySecurity
ESLint MCP✅ Optimized for AI assistant integration

Value & investment case

Why this rule pays for itself. Framework: cicd-impact/philosophy.md.

DimensionValue
CWECWE-943 — Improper Neutralization of Special Elements in Data Query Logic (NoSQL injection, CVSS 9.1)
Feedback-loop tierEditor / pre-commit (sub-second) — cheapest layer per the feedback-loop hierarchy
Defensive-layer leverage~10× cheaper than unit-test · ~1,000× cheaper than production rollback · 10,000+× cheaper than customer disclosure (cost-ratio anchors)
Niche relevanceCritical: fintech, healthtech, B2B SaaS (any MongoDB-backed multi-tenant app) · High: marketplaces, infra/devtools · Medium: B2C
Investor-frame impactNoSQL operator injection ($ne, $gt, $or injection) → authentication bypass + data exfiltration. The MongoDB-specific equivalent of SQL injection; same class of disclosure event. Catch at lint-time prevents the entire attack family.

Read also: philosophy.md §investor-frame · niche-presets.json · analyzer-evaluation-framework.md

Rule Details

This rule specifically targets operator injection - a subset of NoSQL injection where attackers submit objects with MongoDB operators instead of expected primitive values.

Attack Scenario

// Expected: { username: "john", password: "secret123" }
// Attacker sends: { username: "admin", password: { "$ne": "" } }

// This query:
db.users.findOne({ username: req.body.username, password: req.body.password });

// Becomes:
db.users.findOne({ username: 'admin', password: { $ne: '' } });
// Matches admin user with ANY non-empty password - authentication bypass!

❌ Incorrect

const filter = { $ne: req.body.value };

✅ Correct

// Force value comparison with $eq
User.findOne({ email: { $eq: req.body.email } });

// Cast to primitive type
User.findOne({ email: String(req.body.email) });

// Validate input is not an object
if (typeof req.body.email !== 'string') throw new Error('Invalid input');
User.findOne({ email: req.body.email });

Known False Negatives

Object Spread

// ❌ NOT DETECTED - spreads entire body into query
User.findOne({ ...req.body });

Dynamic Property Access

// ❌ NOT DETECTED - dynamic key assignment
const query = {};
query[field] = value;

Aggregation Pipelines

// ❌ NOT DETECTED - complex pipeline structure
User.aggregate([{ $match: req.body.filter }]);

When Not To Use It

  • When using Mongoose schema with strict validation that rejects object values
  • When all input is validated through a strict JSON schema (e.g., Zod, Joi)
  • In test files with intentionally vulnerable patterns

References

⚙️ Options

OptionTypeDefaultDescription
allowInTestsbooleantrueSkip this rule in *.test.* / *.spec.* files

Did this rule catch something? Star the repo to get new CWE coverage as we ship it — or follow the AI-code-security benchmarks behind these rules.