Как безопасно загрузить YAML-файл, содержащий несколько документов? - PullRequest
0 голосов
/ 24 мая 2018

Обычный способ безопасной загрузки типового YAML-файла с одним документом осуществляется с помощью YAML.safe_load(content).

YAML-файлов может содержать несколько документов:

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

Загрузка файла YAML, напримертак как при использовании YAML.safe_load(content) будет возвращен только первый документ:

{ 'key' => 'value' }

Если вы разделите файл и попытаетесь безопасно загрузить второй документ, вы получите ожидаемое исключение:

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

Для загрузки нескольких документов вы можете использовать YAML.load_stream(content), который возвращает массив:

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

Проблема в том, что нет YAML.safe_load_stream, который бы вызывал исключения для типов данных, не входящих в белый список.

1 Ответ

0 голосов
/ 24 мая 2018

Я написал обходной путь, который использует интерфейс YAML.parse_stream:

Редактировать : теперь как gem yaml-safe_load_stream .Кроме того, сопровождающие Psych (YAML в ruby ​​stdlib) изучают добавление этой функции в библиотеку.

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?.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...