環境に一切干渉できない eval
#!ruby -Ku
# クラス化して Module.new.instance_eval するようにしてみる。
# まだなんかあるかなぁ……
class SafeEval
def safe_eval(code, tm=1)
result = nil
tg = nil
th = Thread.start do
# スレッドグループを作り、
# 新たなスレッドはすべてこれに所属させる。
tg = ThreadGroup.new.add(Thread.current)
$SAFE = 4
result = Module.new.instance_eval(code)
end.join(tm)
# 生成されたスレッドをすべて削除
tg.list.each {|t| t.kill }
raise TimeoutError unless th # タイムアウトした場合 Thread は nil を返す
result
end
alias eval safe_eval
end
if $0 == __FILE__
require "test/unit"
class SafeEvalTest < Test::Unit::TestCase
def setup
@t = SafeEval.new.taint
end
def test_safe
assert_raise(SecurityError) do
@t.safe_eval("puts ''")
end
assert_raise(SecurityError) do
@t.safe_eval("$foo = :foo")
end
assert_nothing_raised(SecurityError) do
@t.safe_eval("def hoge; end")
end
@t.safe_eval <<-EOS
def safe_eval(code)
"Nice boat."
end
EOS
assert_not_equal("Nice boat", @t.safe_eval("nil"))
end
def test_safe_access
assert_raise(NoMethodError) do
@t.safe_eval("@foo << :bar")
end
assert_equal(nil, @t.instance_variable_get(:@foo))
assert_raise(NameError) do
@t.safe_eval("@@foo")
end
end
end
endまだなんかあるかなぁ……
SafeEval クラスは毎回つかいすてる。
インスタンス変数は無名 Module でさよならする。
クラス変数は SafeEval クラスが untaint である限りつくれない。