I'm looking for a way to prevent the redefinition of a method. I'm able
to check when a method is defined (redefining Module#method_added) but
I'm unable to halt the new definition. I'm also unable to know if the
method is already defined or not.
I'm trying to implement something similar to the Java Security Manager
for Ruby. The project is currently in prealpha stage at rubyforge
(http://securedruby.rubyforge.org).
But Ruby is different from Java because it's much more dinamic. And I
like it because of it!. But it brings some issues regarding code
redefinition. What I'm trying to achieve is a way to run untrusted code
in the ruby interpreter and don't worry about possible damages to the
system. "Will this code erase all my home directory?" (or "format my
harddrive?", if I'm root) is a valid concern. Of course, in Ruby you
can always check the source code just looking at it. But, what if you
have a >100000 lines script that does something interesting but you
don't trust because your archinemesis wrote it? What if you wan't to
use "eval" to run user input code?
I think I'm able to stop direct file accesses but, because of the
dinamicity (does such word exist?) of ruby, malign code (malign as the
Austin Power archinemesis, Dr. Evil) might redefine methods to trick
our good and helpless code to do something evil for the human kind (ok,
I'm exaggerating a little bit here). For instance imagine this:
evil.rb:
class Object
def chomp a
# it's really evil.
"a name of a file that if's erased the world will be destroyed"
end
end
good_and_dumb.rb:
require "evil" # did I told you the dumb part?
FileUtils.delete(chomp "erase_to_save_the_world.doc")
Well, the world is doomed. But If we were able to stop evil to redefine
chomp, this wouldn't happen. That's why I need a way to selectively
stop method definition/redefinition/removal.
But I'm unable to find a way to implement it in a pure ruby way. Do you
know any? All the good and dumb ruby code in the world will thank you.
I'm looking for a way to prevent the redefinition of a method. I'm able
to check when a method is defined (redefining Module#method_added) but
I'm unable to halt the new definition. I'm also unable to know if the
method is already defined or not.
You can freeze the class to avoid having any redefinition on it:
MyClass.freeze
> I'm looking for a way to prevent the redefinition of a method. I'm able
> to check when a method is defined (redefining Module#method_added) but
> I'm unable to halt the new definition. I'm also unable to know if the
> method is already defined or not.
You can freeze the class to avoid having any redefinition on it:
MyClass.freeze
This is not valid at method-level though
--
Sylvain Joyeux
I've tried it, but there is no unfreeze method :S. And I need to be
able to halt the redefinition SOME TIMES and some times NOT.
Just out of curiosity does anyone know what happens if I do (class << MyClass; self; end).freeze
?
···
On Oct 8, 2005, at 12:32 PM, Sylvain Joyeux wrote:
I'm looking for a way to prevent the redefinition of a method. I'm able
to check when a method is defined (redefining Module#method_added) but
I'm unable to halt the new definition. I'm also unable to know if the
method is already defined or not.
You can freeze the class to avoid having any redefinition on it:
MyClass.freeze
This is not valid at method-level though
--
Sylvain Joyeux
I'm trying to implement something similar to the Java Security Manager
for Ruby. The project is currently in prealpha stage at rubyforge
(http://securedruby.rubyforge.org).
But Ruby is different from Java because it's much more dinamic. And I
like it because of it!. But it brings some issues regarding code
redefinition. What I'm trying to achieve is a way to run untrusted code
in the ruby interpreter and don't worry about possible damages to the
system. "Will this code erase all my home directory?" (or "format my
harddrive?", if I'm root) is a valid concern. Of course, in Ruby you
can always check the source code just looking at it. But, what if you
have a >100000 lines script that does something interesting but you
don't trust because your archinemesis wrote it? What if you wan't to
use "eval" to run user input code?
I think I'm able to stop direct file accesses but, because of the
dinamicity (does such word exist?) of ruby, malign code (malign as the
Austin Power archinemesis, Dr. Evil) might redefine methods to trick
our good and helpless code to do something evil for the human kind (ok,
I'm exaggerating a little bit here). For instance imagine this:
evil.rb:
class Object
def chomp a
# it's really evil.
"a name of a file that if's erased the world will be destroyed"
end
end
good_and_dumb.rb:
require "evil" # did I told you the dumb part?
FileUtils.delete(chomp "erase_to_save_the_world.doc")
Well, the world is doomed. But If we were able to stop evil to redefine
chomp, this wouldn't happen. That's why I need a way to selectively
stop method definition/redefinition/removal.
But I'm unable to find a way to implement it in a pure ruby way. Do you
know any? All the good and dumb ruby code in the world will thank you.
I want more granularity than what the five (0-4) $SAFE levels offer. I
want to be able to say "can write in file A but not in file B"
$ chmod a-w a.txt
$ chmod a+w b.txt
Regarding the method hooks, one thing that you can do to complicate things
somewhat is to do something like this (not tested):
class SomeClass
def some_method(*a, &b)
# ...
end
alias_method :__safety__, :some_method
def self.method_added(sym)
unless @my_add
# Revert back to original
if sym == :some_method
alias_method :some_method, :__safety__
end
else @my_add = false
end
end
end
However, the problem is that the user may simply #undef_method your #method_added or do something equivalent. It is a neverending cycle. #freeze helps a bit but there is a way around it, too.
irb(main):001:0> class Foo
irb(main):002:1> def foo
irb(main):003:2> puts 'foo'
irb(main):004:2> end
irb(main):005:1> end
=> nil
irb(main):006:0> Foo.freeze
=> Foo
irb(main):007:0> class Foo
irb(main):008:1> def foo
irb(main):009:2> puts 'foo'
irb(main):010:2> end
irb(main):011:1> end
TypeError: can't modify frozen class
from (irb):8
irb(main):012:0> x = Foo.dup
=> #<Class:0xb7cef2d4>
irb(main):013:0> Foo = x
(irb):13: warning: already initialized constant Foo
=> Foo
irb(main):014:0> class Foo
irb(main):015:1> def foo
irb(main):016:2> puts 'bar'
irb(main):017:2> end
irb(main):018:1> end
=> nil
irb(main):019:0> Foo.new.foo
bar
=> nil
irb(main):020:0>
The only thing you can do is to modify the Ruby interpreter. You will
need to hook into the method table modification code and intercept all
those calls. Then, for each call, inspect the arguments and no-op any
attempts to modify some defined methods (you should probably allow this
to be configurable).
Or, the simpler solution: A) do not run code that may be malicious and
B) use the operating system's security features such as file permissions
oops, I was just repeating what ES said. Maybe some combination of
freeze and making your class un-subclassable and your method
un-sendable, so that your privates don't get turned public.
···
I can always override dup to freeze the duplicated object. Is it right?
can I break other things in the Ruby library?
> Besides, you can dup a frozen object and alter it (but clone gives you
> a still frozen object)
>
> class Foo
> def bar
> print "bar"
> end
> end
>
> Foo.freeze
> Foo2=Foo.dup
>
> class Foo2
> def Foo2.bar
> print "bar in Foo2"
> end
> end
> p Foo2.bar
>
> >
> > I've tried it, but there is no unfreeze method :S. And I need to be
> > able to halt the redefinition SOME TIMES and some times NOT.
Regarding the method hooks, one thing that you can do to complicate things
somewhat is to do something like this (not tested):
class SomeClass
def some_method(*a, &b)
# ...
end
alias_method :__safety__, :some_method
def self.method_added(sym)
unless @my_add
# Revert back to original
if sym == :some_method
alias_method :some_method, :__safety__
end
else @my_add = false
end
end
end
Uou! Something like this is what I'm looking for! I can build my
ClassModificationPermission (or something like that) from this!
However, the problem is that the user may simply #undef_method your #method_added or do something equivalent. It is a neverending cycle. #freeze helps a bit but there is a way around it, too.
I may define the self.method_removed method to prevent the removal
(doing the same trick).
irb(main):001:0> class Foo
irb(main):002:1> def foo
irb(main):003:2> puts 'foo'
irb(main):004:2> end
irb(main):005:1> end
=> nil
irb(main):006:0> Foo.freeze
=> Foo
irb(main):007:0> class Foo
irb(main):008:1> def foo
irb(main):009:2> puts 'foo'
irb(main):010:2> end
irb(main):011:1> end
TypeError: can't modify frozen class
from (irb):8
irb(main):012:0> x = Foo.dup
=> #<Class:0xb7cef2d4>
irb(main):013:0> Foo = x
(irb):13: warning: already initialized constant Foo
=> Foo
irb(main):014:0> class Foo
irb(main):015:1> def foo
irb(main):016:2> puts 'bar'
irb(main):017:2> end
irb(main):018:1> end
=> nil
irb(main):019:0> Foo.new.foo
bar
=> nil
irb(main):020:0>
Isn't there a callback method for constant assignament? I'll look for
it.
If that's the case, I can do the same trick (again).
The only thing you can do is to modify the Ruby interpreter. You will
need to hook into the method table modification code and intercept all
those calls. Then, for each call, inspect the arguments and no-op any
attempts to modify some defined methods (you should probably allow this
to be configurable).
I'm trying not to modify the ruby interpreter. I want a pure Ruby
solution. I've been thinking to make my (future) lib a gem and
distribute this way. If I have to modify it, I've been thinking two
choices:
1 - Add pre-event callbacks to all the operations I need to prevent.
(It should be as simpler than your solution, but I'm just guessing).
These callbacks should be thread safe. Then I would implement the
permission check in these hooks.
2 - Force def, class, module and = to be METHODS. If they were methods
I would just redefine them to check if the operation is allowed and
call the older method. This would be pure ruby code (unless there is a
serious performance issue).
Thank's for your input,
now I have something to try.
> I want more granularity than what the five (0-4) $SAFE levels offer. I
> want to be able to say "can write in file A but not in file B"
(...)
Regarding the method hooks, one thing that you can do to complicate things
somewhat is to do something like this (not tested):
class SomeClass
def some_method(*a, &b)
# ...
end
alias_method :__safety__, :some_method
def self.method_added(sym)
unless @my_add
# Revert back to original
if sym == :some_method
alias_method :some_method, :__safety__
end
else @my_add = false
end
end
end
I've patched it a little bit and it worked nicely.
class Klass
def foo
puts "foo"
end
alias_method :__safety__, :foo
def self.method_added(sym)
if sym == :foo && ! caller.find { |call| /method_added/ =~ call }
alias_method :foo, :__safety__
end
end
end
class Klass
def foo
puts "evil"
end
end
a = Klass.new
a.foo // prints "foo"
Do you know if this solution is thread safe or not?,
Aureliano.