Code execution from file?

hello,

i have a text field in a database table (a text file, generally
speaking), with arbitrary Ruby code. i would like to execute this code
in my application's current environment. but i need to hide all my
variables except for some specific ones from this code.

so i was thinking to create a class with a static method that accepts
parameters and put the content of the file in to this method. and pass
the variables i want to expose as parameters.

how is this done in ruby? i have never done it.

in addition, how to execute this code with maximal security level?

also, what would happen if the code reopened classes that i have
created? is this possible? how to make it not do that?

basically i want the user to type some code in to my application and
run it but i want to protect against malicious users who want to erase
my hard disk, etc. :wink:

thanks for any help!
konstantin

please forgive the term "class with a static method", i meant "class
with a class method". my brain is still in this c++/java/c# rut.

harp:~ > cat a.rb
     def run file, vars = {}
       code = IO::read file
       dumped = vars.inject({}){|d,kv| k, v = kv; d.update k => Marshal::dump(v)}
       Thread::new(vars, code) do |vars, code|
         $SAFE = 4
         program = ""
         dumped.each{|k,v| program << "#{ k } = Marshal::load <<-__data__\n#{ v }\n__data__\n\n"}
         program << code
         eval program
       end.value
     end

     value = run "b.rb", "a" => "forty-two"
     p value

     value = run "c.rb", "a" => "42"
     p value

     harp:~ > cat b.rb
     a.upcase

     harp:~ > cat c.rb
     a.center 42

     harp:~ > ruby a.rb
     "FORTY-TWO"
     " 42 "

hth.

-a

···

On Thu, 8 Dec 2005, ako... wrote:

hello,

i have a text field in a database table (a text file, generally speaking),
with arbitrary Ruby code. i would like to execute this code in my
application's current environment. but i need to hide all my variables
except for some specific ones from this code.

so i was thinking to create a class with a static method that accepts
parameters and put the content of the file in to this method. and pass the
variables i want to expose as parameters.

how is this done in ruby? i have never done it.

in addition, how to execute this code with maximal security level?

also, what would happen if the code reopened classes that i have created? is
this possible? how to make it not do that?

basically i want the user to type some code in to my application and run it
but i want to protect against malicious users who want to erase my hard
disk, etc. :wink:

--

ara [dot] t [dot] howard [at] noaa [dot] gov
all happiness comes from the desire for others to be happy. all misery
comes from the desire for oneself to be happy.
-- bodhicaryavatara

===============================================================================

thank you very much for your help.

my application runs inside a web browser process, and creating a thread
does not seem scalable.

i tried to evaluate a file from inside a lambda and it seems to work. i
also was able to set safety level for lambda locally which effectively
restores $SAFE when lambda finishes. the only drawback of this was that
my environment is still visible from lambdas.

code = <<CODE
    v = MyClass.new('from text')
    v.mymethod
CODE

class MyClass
  def initialize(v) @v = v end
  def mymethod() puts "SAFE=#{$SAFE}, value=#{@v}" end
end

p = eval <<LAMBDA
    lambda {
    module Mod
    $SAFE = 3
    #{code}
    end
    }
LAMBDA

v = MyClass.new('original')
v.mymethod
p.call
v.mymethod

There are a number of mechanisms to "lock down" the execution for a
chunk of Ruby code, the most common being the built-in $SAFE global
variable. Unfortunately, due to a number of recently-published
vulnerabilities in the implementation of the $SAFE checks, it's
generally not considered a good way to insure that your application
will be protected from malignant users.

The best example I've seen to date of a truly safe Ruby interpreter has
to be why the lucky stiff's "Try Ruby" website
(http://tryruby.hobix.com/), which lets anyone interact with a
restricted IRb prompt via their browser and some slick Javascript. You
might email why_ directly, and see if he'd be willing to share some
tips with you (and the rest of us!).

At a bare minimum, you need to make sure the following built-in classes
and modules are either entirely hidden, or redefined to "safe"
versions:

Dir
File
FileStat
FileUtils
IO (at least methods like 'popen', 'fcntl', etc.)
ObjectSpace
Pathname
Process
GzipFile
Kernel ('fork', 'at_exit', 'caller', 'load', 'require', many more)

....and probaby others, as well. Really, it's a *hard* thing to make
arbitrary code execution safe, so unless you can re-use and share
effort with others, I'd caution you against doing this unless
absolutely necessary.

Good luck,

Lennon

thank you very much for your help.

my application runs inside a web browser process, and creating a thread does
not seem scalable.

but what if the user code creates one? it's the only way to isolate $SAFE and
your vars, see below...

i tried to evaluate a file from inside a lambda and it seems to work. i also
was able to set safety level for lambda locally which effectively restores
$SAFE when lambda finishes. the only drawback of this was that my
environment is still visible from lambdas.

also note that lambda will prevent __everything__ in it's scope from __ever__
being garbage collected. this is bad idea in a web app - assuming it's
persistant like fastcgi.

in any case be careful:

     harp:~ > cat a.rb
     code = IO::read "b.rb"

     p = eval <<LAMBDA
        lambda {
        module Mod
        $SAFE = 3
        #{code}
        end
        }
     LAMBDA

     p.call
     p "we won't see this!"

     harp:~ > cat b.rb
     exit!

     harp:~ > ruby a.rb

a thread will prevent you main program from exiting.

regards.

-a

···

On Thu, 8 Dec 2005, ako... wrote:
--

ara [dot] t [dot] howard [at] noaa [dot] gov
all happiness comes from the desire for others to be happy. all misery
comes from the desire for oneself to be happy.
-- bodhicaryavatara

===============================================================================

is there a way in ruby to set up a hook that would intercept calls to
any methods of a class?

putting aside security issues i think i was able to isolate my
environment and pass only desired variables in to the code. i create a
temporary module and evaluate my code in its context. then i remove
this module (that is why i needed the external module A, i could not
figure out how to remove a module otherwise). experts, does this look
feasible? i do not know though what to do to ensure security...

thanks
konstantin

module A
  x = 'x'
  y = 'y'

  module T
    class << self
      def get_binding(p) binding end
    end
  end

  # eval('puts x', T.get_binding(x)) -> # fails
  puts eval('"variable #{p}"', T.get_binding(x))

  remove_const :T

  #puts eval('"variable #{p}"', T.get_binding(x)) -> # fails
end

The Facets library has a lot of cool methods,
one of which ('wrap_method') can intercept calls.

   http://facets.rubyforge.org/rdoc/index.html

ako... wrote:

···

is there a way in ruby to set up a hook that would intercept calls to
any methods of a class?

this still leaves things like Object in scope. to truely isolate yourself
you'll need to fork a process that does all the work.

-a

···

On Thu, 8 Dec 2005, ako... wrote:

putting aside security issues i think i was able to isolate my
environment and pass only desired variables in to the code. i create a
temporary module and evaluate my code in its context. then i remove
this module (that is why i needed the external module A, i could not
figure out how to remove a module otherwise). experts, does this look
feasible? i do not know though what to do to ensure security...

--

ara [dot] t [dot] howard [at] noaa [dot] gov
all happiness comes from the desire for others to be happy. all misery
comes from the desire for oneself to be happy.
-- bodhicaryavatara

===============================================================================

i understand abouth threads. i think i am just researching the problem
so far. would you explain about Object?

i tried this:

module A
  class Object
    def mymethod(x) puts "#{x} in A" end
  end

code = <<CODE
  class Object; end
  o = Object.new
  #o.mymethod(p) # -> fails
  class Object
    def mymethod(x) puts "\#{x} in code" end
  end
  o = Object.new
  o.mymethod(p)
CODE

  x = 'x'
  y = 'y'

  module T
    class << self
      def get_binding(p) binding end
    end
  end

  eval("#{code}", T.get_binding(y))

  o = Object.new
  o.mymethod(x)

  remove_const :T
end