Safe expression evaluation in Ruby for user submitted formulas
June 8, 2021
Keisan is a Ruby gem providing an extensible way of evaluating user-submitted expressions. The library supports plenty of predefined functions and even allows you to define your own. Keisan is particularly suited for admin UIs where formulas must be maintained and persisted in database.
TL;DR; checkout Keisan for evaluation of user-submitted expressions and formulas. The gem has plenty of built-in functions and can be easily extended.
Our reporting API powers all the data driven results on Keypup, such as the Priority Inbox, Team Board and Data Explorer. Recently we decided to try out a small MVP allowing us to pass dynamic expressions as part our report API calls to perform post-processing (~ data mapping/formatting).
Our objective with that MVP was to allow report queries to become self-contained (e.g. chart queries) and reduce the amount of data massage we have to do in frontend.
The main problem with any kind of user input evaluation is security of course. You do not want to use Ruby eval or give expression evaluators access to the context of your app (i.e. give access to Rails.application.credentials for instance).
So we needed a purely interpreted expression processor with no direct connection to Ruby when evaluating expressions. A few Google searches later we ended up finding...
Keisan, expression parser and evaluator
This gem provides an expression evaluator which is close enough to Ruby but without running expressions in the Ruby context.
Instead expressions provided to Keisan are parsed as an Abstract Syntax Tree (AST) then each node in the tree gets evaluated. Each evaluation is done on primitive values without access to the Ruby context. Invoking a function which is not defined in the evaluator (e.g. Rails.application.credentials) will raise an error.
Because expressions are interpreted from scratch using a string parser and a whitelist of functions, this is considerably safer than any form of eval.
Thanks to Keisan we can safely allow our reporting API to accept dynamic processors:
Getting started with Keisan
First install the gem
Then open an irb console and start playing with the evaluator. Keisan provides a whole lot of base functions to play with, so don't be shy.
Convenient isn't it? Now let's move onto Keisan's most powerful feature.
Defining your own functions
Keisan allows you to extend its functionalities with custom functions.
Custom functions can be defined within the evaluator or in Ruby directly.
Because Keisan can be extended with custom functions, you can create very powerful and flexible calculators.
Let's say you wish to give your app administrators a web UI allowing them to derive stats from your data. You could go as far as providing Keisan functions retrieving data from your database and let them play with them. Let's see what this would look like in Rails.
Creating your own calculator
Alright, you know almost everything there is to know about Keisan. So what about creating your own calculator?
Below is an example of custom calculator with predefined functions.
You may note two things about the calculator above which are related to security. I said earlier Keisan was safe...it is, mostly...except for Denial of Service (DoS) attacks using infinite loops. Fortunately there is way to disable them.
First we disable recursive functions. This prevents users from defining functions looping indefinitely.
Second we disable the while loop. This also prevents users from entering never ending expressions.
That's it. Now feel free to get creative!
I think Keisan can be particularly appealing for any app / admin UI where formulas must be maintained and saved in database (think tax calculations, premium calculation etc..). Keisan provides a safe and extensible way of handling these use cases.
Sign-up and accelerate your engineering organization today !
Keypup's SaaS solution allows engineering teams and all software development stakeholders to gain a better understanding of their engineering efforts by combining real-time insights from their development and project management platforms. The solution integrates multiple data sources into a unified database along with a user-friendly dashboard and insights builder interface. Keypup users can customize tried-and-true templates or create their own reports, insights and dashboards to get a full picture of their development operations at a glance, tailored to their specific needs.