7

有几种方法可以从需要/加载该库的 Ruby 代码中访问库的源代码。在这些方式中,有的直接读取库文件并解析。其他人通过一些提供源信息的内置方法(例如抽象语法树)访问源。在我无法直接读取文件内容的情况下(如前一种方式),访问源的唯一方法是访问提供信息的内置方法。通过重新定义这些方法来做其他事情,我将完全放弃对源代码的访问。什么是最小的方法集,如果我将它们重新定义为其他方法,我将完全失去对外部文件上库源代码的访问权限?


重新表述问题

认为:

  • 有一个用户可以在文件 A 中编写任何 Ruby 代码。
  • 有一个我写的静态Ruby文件B,它加载文件A并调用A中定义的主例程,还定义了一些用户可以在A中使用的类/方法。
  • 用户对 B 没有 +r(读取)或 +w(写入)权限。

我必须通过在文件 B 中写入来重新定义(无效)或删除哪些(标准 Ruby)方法,以使用户无法访问写入文件 B 中的源(通过用户可以在文件 A 中写入的任何代码) ) 当我运行文件 B 时?

有一些库,如 sorcerer、pry,可以提取它有权访问的方法的源代码。纯 Ruby 中必须有一些原始命令,这些库依赖这些命令才能访问源代码。使这种事情成为可能的方法是什么?

如果您不知道完整答案但知道特定库如何提取某些方法的来源,那么这仍然会有所帮助。

4

2 回答 2

6

TL;DR: Ruby-only solutions can only use source_location, so just redefine this to return something like ['/some/empty/file', 1]. C hacks to the interpreter don't use source_location, but you can prevent any use of C extensions by blocking/white-listing require and friends.


For one, to be able to execute a Ruby script, you have to be able to read it...

But back to the question. I know Sourcify doesn't use any mystical method besides a little method on Proc and Method called source_location, which gives the filename and line number were a method/proc is defined. I know from experience that this approach is very fragile, requires writing some sort of parser, and only sometimes works in legitimate situations. So, Sourcify is already out if you redefine source_location in B to return something like /dev/null, line 0 and let Sourcify throw a not-Ruby-source exception.

From Pry's source, it seems that Pry uses the same source_location approach, so two birds with one stone.

Now, there is another option for all of these libraries, which is to drop down to C and hack the interpreter to record source code. This is almost flawless. But we can still avoid the danger in one very simple way. Someone could include all of the code for Pry's method source in A. But you can't include inline C/C extensions without requiring a C library. So, the solution is obvious: Redefine require and require_relative and load to either not work, or to only allow certain libraries. This way, you can keep out the C hacks.

On MRI, there is no way (from Ruby code) besides source_location to do this. So there you go!

Edit: According to @banister, from MRI 2.0+ there is a binding_of_caller method builtin that could replace source location. Nuke this too. ;)

Warning: Ruby is not a good language for this. If you can metaprogram them, they can probably metaprogram you unless you're in a different process.

于 2013-05-10T01:04:31.747 回答
0

如果您使用 John Mair(Pry 的制造商)的令人敬畏的“method_source”gem,实际上非常容易:该方法必须在 Ruby(而不是 C)中实现,并且必须从文件(而不是 irb)中加载。

这是一个在 Rails 控制台中使用 method_source 显示方法源代码的示例:

  $ rails console
  > require 'method_source'

  # the following prints out the method code for #lookup in the Rails I18n Backend:

  > I18n::Backend::Simple.instance_method(:lookup).source.display
    def lookup(locale, key, scope = [], options = {})
      init_translations unless initialized?
      keys = I18n.normalize_keys(locale, key, scope, options[:separator])

      keys.inject(translations) do |result, _key|
        _key = _key.to_sym
        return nil unless result.is_a?(Hash) && result.has_key?(_key)
        result = result[_key]
        result = resolve(locale, _key, result, options.merge(:scope => nil)) if result.is_a?(Symbol)
        result
      end
    end
    => nil 

显示的 Ruby 代码需要来自已经加载的文件(显然它必须是可读的),并且它需要是本机 Ruby 代码(这不适用于已编译/链接的库)。

也可以看看:

于 2013-05-10T00:14:06.150 回答