Broken Object Level Authorization

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

[Updated Mar 2026] Updated CVE-2026-33622

Overview

In real-world APIs, Broken Object Level Authorization (BOLA) occurs when an endpoint operates on a specific resource (for example, a browser tab context) without strictly validating that the requester owns or is allowed to modify that exact object. CVE-2026-33622 highlights a concrete instance of this class: PinchTab’s /wait endpoint accepted a user-supplied expression in fn mode and evaluated it in the tab context, bypassing the intended security policy. Although authentication is required to access the API, this vulnerability enables an attacker with a valid server token to execute arbitrary JavaScript within another user's tab, effectively crossing object boundaries at the code-execution layer. The issue stemmed from applying the policy boundary for code evaluation in /evaluate but not similarly for /wait when mode is fn, creating a dangerous mismatch in authorization controls. The public write-up notes that affected releases (v0.8.3 through v0.8.5) allowed this policy bypass, with no patched version available at the time of publication. In Node.js (Express) applications, this pattern can manifest as endpoints that perform actions on resources identified by IDs (tabs, documents, etc.) without ensuring the action is permissible for the specific resource and user, especially when the action can trigger code execution or manipulation of the object’s state. This guide demonstrates the pattern awareness and concrete remediation steps for Node.js/Express code to prevent such BOLA scenarios.

Affected Versions

PinchTab v0.8.3 through v0.8.5

Code Fix Example

Node.js (Express) API Security Remediation
/* Vulnerable pattern (illustrative) */
const express = require('express');
const app = express();
app.use(express.json());

// Simplified in-memory objects (illustrative only)
const tabs = {
  't1': { id: 't1', owner: 'u1', state: {} }
};

app.post('/wait', (req, res) => {
  const { tabId, mode, fn } = req.body;
  const tab = tabs[tabId];
  if (!tab) return res.status(404).json({ error: 'tab not found' });
  // Vulnerable: no object-level authorization check and code is evaluated when mode === 'fn'
  if (mode === 'fn') {
    // Unsafe: evaluates user-supplied code
    const result = Function('return (' + fn + ')')();
    tab.state = { ...tab.state, result };
    return res.json({ ok: true, result });
  }
  // Non-code wait path
  tab.state = { ...tab.state, waiting: true };
  res.json({ ok: true });
});

/* Fixed pattern (illustrative) */
const secureApp = express();
secureApp.use(express.json());
secureApp.post('/wait', (req, res) => {
  const { tabId, mode, fn } = req.body;
  const tab = tabs[tabId];
  if (!tab) return res.status(404).json({ error: 'tab not found' });
  // Enforce object-level authorization: user must own the tab
  const userId = req.user?.id; // assume authentication middleware sets req.user
  if (tab.owner !== userId) return res.status(403).json({ error: 'forbidden' });
  // Apply policy boundary: disallow code execution in wait mode
  if (mode === 'fn') {
    return res.status(400).json({ error: 'Code evaluation is not allowed in wait mode' });
  }
  // Safe wait operation (no eval)
  tab.state = { ...tab.state, waiting: true };
  res.json({ ok: true });
});
app.use('/', secureApp);

// Note: /evaluate should be updated to mirror the same policy boundary for code execution

CVE References

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