【问题标题】:How to safe load a YAML file that includes multiple documents?如何安全加载包含多个文档的 YAML 文件?
【发布时间】:2018-05-24 08:49:34
【问题描述】:

安全加载典型单文档 YAML 文件的常规方法是使用 YAML.safe_load(content)

YAML 文件可以包含多个文档:

---
key: value
---
key: !ruby/struct
  foo: bar

使用YAML.safe_load(content) 加载这样的 YAML 文件只会返回第一个文档:

{ 'key' => 'value' }

如果你拆分文件并尝试safe_load第二个文档,你会得到预期的异常:

Psych::DisallowedClass (Tried to load unspecified class: Struct)

要加载多个文档,您可以使用返回数组的YAML.load_stream(content)

[
  { 'key' => 'value' },
  { 'key' => #<struct foo="bar"> }
]

问题是没有YAML.safe_load_stream 会引发非白名单数据类型的异常。

【问题讨论】:

    标签: ruby yaml


    【解决方案1】:

    我写了一个利用YAML.parse_stream 接口的解决方法:

    编辑:现在作为 gem yaml-safe_load_stream。此外,Psych 的维护者(ruby stdlib 中的YAML)正在研究adding this feature 库。

    require 'yaml'
    
    module YAML
      def safe_load_stream(yaml, filename = nil, &block)
        parse_stream(yaml, filename) do |stream|
          raise_if_tags(stream, filename)
          if block_given?
            yield stream.to_ruby
          else
            stream.to_ruby
          end
        end
      end
      module_function :safe_load_stream
    
      def raise_if_tags(obj, filename = nil, doc_num = 1)
        doc_num += 1 if obj.is_a?(Psych::Nodes::Document)
    
        if obj.respond_to?(:tag)
          if tag = obj.tag
            message = "tag #{tag} encountered on line #{obj.start_line} column #{obj.start_column} of document #{doc_num}"
            message << " in file #{filename}" if filename
            raise Psych::DisallowedClass, message
          end
        end
    
        if obj.respond_to?(:children)
          Array(obj.children).each do |child|
            raise_if_tags(child, filename, doc_num)
          end
        end
      end
      module_function :raise_if_tags
      private_class_method :raise_if_tags
    end
    

    你可以这样做:

    YAML.safe_load_stream(content, 'file.txt')
    

    并得到一个异常:

    Psych::DisallowedClass (Tried to load unspecified class: tag !ruby/struct
    encountered on line 1 column 7 of document 2 in file file.txt)
    

    .start_line返回的行号是相对于文档开始的,我没有找到获取文档开始的行号的方法,所以我在错误信息中添加了文档号。

    它没有像 YAML.safe_load 这样的类和符号白名单以及锚点/别名的切换。

    还有一些方法可以使用标签,通过这种简单的unless tag.nil? 检测可能会产生误报。

    【讨论】:

      猜你喜欢
      • 2017-07-20
      • 2021-08-20
      • 2019-05-16
      • 2019-04-08
      • 1970-01-01
      • 1970-01-01
      • 2011-01-17
      • 2016-10-24
      • 2010-10-06
      相关资源
      最近更新 更多