2010-01-11 12 views
16

Me gustaría dejar que mis usuarios creen scripts Ruby que hagan cálculos en algunos datos que residen en el servidor web y luego arrojen resultados. Los scripts se ejecutan en el servidor. ¿Hay alguna manera de hacer esto de forma segura?Ejecutando código ruby ​​proporcionado por el usuario en un servidor web

Más específicamente, me gustaría:

  • restringir los recursos de la secuencia de comandos se puede utilizar (CPU y memoria), y limitan su tiempo de ejecución
  • restringir qué clases núcleo del script puede utilizar (por ejemplo cuerdas , Fixnum, flotador, matemáticas, etc.)
  • dejar que el acceso a la escritura y devuelve datos
  • salida de cualquier error al usuario

¿Hay alguna biblioteca o proyecto que haga lo que estoy pidiendo? Si no está en Ruby, ¿quizás algún otro idioma?

Respuesta

17

Se puede utilizar una "pizarra en blanco" como una habitación limpia, y una caja de arena en el que para establecer el safe level a 4.

una pizarra en blanco un objeto que ha despojado de todos los métodos de:

class BlankSlate 

    instance_methods.each do |name| 
    class_eval do 
     unless name =~ /^__|^instance_eval$|^binding$|^object_id$/ 
     undef_method name 
     end 
    end 
    end 

end 

un cuarto limpio es un objeto en el que se evalúa el contexto de otro código:

clean_room = BlankSlate.new 

leer un comando desde una fuente no confiable, entonces untaint ella. A menos que no esté contaminado, Ruby se negará a evaluar la cadena en una caja de arena.

command = gets 
    command.untaint 

Ahora ejecute la cadena en una caja de arena, haciendo subir el nivel de seguridad lo más alto posible. El nivel $ SAFE volverá a la normalidad cuando finalice el proceso. Ejecutamos el comando en el contexto del enlace de la sala limpia, de modo que solo pueda ver los métodos y las variables que puede ver la sala limpia (recuerde que, al igual que cualquier objeto, la sala limpia puede ver cualquier cosa en el escenario global).

result = proc do 
    $SAFE = 4 
    clean_room.instance_eval do 
     binding 
    end.eval(command) 
    end.call 

el resultado de impresión:

p result 
+0

se parece a lo que estaba buscando. Gracias. – nolk

+0

Simplemente curioso: ¿por qué un 'instance_eval' aquí en lugar de simplemente llamar a' clean_room.binding.eval (command) '? –

+1

@Matt, #binding es privado, por lo que debe usar un poco de "foo" para obtenerlo. –

Cuestiones relacionadas