no-bypass-middleware
Detects Mongoose operations that bypass middleware hooks (pre/post hooks).
⚠️ This rule warns by default in the recommended config.
Quick Summary
| Aspect | Details |
|---|---|
| CWE Reference | CWE-284 (Improper Access Control) |
| OWASP | A01:2021 - Broken Access Control |
| Severity | Medium (CVSS: 5.3) |
| Category | Security |
Error Message Format
The rule provides LLM-optimized error messages (Compact 2-line format) with actionable security guidance:
🔒 CWE-284 OWASP:A01 CVSS:7.5 | Improper Access Control detected | HIGH
Fix: Review and apply the recommended fix | https://owasp.org/Top10/A01_2021/Message Components
| Component | Purpose | Example |
|---|---|---|
| Risk Standards | Security benchmarks | CWE-284 OWASP:A01 CVSS:7.5 |
| Issue Description | Specific vulnerability | Improper Access Control detected |
| Severity & Compliance | Impact assessment | HIGH |
| Fix Instruction | Actionable remediation | Follow the remediation steps below |
| Technical Truth | Official reference | OWASP Top 10 |
Rule Details
Some Mongoose methods bypass middleware hooks, which may skip:
- Password hashing
- Audit logging
- Access control checks
- Data sanitization
Methods That Bypass Middleware
| Method | Runs save Middleware? |
|---|---|
Model.updateOne() | ❌ No |
Model.updateMany() | ❌ No |
Model.findOneAndUpdate() | ❌ No |
Model.findByIdAndUpdate() | ❌ No |
document.save() | ✅ Yes |
❌ Incorrect
// Bypasses pre('save') middleware - password won't be hashed!
User.findByIdAndUpdate(id, { password: 'newpassword' });
// Bypasses all document middleware
User.updateMany({ role: 'user' }, { verified: true });✅ Correct
// Use findOne + save to trigger middleware
const user = await User.findById(id);
user.password = 'newpassword';
await user.save(); // pre('save') runs, password gets hashed
// Or use update hooks (if implemented)
userSchema.pre('findOneAndUpdate', function () {
// Hash password here if modified
});Receiver Requirement
updateOne(), findOneAndUpdate(), deleteMany() and friends are are not MongoDB-exclusive. This rule only fires when the receiver is
plausibly a Mongo model, collection or database handle — a PascalCase model
identifier (User.find(...)), a name ending in Model/Collection/Schema
(this.userModel, the idiomatic @InjectModel() injection), a bare
db/model/collection, a db.collection('users') chain, or a value bound
to a mongodb/mongoose import. PascalCase counts only for a module-level
identifier, not for a property reached through this — this.UserRepository
is an injected service, not a model.
It stays silent on:
- Generic repository wrappers (
this.birdRepository.updateOne(...)) — only a Mongoose model has pre/post middleware to bypass in the first place. - Other ORMs that share the method vocabulary.
Known False Negatives
Dynamic Method Calls
// ❌ NOT DETECTED
const method = 'updateOne';
Model[method]({ ... });When Not To Use It
- When you've implemented
pre('updateOne')and similar hooks - For operations that intentionally skip middleware
References
⚙️ Options
| Option | Type | Default | Description |
|---|---|---|---|
allowInTests | boolean | true | Skip 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.