Skip to main content

Free 30-min security demo  — We'll scan your real code and show live findings, no commitment Book Now

Offensive360
ZeroDays CVE-2023-46308
Critical CVE-2023-46308 CVSS 9.8 Plotly.js JavaScript

Prototype Pollution in Plotly.js Plot API Calls via expandObjectPaths

CVE-2023-46308 is a critical prototype pollution vulnerability in Plotly.js before v2.25.2 allowing __proto__ manipulation through plot API calls, enabling arbitrary code execution.

Offensive360 Research Team
Affects: <2.25.2
Source Code

Overview

CVE-2023-46308 represents a critical prototype pollution vulnerability discovered in Plotly.js, the popular open-source JavaScript visualization library, affecting all versions prior to 2.25.2. The vulnerability exists in the plot API call handling mechanism, specifically within the expandObjectPaths and nestedProperty functions, which fail to properly sanitize user-supplied input before processing nested object property assignments. When an attacker supplies a malicious payload containing the __proto__ key, they can manipulate the prototype chain of JavaScript objects, potentially polluting the global Object prototype and leading to arbitrary code execution in the context of the rendering application.

This vulnerability is particularly severe because Plotly.js is widely deployed across data visualization dashboards, business intelligence platforms, and web applications that rely on interactive charting capabilities. The attack surface is extensive—any web application embedding Plotly.js and accepting user-controlled data for visualization is potentially vulnerable. The vulnerability bypasses traditional input validation mechanisms because it operates at the object construction level, exploiting a fundamental JavaScript feature rather than a scripting injection point. With a CVSS score of 9.8, this vulnerability warrants immediate patching across all enterprise deployments.

Technical Analysis

Prototype pollution in JavaScript occurs when an attacker can write properties to Object.prototype or other built-in prototypes, effectively modifying the behavior of all objects that inherit from that prototype. In Plotly.js, the vulnerability manifests in the object path expansion logic that processes configuration objects passed to plotting functions.

The vulnerable code pattern involves recursive object traversal without proper key validation:

// Vulnerable pattern in expandObjectPaths/nestedProperty
function setNestedProperty(obj, path, value) {
  const keys = path.split('.');
  let current = obj;
  
  for (let i = 0; i < keys.length - 1; i++) {
    const key = keys[i];
    // No validation of dangerous keys like __proto__, constructor, prototype
    if (!(key in current)) {
      current[key] = {};
    }
    current = current[key];
  }
  
  current[keys[keys.length - 1]] = value;
}

An attacker can exploit this by providing a configuration object like:

const maliciousConfig = {
  "__proto__.isAdmin": true,
  "__proto__.canExecute": "alert('pwned')"
};

Plotly.newPlot('plotDiv', data, layout, maliciousConfig);

This payload causes the setNestedProperty function to traverse into Object.prototype through the __proto__ reference, effectively poisoning the global prototype chain. Any subsequent object creation or property access can be weaponized by an attacker who has pre-populated dangerous properties on the prototype.

The root cause stems from insufficient input sanitization and the absence of prototype chain protection mechanisms. The expandObjectPaths function processes user-supplied layout and configuration objects directly without filtering dangerous property names. JavaScript’s in operator and property assignment operations don’t distinguish between legitimate nested properties and prototype pollution attempts.

Impact

The practical impact of CVE-2023-46308 extends beyond theoretical code execution. Real-world exploitation scenarios include:

Authentication Bypass: An attacker can pollute Object.prototype with properties that authentication middleware checks, such as isAuthenticated: true or role: 'admin', bypassing access controls implemented in the application layer.

Arbitrary Code Execution: By polluting the prototype with constructor or function properties, attackers can alter how objects are instantiated and executed, potentially leading to function replacement and arbitrary code execution within the browser context.

Data Exfiltration: In server-side rendering scenarios using Node.js with Plotly, prototype pollution can compromise the entire application process, allowing attackers to extract sensitive data or credentials from the server environment.

DOM Manipulation: Polluting prototypes used in DOM operations can enable XSS-style attacks that modify page behavior, inject malicious content, or perform unauthorized actions on behalf of legitimate users.

The vulnerability affects any web application that:

  • Accepts user-controlled data for visualization with Plotly.js
  • Passes unsanitized configuration objects to Plotly functions
  • Runs Plotly.js in environments where global prototype pollution carries security implications

How to Fix It

The immediate remediation is to upgrade Plotly.js to version 2.25.2 or later. The official release includes patches to both the expandObjectPaths and nestedProperty functions that implement prototype chain protection.

Upgrade Instructions:

# Using npm
npm install [email protected] --save

# Using yarn
yarn add [email protected]

# Using bower
bower install plotly.js#2.25.2

For CDN-based deployments, update script references:

<!-- Old -->
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>

<!-- New -->
<script src="https://cdn.plot.ly/plotly-2.25.2.min.js"></script>

The patched version implements explicit key validation:

// Fixed pattern with prototype chain protection
function setNestedProperty(obj, path, value) {
  const keys = path.split('.');
  const dangerousKeys = ['__proto__', 'constructor', 'prototype'];
  
  let current = obj;
  
  for (let i = 0; i < keys.length - 1; i++) {
    const key = keys[i];
    
    // Reject dangerous property names
    if (dangerousKeys.includes(key)) {
      throw new Error(`Unsafe property name: ${key}`);
    }
    
    // Use Object.prototype.hasOwnProperty for safer checking
    if (!Object.prototype.hasOwnProperty.call(current, key)) {
      current[key] = {};
    }
    current = current[key];
  }
  
  current[keys[keys.length - 1]] = value;
}

Additionally, implement input validation at the application level:

function sanitizePlotlyConfig(config) {
  const dangerousPatterns = /(__proto__|constructor|prototype)/;
  
  for (const key in config) {
    if (dangerousPatterns.test(key)) {
      delete config[key];
      console.warn(`Removed dangerous configuration key: ${key}`);
    }
  }
  
  return config;
}

// Usage
const userConfig = getUserProvidedConfig();
const safeConfig = sanitizePlotlyConfig(userConfig);
Plotly.newPlot('plotDiv', data, layout, safeConfig);

Our Take

CVE-2023-46308 exemplifies why prototype pollution remains a critical vulnerability class in JavaScript ecosystems. Unlike memory-safety vulnerabilities in compiled languages, prototype pollution exploits fundamental language semantics, making it particularly difficult to detect without explicit defensive coding practices.

From an enterprise perspective, this vulnerability should trigger immediate inventory and patching operations. Organizations using Plotly.js in production dashboards, data applications, or embedded analytics should prioritize upgrading to 2.25.2 within their standard patch cycles. The CVSS 9.8 score reflects the severity appropriately—this is not a low-impact information disclosure but a direct pathway to code execution.

For development teams, this vulnerability reinforces the importance of treating all user-supplied data as untrusted, even when passed to seemingly benign visualization libraries. The presence of prototype pollution vulnerabilities in widely-used libraries indicates that runtime security assumptions about “safe” APIs should be continuously validated.

Detection with SAST

Static Application Security Testing (SAST) tools can identify prototype pollution vulnerabilities by analyzing control flow around object property assignment operations. Specifically, detection should focus on:

Vulnerable Pattern Recognition: Identifying instances where user-controlled data flows into object property assignment without validation of property names (CWE-1321: Improperly Controlled Modification of Object Prototype Attributes).

Key Blacklist Analysis: Flagging assignment operations involving __proto__, constructor, or prototype keys without explicit sanitization checks.

Path Traversal in Object Construction: Detecting string-based property path parsing (split by .) that directly accesses object properties without filtering dangerous names.

An enterprise SAST solution should detect patterns like:

// Flagged: Direct assignment from user path
obj[userPath] = userValue;

// Flagged: Recursive descent without key validation
function traverse(obj, keys) {
  for (const key of keys) {
    obj = obj[key]; // Dangerous if keys come from user input
  }
}

// Flagged: Configuration object spread without sanitization
Plotly.newPlot(div, data, userProvidedLayout);

Proper SAST configuration should require:

  • Explicit whitelisting of allowed property names
  • Use of Object.prototype.hasOwnProperty checks
  • Separation of data flow from prototype-chain-sensitive operations

References

#prototype-pollution #javascript #plotly #code-execution

Detect this vulnerability class in your codebase

Offensive360 SAST scans your source code for CVE-2023-46308-class vulnerabilities and thousands of other patterns — across 60+ languages.