jueves, 17 de marzo de 2011

Shikashi - A flexible sandbox for ruby

Shikashi is an sandbox for ruby that handles all ruby method calls executed in the interpreter to allow or deny these calls depending on the receiver object, the method name, the source file from where the call was originated

Goals of the project

Provide a sandbox API to build up scripting services with granular control of privileges even at method and global variable levels

The API

The API of Shikashi expose two main services: a "eval" method and a privilege representation. The "eval" just run code in the sandbox and apply the restrictions specified in the privileges passed as parameter. Example


require "shikashi"

sandbox = Shikashi::Sandbox.new
privileges = Shikashi::Privileges.new

privileges.allow_method :"+" # mandatory to prevent SecurityError exception

result = sandbox.run "2+2", privileges
print result,"\n" # 4



Also, the API allows more effective control of the privileges by objects/method names and is not limited to methods, but also allows to control the access to global variables and constants


# ...
privileges.allow_global:$a # mandatory to prevent SecurityError exception

sandbox.run("$a = 4", privileges)

print $a, "\n"
# ...



Current Status

Currently, there are a stable version of the gem (0.3.1), but the compatibility only is guaranteed for very few environments, exluding many of the interesting like Heroku which I have verified that the gem did not work

Future

The next improvement for shikashi (for version 0.4.0) will be of course the compatibility, defining two specific objetives:
  • Making it work in Ruby 1.9
  • Making it work in Heroku (without UI or any web programming)
In addition, secondary objectives about API usability were defined for the next release, these include sugar syntax to make it easier to use like so


Sandbox.run "2+2", Privileges.allow_method("+")


Links:
https://github.com/tario/shikashi
http://tario.github.com/shikashi/doc/

6 comentarios:

  1. There's no way to send variables to the sandbox.

    So basically you can give your user a sandbox to write Ruby code. Providing a sandbox for the user to interact with an API however is near to impossible.
    You can provide him an API. But giving him something to talk with will take hours and hundreds of code of lines that could've been avoided by simply giving the opportunity to bind some variables.

    Hence Shikashi kinda sucks. But it was a nice try !

    ResponderEliminar
    Respuestas
    1. I don't understand your point , but it seems like you found a limitation on the gem, if you explain the problem better, I can help you by improving the gem

      Eliminar
  2. Excellent gem, I have following questions:

    1. How to allow access from sandbox to all methids of external (to sandbox) class?
    i.e.
    Class Aaa; def a;end; def b;end; def c;end; def d;end; def e;end; end
    code = "sandbox_code"

    So, how to add permissions to all methods of Aaa fir sandbox?

    2. Following code won't work, what could be correct value for permissions?
    https://gist.github.com/anonymous/6813761

    ResponderEliminar
    Respuestas
    1. Try this: https://gist.github.com/tario/6814395

      1. Try this: ttps://gist.github.com/tario/6814539
      2. Try this: https://gist.github.com/tario/6814395

      Maybe I should remove .instances_of(..klass..) methods, it is sort of confusing and insecure in some cases. You could end authorizing unnotice methods if you use .instances_of, for now try to use .methods_of and with the explicit names if possible if you want more clear and detailed access control (Example: privileges.methods_of(X).allow :foo)

      I will write lot of examples for this particular cases, also you can check the RDoc here: http://tario.github.io/shikashi/doc/, it has a lot of examples

      Eliminar
    2. Thank you,
      this code (https://gist.github.com/tario/6814395) works, however, if I change line puts "inside test" to puts "inside test #{c}", I get sandbox.rb:28:in `': undefined local variable or method `c' for main:Object (NameError).

      I also tried following code:
      print "inside test #{calc(10,20)}"

      And following, both with same error.
      def test(params={with_ls: false})
      params[:sum] = calc(10,20)
      print "inside test #{params[:sum]}"
      if params[:with_ls]
      system('ls -l') # denied
      end
      end



      Eliminar
    3. You miss a escape sequence, ruby it's trying to interpolate on the string with your code looking for a variable named 'c' , you want to mean this:
      puts "inside test \#{c}"
      Look at this gist: https://gist.github.com/tario/cab801a8a9fd6cfb0f6e

      Eliminar