safe eval?

Discussion in 'Ruby' started by Ara.T.Howard, May 10, 2004.

  1. Ara.T.Howard

    Ara.T.Howard Guest

    i have a project i'm working on where i'd like to support complex
    boolean/relational requests, where those requests must be satisfied on the
    context of defined objects... i'm loath to create an entire parser/scanner
    just to evaluate these expression when ruby's own is already written but also
    don't want to risk using eval for the obvious reason. so, for example, i'll
    have a command line option for a request:

    prog.rb --request='a < 42 and b == true'


    i can think of three approaches for evaluating such requests:

    1) eval

    request = 'a < 42 and b == true'
    a = 42
    b = true
    eval request

    2) code generation using ruby to evaluate (this protects against evil evals)

    request = 'a < 42 and b == true'
    a = 42
    b = true

    code = <<-code
    a = #{ a }
    b = #{ b }
    p(#{ request })
    code

    res = `ruby -e '#{ code }'`

    case res
    when /true/o
    when /false/o
    else
    end

    3) full blown racc parser with associated context/evaluation logic...


    * eval is attractive because i'd be done today, but it'd be too easy for someone
    to do

    prog.rb --request='a < 42 and b == true; raise "ha ha"'

    * code generation is attractive for the same reason but feels hackish and slow


    * the full blown racc parser just seems like alot of work to accomplish such a
    small thing... then again perhaps it wouldn't be that bad...

    can someone think of alternatives or variations that are simple and safe?


    -a
    --
    ===============================================================================
    | EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
    | PHONE :: 303.497.6469
    | ADDRESS :: E/GC2 325 Broadway, Boulder, CO 80305-3328
    | URL :: http://www.ngdc.noaa.gov/stp/
    | TRY :: for l in ruby perl;do $l -e "print \"\x3a\x2d\x29\x0a\"";done
    ===============================================================================
     
    Ara.T.Howard, May 10, 2004
    #1
    1. Advertising

  2. Ara.T.Howard

    ts Guest

    >>>>> "A" == Ara T Howard <> writes:

    A> 1) eval

    [...]

    A> 2) code generation using ruby to evaluate (this protects against evil
    A> evals)

    If you really think that you are protected against the evil eval then just
    try

    A> request = 'a < 42 and b == true'

    request = '(a < 42 && b == true; %x{ls})'

    then replace ls with what you want ...

    Use $SAFE = 4 in a new thread


    Guy Decoux
     
    ts, May 10, 2004
    #2
    1. Advertising

  3. Ara.T.Howard

    Ara.T.Howard Guest

    On Tue, 11 May 2004, ts wrote:

    > >>>>> "A" == Ara T Howard <> writes:

    >
    > A> 1) eval
    >
    > [...]
    >
    > A> 2) code generation using ruby to evaluate (this protects against evil
    > A> evals)
    >
    > If you really think that you are protected against the evil eval then just
    > try
    >
    > A> request = 'a < 42 and b == true'
    >
    > request = '(a < 42 && b == true; %x{ls})'
    >
    > then replace ls with what you want ...
    >
    > Use $SAFE = 4 in a new thread


    and this does protect against modifying globals.... hmmm. simple (could be
    done today) and safe... sounds better than writing a parser/scanner...


    -a
    --
    ===============================================================================
    | EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
    | PHONE :: 303.497.6469
    | ADDRESS :: E/GC2 325 Broadway, Boulder, CO 80305-3328
    | URL :: http://www.ngdc.noaa.gov/stp/
    | TRY :: for l in ruby perl;do $l -e "print \"\x3a\x2d\x29\x0a\"";done
    ===============================================================================
     
    Ara.T.Howard, May 10, 2004
    #3
  4. Ara.T.Howard

    Ara.T.Howard Guest

    On Mon, 10 May 2004, Florian Gross wrote:

    > Ara.T.Howard wrote:
    >
    > > i have a project i'm working on where i'd like to support complex
    > > boolean/relational requests, where those requests must be satisfied on the
    > > context of defined objects... i'm loath to create an entire parser/scanner
    > > just to evaluate these expression when ruby's own is already written but also
    > > don't want to risk using eval for the obvious reason. so, for example, i'll
    > > have a command line option for a request:
    > >
    > > prog.rb --request='a < 42 and b == true'

    >
    > Just use this:



    that looks sweet! i'll try it out later today... thanks!

    -a


    >
    > module Safe; end
    > class << Safe
    > # Runs passed code in a relatively safe sandboxed environment.
    > #
    > # You can pass a block which is called with the sandbox as its first
    > # argument to apply custom changes to the sandbox environment.
    > #
    > # Returns an Array with the result of the executed code and
    > # an exception, if one occured.
    > #
    > # Example of usage:
    > #
    > # result, error = safe "1.0 / rand(10)"
    > # puts if error then
    > # "Error: #{error.inspect}"
    > # else
    > # result.inspect
    > # end
    >
    > def safe(code, sandbox=nil)
    > error = nil
    >
    > begin
    > thread = Thread.new {
    > $-w = nil
    >
    > sandbox ||= Object.new.taint
    >
    > yield(sandbox) if block_given?
    >
    > $SAFE = 5
    > eval(code, sandbox.send:)binding))
    > }
    > value = thread.value
    > result = Marshal.load(Marshal.dump(thread.value))
    > rescue Exception => error
    > error = Marshal.load(Marshal.dump(error))
    > end
    >
    > return result, error
    > end
    > end
    >
    > def safe(*args, &block)
    > Safe::safe(*args, &block)
    > end
    >
    >
    > Regards,
    > Florian Gross
    >


    --
    ===============================================================================
    | EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
    | PHONE :: 303.497.6469
    | ADDRESS :: E/GC2 325 Broadway, Boulder, CO 80305-3328
    | URL :: http://www.ngdc.noaa.gov/stp/
    | TRY :: for l in ruby perl;do $l -e "print \"\x3a\x2d\x29\x0a\"";done
    ===============================================================================
     
    Ara.T.Howard, May 10, 2004
    #4
  5. Ara.T.Howard

    ts Guest

    >>>>> "F" == Florian Gross <> writes:

    F> Just use this:

    svg% cat b.rb
    #!/usr/bin/ruby
    module Safe; end
    class << Safe
    def safe(code, sandbox=nil)
    error = nil

    begin
    thread = Thread.new {
    $-w = nil

    sandbox ||= Object.new.taint

    yield(sandbox) if block_given?

    $SAFE = 5
    eval(code, sandbox.send:)binding))
    }
    value = thread.value
    result = Marshal.load(Marshal.dump(thread.value))
    rescue Exception => error
    error = Marshal.load(Marshal.dump(error))
    end

    return result, error
    end
    end

    def safe(*args, &block)
    Safe::safe(*args, &block)
    end

    safe('
    class << a = []
    def _dump(a)
    $stderr.puts "More you make it complex, more it will be easy to break"
    end
    end
    a')
    svg%


    Guy Decoux
     
    ts, May 11, 2004
    #5
  6. ts wrote:

    > safe('
    > class << a = []
    > def _dump(a)
    > $stderr.puts "More you make it complex, more it will be easy to break"
    > end
    > end
    > a')


    Heh, good one. Thanks for pointing this out.

    Try this fixed version:

    module Safe
    extend self

    def safe(code, sandbox=nil)
    error, result = nil, nil

    begin
    thread = Thread.new do
    sandbox ||= Object.new.taint
    yield(sandbox) if block_given?

    $SAFE = 5
    $-w = nil

    eval(code, sandbox.send:)binding))
    end
    result = secure_object(thread.value)
    rescue Exception => error
    error = secure_object(error)
    end

    return result, error
    end

    def secure_object(obj)
    # We can't dup immediate values. But that's no problem
    # because most of them can't have any singleton methods
    # anyway. (nil, true and false can, but they can't be
    # defined in safe contexts.)
    immediate_classes = [Fixnum, Symbol, NilClass, TrueClass,
    FalseClass]
    return obj if immediate_classes.any? { |klass| klass === obj }

    # Dup won't copy any singleton methods and without any
    # of them the Object will be safe. (But we can't call
    # the Object's .dup because it might be evil already.)
    safe_dup = Object.instance_method:)dup).bind(obj)
    safe_dup.call
    end
    end

    def safe(*args, &block)
    Safe.safe(*args, &block)
    end

    Regards,
    Florian Gross
     
    Florian Gross, May 11, 2004
    #6
  7. Ara.T.Howard

    ts Guest

    >>>>> "F" == Florian Gross <> writes:

    F> Try this fixed version:

    I really don't know if I must post this :-(


    svg% ls b.rb x.rb
    ls: x.rb: No such file or directory
    b.rb*
    svg%

    svg% cat b.rb
    #!/usr/bin/ruby
    module Safe
    extend self

    def safe(code, sandbox=nil)
    error, result = nil, nil

    begin
    thread = Thread.new do
    sandbox ||= Object.new.taint
    yield(sandbox) if block_given?

    $-w = nil
    $SAFE = 5

    eval(code, sandbox.send:)binding))
    end
    result = secure_object(thread.value)
    rescue Exception => error
    error = secure_object(error)
    end

    return result, error
    end

    def secure_object(obj)
    immediate_classes = [Fixnum, Symbol, NilClass, TrueClass,
    FalseClass]
    return obj if immediate_classes.any? { |klass| klass === obj }
    safe_dup = Object.instance_method:)dup).bind(obj)
    safe_dup.call
    end
    end

    def safe(*args, &block)
    Safe.safe(*args, &block)
    end

    b = safe('
    class << s = "`mv b.rb x.rb`"
    def call
    end
    end
    a = Object.new
    ObjectSpace.define_finalizer(a, s)
    a
    ')
    svg%

    svg% b.rb
    svg%

    svg% ls b.rb x.rb
    ls: b.rb: No such file or directory
    x.rb*
    svg%




    Guy Decoux
     
    ts, May 12, 2004
    #7
  8. Ara.T.Howard

    Ara.T.Howard Guest

    On Wed, 12 May 2004, ts wrote:

    > >>>>> "F" == Florian Gross <> writes:

    >
    > F> Try this fixed version:
    >
    > I really don't know if I must post this :-(


    yes, you must!

    what do you do with your spare time guy!? watch out or a man in black wearing
    dark glasses is going to knock on your door... ;-)

    all this makes me start to think that i really must use a racc parser....
    uggh

    -a

    >
    >
    > svg% ls b.rb x.rb
    > ls: x.rb: No such file or directory
    > b.rb*
    > svg%
    >
    > svg% cat b.rb
    > #!/usr/bin/ruby
    > module Safe
    > extend self
    >
    > def safe(code, sandbox=nil)
    > error, result = nil, nil
    >
    > begin
    > thread = Thread.new do
    > sandbox ||= Object.new.taint
    > yield(sandbox) if block_given?
    >
    > $-w = nil
    > $SAFE = 5
    >
    > eval(code, sandbox.send:)binding))
    > end
    > result = secure_object(thread.value)
    > rescue Exception => error
    > error = secure_object(error)
    > end
    >
    > return result, error
    > end
    >
    > def secure_object(obj)
    > immediate_classes = [Fixnum, Symbol, NilClass, TrueClass,
    > FalseClass]
    > return obj if immediate_classes.any? { |klass| klass === obj }
    > safe_dup = Object.instance_method:)dup).bind(obj)
    > safe_dup.call
    > end
    > end
    >
    > def safe(*args, &block)
    > Safe.safe(*args, &block)
    > end
    >
    > b = safe('
    > class << s = "`mv b.rb x.rb`"
    > def call
    > end
    > end
    > a = Object.new
    > ObjectSpace.define_finalizer(a, s)
    > a
    > ')
    > svg%
    >
    > svg% b.rb
    > svg%
    >
    > svg% ls b.rb x.rb
    > ls: b.rb: No such file or directory
    > x.rb*
    > svg%
    >
    >
    >
    >
    > Guy Decoux
    >
    >
    >


    --
    ===============================================================================
    | EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
    | PHONE :: 303.497.6469
    | ADDRESS :: E/GC2 325 Broadway, Boulder, CO 80305-3328
    | URL :: http://www.ngdc.noaa.gov/stp/
    | TRY :: for l in ruby perl;do $l -e "print \"\x3a\x2d\x29\x0a\"";done
    ===============================================================================
     
    Ara.T.Howard, May 12, 2004
    #8
  9. Ara.T.Howard

    Hal Fulton Guest

    [OT] Re: safe eval?

    Ara.T.Howard wrote:

    > what do you do with your spare time guy!? watch out or a man in black wearing
    > dark glasses is going to knock on your door... ;-)


    This comment is twice as scary coming from a .gov email address. :)

    Guy: Don't take this the wrong way... I was once chatting with a couple
    of other rubyists, and we were talking about how impressive your
    knowledge was.

    Then we started talking about how no one we had ever talked to had ever
    seen you in person. So I submitted the tentative theory that you are
    actually not one person at all; you are either a committee of several
    computer science experts, or a network of Cray supercomputers.


    Cheers,

    Hal
     
    Hal Fulton, May 12, 2004
    #9
  10. Ara.T.Howard

    Ara.T.Howard Guest

    Re: [OT] Re: safe eval?

    On Thu, 13 May 2004, Hal Fulton wrote:

    > > what do you do with your spare time guy!? watch out or a man in black wearing
    > > dark glasses is going to knock on your door... ;-)

    >
    > This comment is twice as scary coming from a .gov email address. :)


    and the suit is just back from the dry cleaners... now where are my
    ray-bans...

    > Guy: Don't take this the wrong way... I was once chatting with a couple of
    > other rubyists, and we were talking about how impressive your knowledge was.


    ditto. i tell ruby nubies here in house to include 'ts' when googling for
    answers from c.l.r if they want to find the 'right' answer!

    > Then we started talking about how no one we had ever talked to had ever seen
    > you in person. So I submitted the tentative theory that you are actually not
    > one person at all; you are either a committee of several computer science
    > experts, or a network of Cray supercomputers.


    or, most likely, a committee of computer science experts running a network of
    Cray supercomputers

    -a
    --
    ===============================================================================
    | EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
    | PHONE :: 303.497.6469
    | ADDRESS :: E/GC2 325 Broadway, Boulder, CO 80305-3328
    | URL :: http://www.ngdc.noaa.gov/stp/
    | TRY :: for l in ruby perl;do $l -e "print \"\x3a\x2d\x29\x0a\"";done
    ===============================================================================
     
    Ara.T.Howard, May 12, 2004
    #10
  11. ts wrote:

    > b = safe('
    > class << s = "`mv b.rb x.rb`"
    > def call
    > end
    > end
    > a = Object.new
    > ObjectSpace.define_finalizer(a, s)
    > a
    > ')


    Heh, I don't actually regard this one as a bug of safe(), but more as
    one of Ruby. I'm uncertain if matz agrees, however.

    Personally, I have a more complete version of it that adds $SAFE-checks
    to a lot of Ruby's built-in methods. (All methods of GC,
    ObjectSpace.(define|add)_finalizer, Thread.new / .fork / .start /
    ..critical=, set_trace_func)

    I'm pretty sure that there are more cases like this where $SAFE isn't
    checked correctly in Ruby. If anybody wants to point out more of them, I
    can try to come up with a way to secure them, but I'm unsure if this is
    the best solution and if it will work all the time.

    Actually, that's the reason of using a $SAFE-level of 5 and not 4 as one
    would probably expect. :)

    Here is the way I secure define_finalizer:

    ObjectSpace.module_eval do
    class << self
    old_finalizer = instance_method:)define_finalizer)
    define_method:)_define_finalizer) do |block, *args|
    raise(SecurityError, "Penalizing finalizing") if $SAFE > 1
    old_finalizer.bind(self).call(*args, &block)
    end
    def define_finalizer(*args, &block)
    _define_finalizer(block, *args)
    end

    alias :add_finalizer :define_finalizer
    end
    end

    If anybody wants to have the complete version with all the other added
    checks, just let me know. I'll do some cleaning up and release the whole
    thing in that case.

    Regards,
    Florian Gross
     
    Florian Gross, May 12, 2004
    #11
  12. Ara.T.Howard

    ts Guest

    >>>>> "F" == Florian Gross <> writes:

    F> Heh, I don't actually regard this one as a bug of safe(), but more as
    F> one of Ruby. I'm uncertain if matz agrees, however.

    Well, it's not really important for me : I was just able to run code not
    expected.

    F> Actually, that's the reason of using a $SAFE-level of 5 and not 4 as one
    F> would probably expect. :)

    plruby run with $SAFE = 12 :)


    Guy Decoux
     
    ts, May 13, 2004
    #12
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Eric Newton
    Replies:
    3
    Views:
    9,495
    Brock Allen
    Apr 4, 2005
  2. DataBinder.Eval and Eval.

    , Jun 16, 2006, in forum: ASP .Net
    Replies:
    1
    Views:
    564
    Karl Seguin [MVP]
    Jun 16, 2006
  3. Alex van der Spek

    eval('07') works, eval('08') fails, why?

    Alex van der Spek, Jan 8, 2009, in forum: Python
    Replies:
    6
    Views:
    1,497
    Bruno Desthuilliers
    Jan 8, 2009
  4. Liang Wang
    Replies:
    8
    Views:
    141
    Ben Morrow
    Feb 2, 2008
  5. Marc Girod

    to eval or not to eval?

    Marc Girod, Apr 19, 2011, in forum: Perl Misc
    Replies:
    2
    Views:
    165
    Marc Girod
    Apr 19, 2011
Loading...

Share This Page