GHSA-c75x-9x9h-28rm
Highcharts Dashboards / Grid Pro: `DataTable.setCell` and column writers treat `__proto__` as an existing column.
Packages
| Ecosystem | Package | Affected | Fixed in |
|---|---|---|---|
| npm | @highcharts/dashboards | >= 1.0.0, < 4.2.2 | 4.2.2 |
| npm | @highcharts/grid-pro | >= 2.0.0, < 3.2.0 | 3.2.0 |
Details
As written in the advisory (Markdown source, shown as plain text).
### 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/[email protected]`. 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).
References
External links from the advisory. They lead to third-party sites.
- https://github.com/highcharts/highcharts/security/advisories/GHSA-c75x-9x9h-28rm
- https://nvd.nist.gov/vuln/detail/CVE-2026-118247
- https://github.com/highcharts/highcharts/commit/02dd9349aeb6f4cc9c98806120d917ed316b4c74
- https://github.com/highcharts/highcharts
- https://github.com/highcharts/highcharts/releases/tag/dashboards-v4.2.2
- https://github.com/highcharts/highcharts/releases/tag/grid-v3.2.0