Ruby中为什么块不像方法一样继承调用者的$SAFE级别?

7

当一个线程的$SAFE值为4时调用一个方法,该方法也会以相同的$SAFE级别运行:

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

然而,当调用一个代码块时,它似乎使用它原始上下文中的 $SAFE:

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

有人能解释一下为什么会这样吗?这似乎是一个安全问题。

我使用 raise 而不是 puts 的原因是在 $SAFE = 4 时 puts 不起作用。

这可以用于在看似安全的上下文中评估一个被污染的字符串:

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

闭包不是会捕获定义时的值吗? - Dave Newton
1
你能在 $SAFE 等级为 0 的块内执行操作吗? - Andrew Grimm
1
安德鲁:是的。将lambda的内容替换为类似于“puts `hostname`”的内容会导致它在$SAFE = 4线程内执行该操作。我从作用域的角度理解这种行为(有点),但我想知道$SAFE在这方面是否完全失效了。 - rcrogers
1
我认为不是。您只能执行在安全级别之外准备好的语句。您能否通过传递潜在不安全的参数来“污染”该过程? - Joe Pym
@JoePym:我也这么认为。我增加了一个新的例子,展示了如何使lambda在$SAFE = 4创建的字符串上进行解析。 - rcrogers
1个回答

5

这是因为lambda表达式在定义时使用的作用域(包括所有局部变量!)

因此,您在安全级别0处定义了lambda表达式,在调用它时它会在该级别上执行,因为变量的状态就是那样。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接