Broken Object Property Level Authorization

Broken Object Property Authorization in Node.js (Express) [CVE-2026-33981]

[Fixed 2026-07] Updated CVE-2026-33981

Overview

CVE-2026-33981 describes a scenario in changedetection.io where pre-update logic allowed attacker-controlled expressions to access environment data via the jq env builtin, leaking sensitive variables such as SALTED_PASS, PLAYWRIGHT_DRIVER_URL, and HTTP_PROXY. This is categorized under CWE-200 (Exposure of Sensitive Information). The root risk is that an unauthenticated user (or an authenticated user if a password is not configured) could exfiltrate server environment details by manipulating the input that selects what data to read. The patch for changedetection.io was applied in version 0.54.7 to close this exposure. The real-world impact is data leakage and potential follow-on attacks that rely on exposed secrets or infrastructure endpoints.

Affected Versions

0.54.6 and earlier (CVE-2026-33981)

Code Fix Example

Node.js (Express) API Security Remediation
Vulnerable pattern (Node.js/Express):
const express = require('express');
const app = express();

const data = {
  user: { id: 1, name: 'Alice' },
  // Mimics an environment-like snapshot that should not be exposed
  envSnapshot: process.env
};

// User-supplied path selects a property to return
app.get('/watch', (req, res) => {
  const fieldPath = req.query.field; // e.g., 'envSnapshot.HTTP_PROXY' or 'user.name'
  const value = fieldPath
    ? fieldPath.split('.').reduce((acc, key) => (acc && acc[key] !== undefined) ? acc[key] : undefined, data)
    : data;
  res.json({ value });
});

app.listen(3000, () => console.log('Server running'));

---
Fixed pattern (Node.js/Express):
const express = require('express');
const app = express();

const data = {
  user: { id: 1, name: 'Alice' }
};

// Define a strict allowlist of accessible paths
const ALLOWED_PATHS = new Set(['user.id', 'user.name']);

function resolvePathSafe(obj, path) {
  if (!path || !ALLOWED_PATHS.has(path)) return undefined;
  return path.split('.').reduce((acc, key) => acc && acc[key], obj);
}

app.get('/watch', (req, res) => {
  const fieldPath = req.query.field;
  const value = resolvePathSafe(data, fieldPath);
  res.json({ value });
});

app.listen(3000, () => console.log('Server running'));

CVE References

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