Broken Object Level Authorization

Broken Object Level Authorization in Node.js (Express) [CVE-2026-33326]

[Fixed month year] Updated CVE-2026-33326

Overview

Broken Object Level Authorization vulnerabilities allow attackers to access or enumerate resources beyond their permissions. In Keystone for Node.js, CVE-2026-33326 describes how a client can bypass field-level isFilterable access control in findMany queries by passing a cursor. Before the 6.5.2 patch, this could let an attacker determine whether records exist that have a protected field value, enabling information disclosure in multi-tenant or sensitive apps. The vulnerability occurred because the cursor parameter in findMany accepted the same UniqueWhere input type as used for updates and deletes. The fix for CVE-2025-46720 added checks to the where parameter in update/delete operations, but the cursor path in findMany was not patched until version 6.5.2. An attacker could craft a cursor that targets protected fields and coerce the system into revealing existence or details of records the user should not see. Remediation for Node.js/Express apps involves upgrading to the patched version (6.5.2 or later) and enforcing robust authorization at every data access point. Do not rely on client-provided query shapes to enforce access; validate and constrain what the cursor/where inputs can express, and implement server-side owner- or role-based filters to ensure only permitted data is returned. Finally, add tests that attempt to use protected fields in cursors to verify the authorization checks are effective. In practice, apply server-side safeguards in Express routes that use findMany-like operations: upgrade dependencies, validate inputs, enforce authorization in the ORM layer, and test against enum/type-predicate bypass attempts to prevent regressions.

Affected Versions

< 6.5.2

Code Fix Example

Node.js (Express) API Security Remediation
/* Vulnerable pattern (pre-patch) */
app.get('/widgets', async (req, res) => {
  // Vulnerable: client-supplied cursor may include protected field filters
  const cursor = JSON.parse(req.query.cursor || '{}');
  const items = await db.widget.findMany({ cursor, where: cursor });
  res.json(items);
});

/* Fixed pattern (post-patch) */
app.get('/widgets', async (req, res) => {
  const cursor = JSON.parse(req.query.cursor || '{}');
  // Validate and restrict cursor keys; enforce authorization
  const allowedKeys = new Set(['id', 'name', 'category']);
  if (cursor && Object.keys(cursor).some(k => !allowedKeys.has(k))) {
    return res.status(400).json({ error: 'Invalid filter' });
  }
  // Enforce ownership-based access (example)
  const userId = req.user?.id;
  const items = await db.widget.findMany({ where: { ownerId: userId }, cursor });
  res.json(items);
});

CVE References

Choose which optional cookies to allow. You can change this any time.