angular's nicest part extracted as a standalone module for the browser and node.
angular-expressions exposes a .compile()-method which can be used to compile evaluable expressions:
var expressions = require("angular-expressions");
evaluate = expressions.compile("1 + 1");
evaluate(); // returns 2You can also set and get values on a given scope:
evaluate = expressions.compile("name");
scope = { name: "Jenny" };
evaluate(scope); // returns 'Jenny'
evaluate = expressions.compile("ship.pirate.name = 'Störtebeker'");
evaluate(scope); // won't throw an error because angular's expressions are forgiving
console.log(scope.ship.pirate.name); // prints 'Störtebeker'For assigning values, you can also use .assign():
evaluate = expressions.compile("ship.pirate.name");
evaluate.assign(scope, "Störtebeker");
console.log(scope.ship.pirate.name); // prints 'Störtebeker'Check out their readme for further information.
Angular provides a mechanism to define filters on expressions:
expressions.filters.uppercase = (input) => input.toUpperCase();
expr = expressions.compile("'arr' | uppercase");
expr(); // returns 'ARR'Arguments are evaluated against the scope:
expressions.filters.currency = (input, currency, digits) => {
input = input.toFixed(digits);
if (currency === "EUR") {
return input + "€";
} else {
return input + "$";
}
};
expr = expressions.compile("1.2345 | currency:selectedCurrency:2");
expr({
selectedCurrency: "EUR",
}); // returns '1.23€'If you need an isolated filters object, this can be achieved by setting the filters attribute in the options argument.
var isolatedFilters = {
transform: (input) => input.toLowerCase(),
};
var resultOne = expressions.compile("'Foo Bar' | transform", {
filters: isolatedFilters,
});
console.log(resultOne()); // prints 'foo bar'Compiles src and returns a function evaluate(). The compiled function is cached under compile.cache which is stored as an LRUCache which prevents the cache to exceed a max size (256 cache items by default) to speed up further calls.
Compiles also export the AST.
Example output of: compile("tmp + 1").ast
{ type: 'Program',
body:
[ { type: 'ExpressionStatement',
expression:
{ type: 'Identifier',
name: 'tmp',
constant: false,
toWatch: [ [Circular] ] } } ],
constant: false }
NOTE angular $parse do not export ast variable it's done by this library.
An empty object where you may define your custom filters.
The internal Lexer.
The internal Parser.
Evaluates the compiled src and returns the result of the expression. Property look-ups or assignments are executed on a given scope.
Tries to assign the given value to the result of the compiled expression on the given scope and returns the result of the assignment.
There is no dist build because it's not 2005 anymore. Use a module bundler like webpack or browserify. They're both capable of CommonJS and AMD.
If you are running in Node.JS, we recommend you to use the following flag in your node command so that access to __proto__ is not possible.
This puts an additional security layer which makes vulnerabilities much less likely to affect you.
The flag can be set like this :
node --disable-proto=delete app.jsIf you're using some libraries that still rely on Prototype, this could break your application.
Here is the revised text rewritten specifically for developers using the angular-expressions library in their projects.
angular-expressions strictly blocks the lookup of properties inherited from the prototype chain to prevent Remote Code Execution (RCE) vulnerabilities.
When designing your application templates and evaluation contexts, you must guide your end-users toward safe alternatives rather than attempting to circumvent this protection.
Your users cannot directly invoke inherited methods in their expressions (e.g., "test".toUpperCase() or [3, 1, 2].toSorted()). Instead, expose user-defined filter functions in your configuration:
- Blocked:
"test".toUpperCase()-> Allowed Alternative:"test" | upper - Blocked:
[3, 1, 2].toSorted()-> Allowed Alternative:[3, 1, 2] | sort
When you provide custom filters to the expression evaluator, ensure their return values are secure:
- Primitives: Always safe to return.
- Objects: Should be plain data objects (no inheritance, methods, getters, or setters).
- State Mutation: If a returned object must include methods or setters, verify they cannot mutate any state outside the evaluation context.
🛑 CRITICAL SECURITY RISK: Never write filters that expose constructors or prototypes.
For example, registering filters like prototype: (x) => Object.getPrototypeOf(x) or constructor: (x) => x.constructor allows malicious users to access Object.prototype via ({}) | prototype or Function.prototype via [] | constructor | prototype. This introduces severe RCE vulnerabilities.
If your expressions need to know the type of a variable, return a primitive string descriptor instead of the constructor itself (e.g., return Object.prototype.toString.call(x).slice(8, -1)).
If you pass raw ORM model instances into the evaluation context, field resolution will fail. This happens because ORMs typically define fields as accessor properties on the model's prototype, which angular-expressions blocks.
Before passing data to the expression evaluator, always convert your ORM models into plain-data views:
- Sequelize: Pass
modelInstance.dataValuesinstead ofmodelInstance - Mongoose: Pass
modelInstance.toObject()instead ofmodelInstance
You can change the maxSize of the LRUCache that is used, by running :
const expressions = require("angular-expressions");
expressions.compile.cache.setMaxSize(10000);
When an item is cached, the option for isIdentifierStart and isIdentifierContinue are not stored in the cache, meaning that if you change just those two parameters for the same tag, the previous version will be retrieved. We suggest you to use the same isIdentifierStart and isIdentifierContinue parameters for all your calls.
When providing data or filters to the library, ensure that you do not include "eval", "Function", or any filters that internally invoke these functions, as doing so could expose your users to the risk of Remote Code Execution (RCE).
Kudos go entirely to the great angular.js team, it's their implementation!
Suggestions and bug-fixes are always appreciated. Don't hesitate to create an issue or pull-request. All contributed code should pass
- the tests in node.js by running
npm test - the tests in all major browsers by running
npm run test-browserand then visitinghttp://localhost:8080/bundle
