---
id: "CVE-2026-118247"
kind: "vulnerability"
aliases: ["CVE-2026-118247", "GHSA-c75x-9x9h-28rm"]
title: "Highcharts Dashboards / Grid Pro: `DataTable.setCell` and column writers treat `__proto__` as an existing column."
severity: "medium"
cvss: 6.9
epss: null
exploited: false
status: "open"
category: "package"
products: [{"name": "@highcharts/dashboards", "fixed": ["4.2.2"], "affected": ">= 1.0.0, <= 4.2.1", "ecosystem": "npm"}, {"name": "@highcharts/grid-pro", "fixed": ["3.2.0"], "affected": ">= 2.0.0, <= 3.1.0", "ecosystem": "npm"}]
ecosystems: ["npm"]
tags: ["cwe-1321", "dashboards", "frontend", "grid-pro", "highcharts", "javascript", "package"]
not_affected_if: ["auth-required", "no-datatable-writes", "static-charts"]
published_at: "2026-10-28T18:17:15+00:00"
first_seen_at: "2026-10-28T18:34:33+00:00"
updated_at: "2026-10-28T19:56:29+00:00"
sources: [{"source": "nvd", "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-118247", "first_seen_at": "2026-10-28T18:34:33+00:00"}, {"source": "ghsa", "url": "https://github.com/advisories/GHSA-c75x-9x9h-28rm", "first_seen_at": "2026-10-28T19:56:29+00:00"}]
---

# Highcharts Dashboards / Grid Pro: `DataTable.setCell` and column writers treat `__proto__` as an existing column.

Highcharts DataTable single-cell writers accept __proto__ or constructor as column IDs, so a write can put values onto Object.prototype or the Object constructor. Those inherited values then change array and object reads and for-in loops across the whole page, which can alter control flow or break rendering.

_Summary, check and fix written by a model from the sources; labelled as such._

## Affected

- npm:@highcharts/dashboards affected >= 1.0.0, <= 4.2.1 fixed 4.2.2
- npm:@highcharts/grid-pro affected >= 2.0.0, <= 3.1.0 fixed 3.2.0

## How to check

- Check the installed Highcharts Dashboards version (`npm ls @highcharts/dashboards` or `unknown` if not from npm) and whether it is below `4.2.2`.
- Check the installed Highcharts Grid Pro version (`npm ls @highcharts/grid-pro` or `unknown`) and whether it is below `3.2.0`.
- Check whether connector write-back or Grid Pro cell editing lets users or data sources set column IDs.

## Fix

- Upgrade Highcharts Dashboards to `4.2.2` or later.
- Upgrade Highcharts Grid Pro to `3.2.0` or later.

## Timeline

- 2026-10-28T18:34:33+00:00 nvd https://nvd.nist.gov/vuln/detail/CVE-2026-118247
- 2026-10-28T19:56:29+00:00 ghsa https://github.com/advisories/GHSA-c75x-9x9h-28rm

## From the sources

### Impact
When a single cell value is written to a data table, the column id is used as a key on the table's column collection without filtering, so a column id of `__proto__` resolves to `Object.prototype`, is taken for an existing column, and the write lands at the row index on `Object.prototype`; a column id of `constructor` writes onto the `Object` constructor in the same way. In Dashboards releases that still supported column aliases, the id is first resolved through the alias map, so a literal `__proto__` id is redirected harmlessly there, while an alias whose target is `__proto__`, which the connector's data table options accept, reaches the same write. An attacker who controls the column id can thereby set integer-keyed properties on `Object.prototype`, one per row index, holding the written value; named properties cannot be reached through these paths, because the row index is a number. The integer keys still affect every object in the page: reading an out-of-range index on any array or plain object returns the injected value, and `for...in` over any object yields the injected keys, so unrelated code can take branches it should not using attacker-supplied data, and rendering can fail as a side effect. In Dashboards the write is reached through the Highcharts component's connector write-back, which is enabled by default through `allowConnectorUpdate` and takes the column id from the component's `columnAssignment`, or in the earliest releases from the series name, when a point is updated; in Grid Pro it is reached through cell editing in earlier releases and remains reachable through the public `DataTable` API. Exploitation requires that untrusted data reach the board or Grid configuration and that a point be updated or a cell edited, or that the application write a cell with an untrusted column id.

Consequences beyond disruption depend on the consuming application.

### Workarounds
Two measures are needed. The first closes the paths that reach `Object.prototype`; the second covers table construction, which cannot be patched from outside the library.

#### 1. Strip unsafe keys from untrusted column data

Apply this wherever column data enters your application, before it is handed to a grid, board or `DataTable`. This is the only measure that covers the `DataTable` constructor.

```js
function stripUnsafeColumnIds(columns) {
    for (const key of ['__proto__', 'constructor']) {
        if (Object.prototype.hasOwnProperty.call(columns, key)) {
            delete columns[key];
        }
    }

    return columns;
}

Grid.grid('container', {
    data: { columns: stripUnsafeColumnIds(untrustedColumns) }
});
```

#### 2. Patch the DataTable methods

Needed if your application passes column ids that originate from untrusted input to `setCell`, `changeColumnId` or `setColumns`. Run it once at startup.

```js
{
    const { DataTable } = Grid; // Or Dashboards

    if (!DataTable) {
        throw new Error('Patch loaded before Grid');
    }

    const unsafe = (id) => id === '__proto__' || id === 'constructor';

    const setCell = DataTable.prototype.setCell;
    DataTable.prototype.setCell = function (columnId, ...rest) {
        if (unsafe(columnId)) {
            return;
        }
        return setCell.call(this, columnId, ...rest);
    };

    const changeColumnId = DataTable.prototype.changeColumnId;
    DataTable.prototype.changeColumnId = function (columnId, newColumnId) {
        if (unsafe(columnId) || unsafe(newColumnId)) {
            return false;
        }
        return changeColumnId.call(this, columnId, newColumnId);
    };

    const setColumns = DataTable.prototype.setColumns;
    DataTable.prototype.setColumns = function (columns, ...rest) {
        const safe = {};

        for (const key of Object.keys(columns || {})) {
            if (!unsafe(key)) {
                safe[key] = columns[key];
            }
        }

        return setColumns.call(this, safe, ...rest);
    };
}
```

For npm, take `DataTable` from your import instead of the global — it is exported by Grid Lite, Grid Pro and Dashboards alike.

### Obtaining the patched version
The patched releases are available from all the usual distribution channels. With npm, upgrade the affected package to the patched version listed above, for example `npm install @highcharts/dashboards@4.2.2`. If you load Highcharts Dashboards from a CDN, use a versioned URL, for example `https://code.highcharts.com/dashboards/4.2.2/dashboards.js`. For more on each installation method, see the [installation guide](https://www.highcharts.com/docs/dashboards/installation).

---

_Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai_
