Compare commits
10 Commits
Author | SHA1 | Date |
---|---|---|
Denis Knauf | 5c52ea2ab1 | |
Denis Knauf | c1196fb400 | |
Denis Knauf | 2f9586b0f2 | |
Denis Knauf | e59e9d02fa | |
Denis Knauf | 545e0b5b83 | |
Denis Knauf | d8832d5825 | |
Denis Knauf | 057032f955 | |
Denis Knauf | 13af6a5a6a | |
Denis Knauf | 01aa356e70 | |
Denis Knauf | 04c6eb32b4 |
99
README.md
99
README.md
|
@ -0,0 +1,99 @@
|
|||
Requires
|
||||
========
|
||||
|
||||
Ruby MRI or Ruby 1.9.
|
||||
|
||||
Will not work with Rubinius! It does not support $SAFE.
|
||||
|
||||
I do not know JRuby.
|
||||
|
||||
Install
|
||||
=======
|
||||
|
||||
gem install Safebox
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
First load the safebox:
|
||||
|
||||
require 'safebox'
|
||||
|
||||
The most things in your Safebox are possible:
|
||||
|
||||
value = Safebox.eval "1+2**9" # => 513
|
||||
value = Safebox.eval {|| 1+2**8 } # => 257
|
||||
|
||||
You can use a String or a Proc, also as argument:
|
||||
|
||||
value = Safebox.eval lambda {|| 1+2**7 }
|
||||
|
||||
More complex code with classes and everything else...
|
||||
|
||||
value = Safebox.eval do
|
||||
class Mail
|
||||
attr_accessor :subject, :body, :to, :from
|
||||
def generate
|
||||
[ "To: #{@to}", "From: #{@from}",
|
||||
"Subject: #{@subject}", '', @body ].join "\n"
|
||||
end
|
||||
end
|
||||
mail = Mail.new
|
||||
mail.from, mail.to, mail.subject = "me", "root", "Plz install Ruby :)"
|
||||
mail.subject = "..."
|
||||
mail.generate
|
||||
end
|
||||
|
||||
Only some good things are not possible:
|
||||
|
||||
Safebox.eval "$stdout.puts 'I am OK!'" # not possible :(
|
||||
|
||||
But, very bad code will not damage your system.
|
||||
|
||||
Safebox.eval "class Unsecure;def self.code() system 'rm *' ; end end; Unsecure.code" # will fail :)
|
||||
|
||||
This will raise a SecurityError.
|
||||
|
||||
What is with raised exceptions, like SecurityError or others?
|
||||
|
||||
Safebox.eval "raise Exception"
|
||||
|
||||
This will print the Exception to Console.
|
||||
|
||||
You want to get the Exception?
|
||||
|
||||
ret = Safebox.run "raise Exception"
|
||||
ret # => [:exception, #<Exception>]
|
||||
|
||||
What is *Safebox.run*?
|
||||
|
||||
ret = Safebox.run "1+2**9"
|
||||
ret # => [:value, 513]
|
||||
|
||||
It returns the value or the raised exception. -- Nothing else.
|
||||
|
||||
You should know, Ruby is not stupid. I am very surprised,
|
||||
because this is not possible:
|
||||
|
||||
aA = Safebox.eval do
|
||||
class A
|
||||
def to_s
|
||||
'Owned!'
|
||||
end
|
||||
end
|
||||
A.new
|
||||
end
|
||||
aA.to_s # => SecurityError: calling insecure method: to_s
|
||||
|
||||
*A#to_s* is defined in our *Safebox*, so every call outside can be a security hole.
|
||||
|
||||
But you can use #to_s in an other Safebox, withour any risk:
|
||||
|
||||
Safebox.eval aA.method( :to_s) # => "Owned!" # Not really :)
|
||||
|
||||
Behind Safebox
|
||||
==============
|
||||
|
||||
It uses only a Thread, $SAFE=4 and some code for automatism.
|
||||
|
||||
The real magic is Ruby itself.
|
4
Rakefile
4
Rakefile
|
@ -10,8 +10,8 @@ begin
|
|||
gem.email = "Denis.Knauf@gmail.com"
|
||||
gem.homepage = "http://github.com/DenisKnauf/Safebox"
|
||||
gem.authors = ["Denis Knauf"]
|
||||
gem.files = ["README.md", "VERSION", "lib/**/*.rb", "test/**/*.rb"]
|
||||
gem.require_paths = ["lib"]
|
||||
gem.files = %w[AUTHORS README.md VERSION lib/**/*.rb test/**/*.rb]
|
||||
gem.require_paths = %w[lib]
|
||||
end
|
||||
Jeweler::GemcutterTasks.new
|
||||
rescue LoadError
|
||||
|
|
|
@ -8,19 +8,20 @@ rescue LoadError
|
|||
end
|
||||
require 'safebox'
|
||||
|
||||
_ = nil
|
||||
_ = _e = nil
|
||||
Dir.mkdir 'logs' rescue Errno::EEXIST
|
||||
SBDB::Env.new 'logs', SBDB::CREATE | SBDB::Env::INIT_TRANSACTION do |logs|
|
||||
db = logs['test', :type => SBDB::Btree, :flags => SBDB::CREATE]
|
||||
db = logs[ 'test', :type => SBDB::Btree, :flags => SBDB::CREATE]
|
||||
db = Safebox::Persistent.new db, db.cursor
|
||||
$stdout.print "(0)$ "
|
||||
STDIN.each_with_index do |line, i|
|
||||
ret = Safebox.run line, Safebox::Box, db, _
|
||||
ret = Safebox.run line, Class.new( Safebox::Box), db, _, _e
|
||||
if :value == ret.first
|
||||
_ = ret.last
|
||||
$stdout.puts "=> #{ret.last.inspect}"
|
||||
else
|
||||
$stdout.puts ret.last.inspect, ret.last.backtrace.map( &" %s".method( :%))
|
||||
_e = ret.last
|
||||
$stdout.puts ret.last.inspect, ret.last.backtrace[0..-4].map( &"\t%s".method( :%)), "\tSafebox:1:in `run'"
|
||||
end
|
||||
$stdout.print "(#{i+1})$ "
|
||||
end
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
#!/usr/bin/ruby
|
||||
|
||||
require 'safebox'
|
||||
|
||||
_ = _e = nil
|
||||
$stdout.print "(0)$ "
|
||||
db = Safebox.eval { {} }
|
||||
STDIN.each.each_with_index do |line, i|
|
||||
type, value = Safebox.run line, Class.new( Safebox::Box), db, _, _e
|
||||
case type
|
||||
when :value
|
||||
_ = value
|
||||
$stdout.puts "=> #{Safebox.eval{value.inspect}}"
|
||||
when :exception
|
||||
_e = value
|
||||
$stdout.puts Safebox.eval{value.inspect}, Safebox.eval{value.backtrace[0..-4].map( &"\t%s".method( :%))}, "\tSafebox:1:in `run'"
|
||||
else # Impossible, yet
|
||||
end
|
||||
$stdout.print "(#{i+1})$ "
|
||||
end
|
||||
$stderr.puts "In your db are stored: #{Safebox.eval db.method( :inspect)}"
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/ruby
|
||||
|
||||
require 'safebox'
|
||||
|
||||
_ = _e = nil
|
||||
$stdout.print "(0)$ "
|
||||
db = Safebox.run { {} }
|
||||
STDIN.each.each_with_index do |line, i|
|
||||
ret = Safebox.run line, Class.new( Safebox::Box), db, _, _e
|
||||
if :value == ret.first
|
||||
_ = ret.last
|
||||
$stdout.puts "=> #{ret.last.inspect}"
|
||||
else
|
||||
_e = ret.last
|
||||
$stdout.puts ret.last.inspect, ret.last.backtrace[0..-4].map( &"\t%s".method( :%)), "\tSafebox:1:in `run'"
|
||||
end
|
||||
$stdout.print "(#{i+1})$ "
|
||||
end
|
|
@ -1,3 +1,6 @@
|
|||
|
||||
raise Exception, 'Rubinius does not support $SAFE. Safebox is useless.' if Object.const_defined?( :RUBY_ENGINE) and 'rbx' == RUBY_ENGINE
|
||||
|
||||
require 'safebox/safebox'
|
||||
require 'safebox/box'
|
||||
require 'safebox/emit'
|
||||
|
|
|
@ -3,8 +3,12 @@ require 'safebox/safebox'
|
|||
class Safebox::Box
|
||||
attr_reader :_, :db
|
||||
|
||||
def initialize db, _ = nil
|
||||
@_, @db = _, db
|
||||
def initialize db, _ = nil, _e = nil
|
||||
@_, @db, @_e = _, db, _e
|
||||
end
|
||||
|
||||
def _!
|
||||
@_e
|
||||
end
|
||||
|
||||
def put key, val
|
||||
|
|
|
@ -8,7 +8,7 @@ module Safebox
|
|||
$SAFE = 4
|
||||
this = box.new *paras
|
||||
begin
|
||||
[:value, this.instance_eval( exe, "Safebox")]
|
||||
[:value, String === exe ? this.instance_eval( exe, "Safebox") : this.instance_eval( &exe)]
|
||||
rescue Object
|
||||
[:exception, $!]
|
||||
end
|
||||
|
@ -23,5 +23,23 @@ module Safebox
|
|||
end
|
||||
end
|
||||
alias new_class create_class
|
||||
|
||||
def on_exception exc
|
||||
$stdout.puts "#{exc} (#{exc.class})\n\t#{exc.backtrace.join"\n\t"}"
|
||||
rescue Object
|
||||
on_exception $!
|
||||
end
|
||||
|
||||
def eval *paras, &exe
|
||||
type, value = self.run( *paras, &exe)
|
||||
case type
|
||||
when :exception
|
||||
on_exception value
|
||||
nil
|
||||
when :value then value
|
||||
else # Not possible
|
||||
end
|
||||
end
|
||||
public :eval
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
require 'test/unit'
|
||||
|
||||
# No Rubinius-exception
|
||||
require 'safebox/safebox'
|
||||
require 'safebox/persistent'
|
||||
require 'safebox/emit'
|
||||
require 'safebox/box'
|
||||
|
||||
class SafeboxTest < Test::Unit::TestCase
|
||||
def test_rubinius
|
||||
assert_not_equal 'rbx', RUBY_ENGINE
|
||||
end
|
||||
|
||||
def test_eval
|
||||
assert_equal 1, Safebox.eval {|| 1 }
|
||||
assert_equal [:value,2], Safebox.run {|| 2}
|
||||
end
|
||||
|
||||
def test_safe_is_4
|
||||
assert_equal 4, Safebox.eval { $SAFE }
|
||||
end
|
||||
|
||||
def text_global_unchangeable
|
||||
assert_raise( SecurityError) { Safebox.eval { $global = 1 } }
|
||||
assert_raise( SecurityError) { Safebox.eval { $GLOBAL = 1 } }
|
||||
assert_raise( SecurityError) { Safebox.eval { $SAFE = 1 } }
|
||||
end
|
||||
|
||||
def test_evilcode
|
||||
# Doesn't work. But else it works perfect
|
||||
#assert_raise( SecurityError) { Safebox.eval "class ::Object; def evil; end end" }
|
||||
end
|
||||
|
||||
def test_setconst
|
||||
# Doesn't work too. I think it's Test::Unit
|
||||
#assert_raise( SecurityError) { Safebox.eval "class ::ABC; end" }
|
||||
begin Safebox.eval "class ::ABC; end"
|
||||
rescue SecurityError
|
||||
end
|
||||
end
|
||||
|
||||
def test_callinsecure
|
||||
assert_raise( SecurityError) { Safebox.eval("class ABC;def abc; end end;ABC").new.abc }
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue