Topic: how to write a simple sanitize function

As dangerous as it is, I need to let users enter a formula into our database on our site that gets "eval"ed.  The "good" news is that the formula can only use 10 fixed variables and only uses the +, -, *, and / operators.

So I was wondering if there's an easy way (speed is not important) to sanitize the formula before it gets written to the database.

So for example, lets say the following are allowed:  "quantity", "price", "speed", "weight", "+", "-", "*", "/", "="

So

sanitize("price = quantity*speed") = "price = quantity*speed"

but 

sanitize("puts User.first.key") = nil

Last edited by sthapit (2010-06-03 14:04:26)

Re: how to write a simple sanitize function

As a first line of defense i would save each formula as an array of strings (just by splitting on spaces), marshalled into a text field with serialize
http://railsbrain.com/api/rails-2.3.2/d … =serialize

Then you can have a list of allowed strings, and reject any formula that has a component that's not in this list.  When you want to eval it you just join it together again first.

With a serialized field it's often useful to override the get and set methods to convert it to and from a string.  When you do this, remember that mymodel.formula is the get method and mymodel[:formula] is the actual data.


serialize :formula, Array
ALLOWED_FORMULA_TOKENS = ["quantity", "price", "speed", "weight", "+", "-", "*", "/", "="]
validate :formula_is_safe

def formula_is_safe
  if (illegal_tokens = (self[:formula] - ALLOWED_FORMULA_TOKENS)).size > 0
    self.errors.add(:formula, "contains one or more illegal tokens: #{illegal_tokens.join(", ")}"
  end
end

def formula
  self[:formula].join(" ")
end

def formula=(string)
  self[:formula] = string.split(" ")
end

Last edited by Max Williams (2010-06-04 06:09:39)

###########################################
#If i've helped you then please recommend me at Working With Rails:
#http://www.workingwithrails.com/person/ … i-williams

Re: how to write a simple sanitize function

thanks!  this looks like it could work.  one small problem, if i understand your code correctly, is that it will also reject numbers?

so "price = 3.1*quantity*speed + 7" needs to be allowed.  any idea how to modify the code to allow numbers?

Last edited by sthapit (2010-06-04 06:58:59)

Re: how to write a simple sanitize function

sthapit wrote:

thanks!  this looks like it could work.  one small problem, if i understand your code correctly, is that it will also reject numbers?

so "price = 3.1*quantity*speed + 7" needs to be allowed.  any idea how to modify the code to allow numbers?

doh, yes of course.  Just need to do a different test that has a regex to match numbers.  let's assume we want to allow negative numbers and decimals.  I think this regex will do the job:  /^\-?\d+(\.\d+)?$/

It also just occurred to me that we want to strip any extraneous spaces out of the formula, so i modified formula= a little.  Here's attempt 2.


serialize :formula, Array
ALLOWED_FORMULA_TOKENS = ["quantity", "price", "speed", "weight", "+", "-", "*", "/", "="]
validate :formula_is_safe

def formula_is_safe
  if (tokens = self.illegal_formula_tokens).size > 0
    self.errors.add(:formula, "contains one or more illegal tokens: #{tokens.join(", ")}"
  end
end

def illegal_formula_tokens
  self[:formula].reject{|token| ALLOWED_FORMULA_TOKENS.include?(token) || token =~ /^\-?\d+(\.\d+)?$/ }
end

def formula
  self[:formula].join(" ")
end

def formula=(string)
  self[:formula] = string.split(" ").reject(:blank?)
end

###########################################
#If i've helped you then please recommend me at Working With Rails:
#http://www.workingwithrails.com/person/ … i-williams

Re: how to write a simple sanitize function

Couple of further thoughts:
1) to make life simple, you could force people to put spaces in between all of the elements - so they would have to type

price = 3.1 * quantity * speed + 7

instead of

price = 3.1*quantity*speed + 7

2) you should add braces ie ( and ) to the allowed tokens so people can bracket up complicated formulae.  They should put spaces around the brackets as described in point 1.

Last edited by Max Williams (2010-06-04 07:17:11)

###########################################
#If i've helped you then please recommend me at Working With Rails:
#http://www.workingwithrails.com/person/ … i-williams