Samuel Rosen
Samuel Rosen

Reputation: 183

Is there a safe way to Eval In ruby? Or a better way to do this?

When a user uses my application, at one point they will get an array of arrays, that looks like this:

results = [["value",25], ["value2",30]...]

The sub arrays could be larger, and will be in a similar format. I want to allow my users to write their own custom transform function that will take an array of arrays, and return either an array of arrays, a string, or a number. A function should look like this:

def user_transform_function(array_of_arrays)
  # eval users code, only let them touch the array of arrays
end 

Is there a safe way to sandbox this function and eval so a user could not try and execute malicious code? For example, no web callouts, not database callouts, and so on.

Upvotes: 3

Views: 3563

Answers (2)

Automatico
Automatico

Reputation: 12916

WARNING: I can not guarantee that this is truly safe!

You might be able to run it as a separate process and use ruby $SAFE, however this does not guarantee that what you get is safe, but it makes it harder to mess things up.

What you then would do is something like this:

script = "arr.map{|e| e+2}" #from the user.
require "json"

array = [1, 2, 3, 4]
begin
    results = IO.popen("ruby -e 'require \"json\"; $SAFE=3; arr = JSON.parse(ARGV[0]); puts (#{script}).to_json' #{array.to_json}") do |io|
        io.read
    end 
rescue Exception => e
    puts "Ohh, good Sir/Mam, your script caused an error."
end

if results.include?("Insecure operation")
    puts "Ohh, good Sir/Mam, you cannot do such a thing"
else
    begin
        a = JSON.parse(results)
        results = a
    rescue Exception => e
        puts "Ohh, good Sir/Mam, something is wrong with the results."
        puts results
    end
end

       conquer_the_world(results) if     results.is_a?(Array)
do_not_conquer_the_world(results) unless results.is_a?(Array)

OR

You could do this, it appears:

def evaluate_user_script(script)
  Thread.start {
    $SAFE = 4
    eval(script)
  }    
end

But again: I do not know how to get the data out of there.

Upvotes: 1

roman-roman
roman-roman

Reputation: 2796

First, if you will use eval, it will never be safe. You can at least have a look in the direction of taint method.

What I would recommend is creating your own DSL for that. There is a great framework in Ruby: http://treetop.rubyforge.org/index.html. Of course, it will require some effort from your side, but from the user prospective I think it could be even better.

Upvotes: 2

Related Questions