Impact The Highcharts component uses the value of chartConstructor to select a function from the Highcharts namespace and invokes it with the component's container element and its chartOptions, without checking the value against the supported chart constructors. An attacker who controls the dashboard configuration can therefore name any other public member of that namespace; utility functions in that namespace copy the supplied options onto the container element as properties or attributes, so markup and event handlers carried in chartOptions land in the page and run in the origin of the page hosting the dashboard. Exploitation requires that untrusted data reach the Dashboards component configuration. No user interaction is required beyond rendering the board.
Workarounds If your board configuration comes from a source you do not fully control, paste this snippet right after importing libraries: ts (function () { const HighchartsComponent = Dashboards && Dashboards.HighchartsComponent;
if (!HighchartsComponent || !HighchartsComponent.prototype.createChart) { return; // HighchartsComponent not loaded }
const allowedConstructors = [ 'chart', 'stockChart', 'mapChart', 'ganttChart' ]; const originalCreateChart = HighchartsComponent.prototype.createChart;
HighchartsComponent.prototype.createChart = function () { if (allowedConstructors.indexOf(this.chartConstructor) === -1) { this.chartConstructor = 'chart'; }
return originalCreateChart.apply(this, arguments); }; }());
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.
Impact The edit mode fullscreen labels are parsed as HTML when the corresponding control is rendered. An attacker who controls the Dashboards language options can supply markup carrying event handlers, which then execute in the origin of the page hosting the dashboard. Exploitation requires that untrusted data reach the Dashboards language configuration and that the affected control be rendered. Script running this way has the same access to page data, storage and session state as the hosting application.
Workarounds
Until you can upgrade, replace Fullscreen.prototype.setButtonText atapplication startup. The replacement is the original method with textContent in place of innerHTML.
If you install Dashboards from npm
Import the module once, before any board is created.
js import Fullscreen from '@highcharts/dashboards/es-modules/Dashboards/EditMode/Fullscreen.js';
Fullscreen.prototype.setButtonText = function () { const editMode = this.board.editMode, button = editMode?.tools.contextMenu?.items.viewFullscreen;
if (button?.innerElement) { const lang = editMode.lang;
button.innerElement.textContent = (this.isOpen ? lang.exitFullscreen : lang.viewFullscreen) || ''; } };
If you load Dashboards from a <script> tag
The same replacement, taking Fullscreen from the global namespace. It must run after modules/layout and before any board is created.
html <script src="https://code.highcharts.com/dashboards/dashboards.js"></script> <script src="https://code.highcharts.com/dashboards/modules/layout.js"></script> <script> { const Fullscreen = window.Dashboards?.Fullscreen;
Fullscreen.prototype.setButtonText = function () { const editMode = this.board.editMode, button = editMode?.tools.contextMenu?.items.viewFullscreen;
if (button?.innerElement) { const lang = editMode.lang;
button.innerElement.textContent = (this.isOpen ? lang.exitFullscreen : lang.viewFullscreen) || ''; } }; } </script>
If your application does not need Edit Mode, leaving it disabled (editMode.enabled: false) removes the exposure as well. The same applies to the context menu — the label is only reachable when viewFullscreen is among editMode.contextMenu.items.
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.3. If you load Highcharts Dashboards from a CDN, use a versioned URL, for example https://code.highcharts.com/dashboards/4.2.3/dashboards.js. For more on each installation method, see the installation guide.
Impact The component language strings shown in the edit mode sidebar are parsed as HTML rather than inserted as text. An attacker who controls those language options can supply markup carrying event handlers, which execute in the origin of the page hosting the dashboard once the sidebar renders the affected labels. Exploitation requires that untrusted data reach the Dashboards language configuration and that a user open the edit mode sidebar.
Workarounds
Until you can upgrade, patch EditMode once at application startup. The hook runs before the sidebar is built, so the labels are already harmless by the time they are rendered. Assigning a string to textContent and reading back innerHTML turns any markup into literal text, which is exactly what the fixed version renders.
After importing the module, use this code snipped once, before any board is created.
js { const EditMode = window.Dashboards?.EditMode; const escapeHTML = (text) => text .replace(/&/g, '&') .replace(/</g, '<') .replace(/>/g, '>'); const patched = new WeakSet(); const originalInit = EditMode.prototype.init;
EditMode.prototype.init = function (...args) { const labels = this.lang?.sidebar;
if (labels && !patched.has(this)) { patched.add(this);
for (const key of Object.keys(labels)) { if (typeof labels[key] === 'string') { labels[key] = escapeHTML(labels[key]); } } }
return originalInit.apply(this, args); }; }
If your application does not need Edit Mode, leaving it disabled (editMode.enabled: false) removes the exposure as well.
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.3. If you load Highcharts Dashboards from a CDN, use a versioned URL, for example https://code.highcharts.com/dashboards/4.2.3/dashboards.js. For more on each installation method, see the installation guide.
Highcharts JS is a JavaScript charting library based on SVG. From 1.0.0 until 4.2.2, Highcharts Dashboards edit mode walks editableOptions propertyPath segments without rejecting prototype-related keys. An attacker who controls editableOptions can provide a path through proto or constructor, and a user's edit writes the configured value onto Object.prototype. The property then becomes visible to JavaScript throughout the page, with consequences determined by the consuming application. This issue is fixed in 4.2.2.
Impact The data cursor keeps its listener and state bookkeeping in plain objects keyed by data table id, so a table id of proto supplied in the dataPool connector configuration resolves to Object.prototype when a sync registers its listeners, and the registration creates a property on Object.prototype named after the sync state and holding the internal listener array. The property name begins with a state prefix fixed by the library, to which the sync group option can append an attacker-chosen suffix, and its value is the library's own listener array, but the property is enumerable and visible on every object in the page, and emitting a lasting state for that table id then throws, so syncs that keep lasting states, such as extremes, stop working. Exploitation requires that untrusted data reach the Dashboards configuration and that a sync such as highlight or extremes be enabled on a component using that table, which the same configuration can do. No user interaction is required beyond loading the board.
The injected property is inert until other code on the page reads or enumerates it, so consequences depend on the consuming application.
Workarounds Drop-in fix - for any Highcharts Dashboards versions affected (1.0.0-4.2.1). Add the below IIFE to run after Highcharts Dashboards is loaded.
!function(t){if(t&&t.DataCursor&&t.DataTable){var e=t.DataCursor.prototype,r=t.DataTable.prototype;e.tableIdPollutionPatched||(e.tableIdPollutionPatched=!0,null!==Object.getPrototypeOf((new t.DataCursor).listenerMap)&&['addListener','emitCursor','remitCursor','removeListener'].forEach(function(t){var r=e[t];r&&(e[t]=function(){var t;return t=this,['listenerMap','stateMap'].forEach(function(e){var r,a=t[e];a&&null!==Object.getPrototypeOf(a)&&(r=Object.create(null),Object.keys(a).forEach(function(t){r[t]=a[t]}),t[e]=r)}),r.apply(this,arguments)})}),'proto'!==new t.DataTable({id:'proto'}).id||Object.getOwnPropertyDescriptor(r,'id')||Object.defineProperty(r,'id',{configurable:!0,enumerable:!0,get:function(){return this.safeTableId},set:function(e){'proto'!==e&&'constructor'!==e||(e=t.uniqueKey(),this.autoId=!0),this.safeTableId=e}}))}}(Dashboards);
Live demo & testing
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.