From 04c6eb32b4b8ed8027a1c8fd934c3f53b5353f9a Mon Sep 17 00:00:00 2001 From: Denis Knauf Date: Sun, 21 Mar 2010 00:59:45 +0100 Subject: [PATCH 01/10] bin/box.rb: Lesser Backtrace --- bin/box.rb | 9 +++++---- lib/safebox/box.rb | 8 ++++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/bin/box.rb b/bin/box.rb index 670ee03..a6540a5 100755 --- a/bin/box.rb +++ b/bin/box.rb @@ -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 diff --git a/lib/safebox/box.rb b/lib/safebox/box.rb index 77d8f0b..c85133f 100644 --- a/lib/safebox/box.rb +++ b/lib/safebox/box.rb @@ -3,8 +3,12 @@ require 'safebox/safebox' class Safebox::Box attr_reader :_, :db - def initialize db, _ = nil - @_, @db = _, db + def initialize db, _ = nil, _e + @_, @db, @_e = _, db, _e + end + + def _! + @_e end def put key, val From 01aa356e70c840eaf51dc07f075560d08d1e6265 Mon Sep 17 00:00:00 2001 From: Denis Knauf Date: Sun, 21 Mar 2010 01:42:55 +0100 Subject: [PATCH 02/10] little changes: metafiles --- Rakefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Rakefile b/Rakefile index 7ec2e5d..b9137a6 100644 --- a/Rakefile +++ b/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 From 13af6a5a6adcc920581469cf2024190d5274bf51 Mon Sep 17 00:00:00 2001 From: Denis Knauf Date: Wed, 31 Mar 2010 19:00:23 +0200 Subject: [PATCH 03/10] Safebox.eval added. It is like Kernel.eval, but in a box and with . --- bin/box2.rb | 19 +++++++++++++++++++ lib/safebox/box.rb | 2 +- lib/safebox/safebox.rb | 19 ++++++++++++++++++- 3 files changed, 38 insertions(+), 2 deletions(-) create mode 100755 bin/box2.rb diff --git a/bin/box2.rb b/bin/box2.rb new file mode 100755 index 0000000..d58b555 --- /dev/null +++ b/bin/box2.rb @@ -0,0 +1,19 @@ +#!/usr/bin/ruby + +require 'safebox' + +_ = _e = nil +$stdout.print "(0)$ " +db = {} +db.taint +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 diff --git a/lib/safebox/box.rb b/lib/safebox/box.rb index c85133f..1ac7ffa 100644 --- a/lib/safebox/box.rb +++ b/lib/safebox/box.rb @@ -3,7 +3,7 @@ require 'safebox/safebox' class Safebox::Box attr_reader :_, :db - def initialize db, _ = nil, _e + def initialize db, _ = nil, _e = nil @_, @db, @_e = _, db, _e end diff --git a/lib/safebox/safebox.rb b/lib/safebox/safebox.rb index b6efb34..a848bdb 100644 --- a/lib/safebox/safebox.rb +++ b/lib/safebox/safebox.rb @@ -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,22 @@ 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 + ret = self.run( *paras, &exe) + case ret.first + when :exception # Really unsecure. Somebody can create an own exception with own #to_s, #class or #backtrace. + on_exception ret.last + nil + when :value then ret.last + end + end + public :eval end end From 057032f955b38f397193d309e99fdbd0413b1f9f Mon Sep 17 00:00:00 2001 From: Denis Knauf Date: Wed, 31 Mar 2010 19:00:59 +0200 Subject: [PATCH 04/10] VERSION 0.0.2 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 8acdd82..4e379d2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.0.1 +0.0.2 From d8832d5825cb7fc4c70b6d1223a8e5bfd427299c Mon Sep 17 00:00:00 2001 From: Denis Knauf Date: Wed, 31 Mar 2010 19:32:26 +0200 Subject: [PATCH 05/10] Little Doku. --- README.md | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/README.md b/README.md index e69de29..18df0bb 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,47 @@ +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" + value = Safebox.eval {|| 1+2**8 } + +Only some good things are not possible: + + Safebox.eval "$stdout.puts 'I am OK!'" + +But, very bad code will not damage your system. + + Safebox.eval "class Unsecure;def self.code() system 'rm *' ; end end; Unsecure.code" + +This will raise a SecurityError. + +What is with raised exceptions? + + Safebox.eval "raise Exception" + +This will print the Exception. + +Or if you want to get the Exception: + + ret = Safebox.run "raise Exception" + ret # => [:exception, #] + +What is *Safebox.run*? + + ret = Safebox.run "1+2**9" + ret # => [:value, 513] + +You get something back, which can be unsafe! + + Safebox.eval( "class A;def to_s() 'Owned!'; end end; A.new").to_s + puts Safebox.eval( "class A;def to_s() 'Owned!'; end end; A.new") From 545e0b5b83ee093a3de6800f6fb4aaf87141e3f7 Mon Sep 17 00:00:00 2001 From: Denis Knauf Date: Wed, 31 Mar 2010 22:59:31 +0200 Subject: [PATCH 06/10] Little more docu --- README.md | 72 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 62 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 18df0bb..ade6328 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,12 @@ +Requires +======== + +Ruby MRI or Ruby 1.9. + +Will not work with Rubinius! It does not support $SAFE. + +I do not know JRuby. + Install ======= @@ -12,26 +21,46 @@ First load the safebox: The most things in your Safebox are possible: - value = Safebox.eval "1+2**9" - value = Safebox.eval {|| 1+2**8 } + 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!'" + 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" + 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? +What is with raised exceptions, like SecurityError or others? Safebox.eval "raise Exception" -This will print the Exception. +This will print the Exception to Console. -Or if you want to get the Exception: +You want to get the Exception? ret = Safebox.run "raise Exception" ret # => [:exception, #] @@ -41,7 +70,30 @@ What is *Safebox.run*? ret = Safebox.run "1+2**9" ret # => [:value, 513] -You get something back, which can be unsafe! +It returns the value or the raised exception. -- Nothing else. - Safebox.eval( "class A;def to_s() 'Owned!'; end end; A.new").to_s - puts Safebox.eval( "class A;def to_s() 'Owned!'; end end; A.new") +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. From e59e9d02fa95d9262e06305c43d4bd187e9bd2b3 Mon Sep 17 00:00:00 2001 From: Denis Knauf Date: Wed, 31 Mar 2010 23:02:20 +0200 Subject: [PATCH 07/10] Little more docu --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ade6328..0afd307 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ Usage First load the safebox: + #! ruby require 'safebox' The most things in your Safebox are possible: From 2f9586b0f243aab8d21448eebd90cff10dd407f3 Mon Sep 17 00:00:00 2001 From: Denis Knauf Date: Wed, 31 Mar 2010 23:08:48 +0200 Subject: [PATCH 08/10] VERSION 0.0.2 --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 0afd307..ade6328 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,6 @@ Usage First load the safebox: - #! ruby require 'safebox' The most things in your Safebox are possible: From c1196fb400803c416ed8ec2c394d8b9de207df7c Mon Sep 17 00:00:00 2001 From: Denis Knauf Date: Wed, 31 Mar 2010 23:22:03 +0200 Subject: [PATCH 09/10] better metrics --- lib/safebox/safebox.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/safebox/safebox.rb b/lib/safebox/safebox.rb index a848bdb..bf36c83 100644 --- a/lib/safebox/safebox.rb +++ b/lib/safebox/safebox.rb @@ -31,12 +31,13 @@ module Safebox end def eval *paras, &exe - ret = self.run( *paras, &exe) - case ret.first + type, value = self.run( *paras, &exe) + case type when :exception # Really unsecure. Somebody can create an own exception with own #to_s, #class or #backtrace. - on_exception ret.last + on_exception value nil - when :value then ret.last + when :value then value + else # Not possible end end public :eval From 5c52ea2ab1df7a0adc2248d977695427c5e6598c Mon Sep 17 00:00:00 2001 From: Denis Knauf Date: Sun, 25 Jul 2010 15:33:39 +0200 Subject: [PATCH 10/10] test-units added - does not work, but only in unit-tests it does not work. exception if somebody tries to use it in rubinius --- bin/box2.rb | 20 ++++++++++--------- bin/box3.rb | 18 +++++++++++++++++ lib/safebox.rb | 3 +++ lib/safebox/safebox.rb | 2 +- test/safebox.rb | 45 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 78 insertions(+), 10 deletions(-) create mode 100755 bin/box3.rb create mode 100644 test/safebox.rb diff --git a/bin/box2.rb b/bin/box2.rb index d58b555..e96a2e3 100755 --- a/bin/box2.rb +++ b/bin/box2.rb @@ -4,16 +4,18 @@ require 'safebox' _ = _e = nil $stdout.print "(0)$ " -db = {} -db.taint +db = Safebox.eval { {} } 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'" + 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)}" diff --git a/bin/box3.rb b/bin/box3.rb new file mode 100755 index 0000000..9f08c8b --- /dev/null +++ b/bin/box3.rb @@ -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 diff --git a/lib/safebox.rb b/lib/safebox.rb index 25032f2..d29f557 100644 --- a/lib/safebox.rb +++ b/lib/safebox.rb @@ -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' diff --git a/lib/safebox/safebox.rb b/lib/safebox/safebox.rb index bf36c83..37be02e 100644 --- a/lib/safebox/safebox.rb +++ b/lib/safebox/safebox.rb @@ -33,7 +33,7 @@ module Safebox def eval *paras, &exe type, value = self.run( *paras, &exe) case type - when :exception # Really unsecure. Somebody can create an own exception with own #to_s, #class or #backtrace. + when :exception on_exception value nil when :value then value diff --git a/test/safebox.rb b/test/safebox.rb new file mode 100644 index 0000000..b1fcc40 --- /dev/null +++ b/test/safebox.rb @@ -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