A class for creating Content Security Policy (CSP)-compliant evaluators of JavaScript expressions.
Content Security Policy is a browser security mechanism that can disallow dynamically compiled code, including
code generated with JavaScript Function constructors.
Starting with JET 20.0.6, the default JET expression evaluator is CSP-compliant and uses
CspExpressionEvaluator. To provide an additional global scope for evaluating expressions, create and pass a
CspExpressionEvaluator instance to the Config.setExpressionEvaluator()
method before applying knockout bindings in the application for the first time.
Any extra context required for evaluating expressions can be passed to the object constructor using the globalScope property.
Expression text must be trusted application code. This evaluator is CSP-compliant because it does not use dynamic code compilation; it is
not a security sandbox. Restricting globalScope does not make untrusted expression text safe because expressions can access
capabilities from binding contexts, including inherited members and, where supplied, $element and related DOM APIs. This
evaluator restricts known prototype-chain access routes in its default capabilities, but applications remain responsible for the
capabilities they expose.
const extraScope = { Date: Date };
Config.setExpressionEvaluator(new CspExpressionEvaluator({ globalScope: extraScope }));
Expressions supported by the JET CspExpressionEvaluator
- Identifiers, e.g.
[[value]].
- Members, e.g.
[[router.stateId]].
- Literals, e.g.
[['abc']].
- Function callbacks, e.g.
[[getColor('customer', id)]].
- Unary operators are limited to '-', '+', '~', '!' and '...', e.g.
[[-100]].
- Instanceof or typeof operators such as
'date instanceof Date'
- Binary operators, e.g.
[[value + '.png']].
- Exponentiation (**) such as
'3 ** 4'
- Logical operators, e.g.
[[a && b]] or [[a || b]].
- Nullish coalescing operator (??) such as
'value ?? "default value"'
- Conditional or ternary operators, e.g.
[[test ? consequent : alternate]].
- Optional chaining operators, e.g.
[[a?.b]].
- Array literals, e.g.
[a, b, c].
- Object literals, e.g.
[[{'selection_state': selected}]].
- 'new' operator such as
'new Object()'
- Regular expressions in the form of explicit RegExp objects such as
[[testString.match(new RegExp('abc', 'i'))]]
- Template literals except tagged templates, e.g.
[[`Hello, ${user}!`]]
- Functions are limited to a single statement, e.g.
function(){return 'abc'} or
function(x, y){return x + y}.
- Arrow functions are limited to a single statement, e.g.
() => 'abc', () => {return 'abc'},
(x, y) => {return x + y} or
[1, 2, 3].map(item => item + 1)
Expression limitations:
The following syntax and behaviors are unsupported or restricted in expressions:
- Expression text must be trusted application code. This evaluator avoids dynamic code compilation for CSP compliance, but it is not a security sandbox. Expressions can invoke functions and access non-restricted values from binding contexts, including inherited members and, where supplied,
$element and related DOM APIs, as well as globalScope.
- To prevent prototype-chain access, access to properties named
'constructor', '__proto__', and 'prototype' is read-only and supported only as own, non-accessor, non-function data properties, including when accessed with bracket notation or as an identifier. A 'prototype' property cannot be accessed on a function. For these names, a Proxy's data descriptor value is used instead of its get trap result.
- Inherited
'__lookupGetter__', '__lookupSetter__', '__defineGetter__', and '__defineSetter__' prototype helpers are not supported. Own data properties with these names are supported.
- An object-literal
'__proto__' property is created as an own data property and does not change the object's prototype.
- The default
Object global does not support assign or the reflection methods create, defineProperty, defineProperties, getOwnPropertyDescriptor, getOwnPropertyDescriptors, getPrototypeOf, or setPrototypeOf.
- Assignment operators of any type, such as
'=', '+=', or '|=', are generally unsupported.
JET and Knockout may use internal assignment expressions to implement two-way binding writeback; applications should not rely on
assignment being accepted in expression text.
- Blocks of code such as
'if (...){}'
- Comma operator (,) such as
'(expr1, expr2)'
- in operator such as
'prop in testObject'
- Increment/decrement operators such as
'x++' or 'x--'
- Inline regular expressions such as
'testString.match(/abc/i)'
- Spread operator (...) such as
'sum(...arrayValue)'
- Tagged templates such as
'myTag`Hello, ${user}!`'
- await/async keywords such as
'await myAsyncTask()'