When a method is called by a thread with $SAFE = 4, that method is run with the same $SAFE level:
def test_method
raise "value of $SAFE inside the method: #{$SAFE}"
end
t = Thread.new{$SAFE = 4; self.test_method}; t.join
=> RuntimeError: value of $SAFE inside the method: 4
However, when a block is called, it seems to use the $SAFE from its original context instead:
test_lambda = lambda do
raise "value of $SAFE inside the lambda: #{$SAFE}"
end
t = Thread.new{$SAFE = 4; test_lambda.call}; t.join
=> RuntimeError: value of $SAFE inside the lambda: 0
Can someone explain why it works this way? It seems like a security problem.
(the reason I'm using raise
instead of puts
is that puts
doesn't work at $SAFE = 4)
This can be used to eval a tainted string in a seemingly safe context:
test_lambda = lambda{|s| puts "Tainted: #{s.tainted?}"; eval s}
t = Thread.new{$SAFE = 4; test_lambda.call("puts `date`")}; t.join
=> Tainted: true
=> Fri Mar 30 03:15:33 UTC 2012
It's because a lambda operates with the scope it was defined at (including all the local variables!)
Hence, you defined the lambda at safe level 0, and it therefore executed at that level when it was called, as that's what the state of the variable was.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With