Skip to main content
interlace
Plugin: drizzle-securityRules

no-unscoped-mutation

Require a chained `.where()` on Drizzle delete and update builders, so a bulk mutation cannot rewrite or delete every row in the table.

CWE: CWE-284 OWASP: A01:2021 – Broken Access Control

Detects Drizzle bulk mutations that reach every row in the table. This rule is part of eslint-plugin-drizzle-security.

💼 This rule is set to error in the strict config.

Quick Summary

AspectDetails
CWE ReferenceCWE-284 (Improper Access Control)
SeverityHigh (CVSS 7.5)
Auto-Fix❌ No auto-fix available
CategorySecurity

Why this matters

A bulk mutation without a filter is one forgotten clause away from rewriting or deleting the entire table. It type-checks, it passes review, and it usually only shows up once it has run against production data. Drizzle's argument is the table (db.delete(users)), never a filter — the scope always arrives as a chained .where().

❌ Incorrect

// Deletes every row in users
await db.delete(users);

// Rewrites every row
await db.update(users).set({ role: 'admin' });

// returning() is not a filter
await db.delete(users).returning();

✅ Correct

await db.delete(users).where(eq(users.id, id));

await db
  .update(users)
  .set({ active: false })
  .where(eq(users.id, id));

Known limitations

This rule reports only what it can prove. Scope that cannot be read statically is treated as present, so the rule stays silent rather than guessing.

When not to use it

Disable this rule in maintenance scripts, seeders, and test fixtures whose job is to clear a table. Prefer a scoped eslint-disable-next-line on the specific call over switching the rule off for the whole project.

Further reading

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.