Add bin and edit workflow
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 9s

This commit is contained in:
2026-09-16 13:11:16 -06:00
parent c8ac4fcae5
commit 4cee170d66
17576 changed files with 895740 additions and 2 deletions
@@ -0,0 +1,34 @@
module SafeYAML
class Deep
def self.freeze(object)
object.each do |*entry|
value = entry.last
case value
when String, Regexp
value.freeze
when Enumerable
Deep.freeze(value)
end
end
return object.freeze
end
def self.copy(object)
duplicate = object.dup rescue object
case object
when Array
(0...duplicate.count).each do |i|
duplicate[i] = Deep.copy(duplicate[i])
end
when Hash
duplicate.keys.each do |key|
duplicate[key] = Deep.copy(duplicate[key])
end
end
duplicate
end
end
end
@@ -0,0 +1,36 @@
require "set"
module SafeYAML
class LibyamlChecker
LIBYAML_VERSION = Psych::LIBYAML_VERSION rescue nil
# Do proper version comparison (e.g. so 0.1.10 is >= 0.1.6)
SAFE_LIBYAML_VERSION = Gem::Version.new("0.1.6")
KNOWN_PATCHED_LIBYAML_VERSIONS = Set.new([
# http://people.canonical.com/~ubuntu-security/cve/2014/CVE-2014-2525.html
"0.1.4-2ubuntu0.12.04.3",
"0.1.4-2ubuntu0.12.10.3",
"0.1.4-2ubuntu0.13.10.3",
"0.1.4-3ubuntu3",
# https://security-tracker.debian.org/tracker/CVE-2014-2525
"0.1.3-1+deb6u4",
"0.1.4-2+deb7u4",
"0.1.4-3.2"
]).freeze
def self.libyaml_version_ok?
return true if YAML_ENGINE != "psych" || defined?(JRUBY_VERSION)
return true if Gem::Version.new(LIBYAML_VERSION || "0") >= SAFE_LIBYAML_VERSION
return libyaml_patched?
end
def self.libyaml_patched?
return false if (`which dpkg` rescue '').empty?
libyaml_version = `dpkg -s libyaml-0-2`.match(/^Version: (.*)$/)
return false if libyaml_version.nil?
KNOWN_PATCHED_LIBYAML_VERSIONS.include?(libyaml_version[1])
end
end
end
@@ -0,0 +1,181 @@
require "set"
require "yaml"
# This needs to be defined up front in case any internal classes need to base
# their behavior off of this.
module SafeYAML
YAML_ENGINE = defined?(YAML::ENGINE) ? YAML::ENGINE.yamler : (defined?(Psych) && YAML == Psych ? "psych" : "syck")
end
require "safe_yaml/libyaml_checker"
require "safe_yaml/deep"
require "safe_yaml/parse/hexadecimal"
require "safe_yaml/parse/sexagesimal"
require "safe_yaml/parse/date"
require "safe_yaml/transform/transformation_map"
require "safe_yaml/transform/to_boolean"
require "safe_yaml/transform/to_date"
require "safe_yaml/transform/to_float"
require "safe_yaml/transform/to_integer"
require "safe_yaml/transform/to_nil"
require "safe_yaml/transform/to_symbol"
require "safe_yaml/transform"
require "safe_yaml/resolver"
require "safe_yaml/syck_hack" if SafeYAML::YAML_ENGINE == "syck" && defined?(JRUBY_VERSION)
module SafeYAML
MULTI_ARGUMENT_YAML_LOAD = YAML.method(:load).arity != 1
DEFAULT_OPTIONS = Deep.freeze({
:default_mode => nil,
:suppress_warnings => false,
:deserialize_symbols => false,
:whitelisted_tags => [],
:custom_initializers => {},
:raise_on_unknown_tag => false
})
OPTIONS = Deep.copy(DEFAULT_OPTIONS)
PREDEFINED_TAGS = {}
if YAML_ENGINE == "syck"
YAML.tagged_classes.each do |tag, klass|
PREDEFINED_TAGS[klass] = tag
end
else
# Special tags appear to be hard-coded in Psych:
# https://github.com/tenderlove/psych/blob/v1.3.4/lib/psych/visitors/to_ruby.rb
# Fortunately, there aren't many that SafeYAML doesn't already support.
PREDEFINED_TAGS.merge!({
Exception => "!ruby/exception",
Range => "!ruby/range",
Regexp => "!ruby/regexp",
})
end
Deep.freeze(PREDEFINED_TAGS)
module_function
def restore_defaults!
OPTIONS.clear.merge!(Deep.copy(DEFAULT_OPTIONS))
end
def tag_safety_check!(tag, options)
return if tag.nil? || tag == "!"
if options[:raise_on_unknown_tag] && !options[:whitelisted_tags].include?(tag) && !tag_is_explicitly_trusted?(tag)
raise "Unknown YAML tag '#{tag}'"
end
end
def whitelist!(*classes)
classes.each do |klass|
whitelist_class!(klass)
end
end
def whitelist_class!(klass)
raise "#{klass} not a Class" unless klass.is_a?(::Class)
klass_name = klass.name
raise "#{klass} cannot be anonymous" if klass_name.nil? || klass_name.empty?
# Whitelist any built-in YAML tags supplied by Syck or Psych.
predefined_tag = PREDEFINED_TAGS[klass]
if predefined_tag
OPTIONS[:whitelisted_tags] << predefined_tag
return
end
# Exception is exceptional (har har).
tag_class = klass < Exception ? "exception" : "object"
tag_prefix = case YAML_ENGINE
when "psych" then "!ruby/#{tag_class}"
when "syck" then "tag:ruby.yaml.org,2002:#{tag_class}"
else raise "unknown YAML_ENGINE #{YAML_ENGINE}"
end
OPTIONS[:whitelisted_tags] << "#{tag_prefix}:#{klass_name}"
end
if YAML_ENGINE == "psych"
def tag_is_explicitly_trusted?(tag)
false
end
else
TRUSTED_TAGS = Set.new([
"tag:yaml.org,2002:binary",
"tag:yaml.org,2002:bool#no",
"tag:yaml.org,2002:bool#yes",
"tag:yaml.org,2002:float",
"tag:yaml.org,2002:float#fix",
"tag:yaml.org,2002:int",
"tag:yaml.org,2002:map",
"tag:yaml.org,2002:null",
"tag:yaml.org,2002:seq",
"tag:yaml.org,2002:str",
"tag:yaml.org,2002:timestamp",
"tag:yaml.org,2002:timestamp#ymd"
]).freeze
def tag_is_explicitly_trusted?(tag)
TRUSTED_TAGS.include?(tag)
end
end
if SafeYAML::YAML_ENGINE == "psych"
require "safe_yaml/psych_handler"
require "safe_yaml/psych_resolver"
require "safe_yaml/safe_to_ruby_visitor"
def self.load(yaml, filename=nil, options={})
# If the user hasn't whitelisted any tags, we can go with this implementation which is
# significantly faster.
if (options && options[:whitelisted_tags] || SafeYAML::OPTIONS[:whitelisted_tags]).empty?
safe_handler = SafeYAML::PsychHandler.new(options) do |result|
return result
end
arguments_for_parse = [yaml]
arguments_for_parse << filename if SafeYAML::MULTI_ARGUMENT_YAML_LOAD
Psych::Parser.new(safe_handler).parse(*arguments_for_parse)
return safe_handler.result
else
safe_resolver = SafeYAML::PsychResolver.new(options)
tree = SafeYAML::MULTI_ARGUMENT_YAML_LOAD ?
Psych.parse(yaml, filename) :
Psych.parse(yaml)
return safe_resolver.resolve_node(tree)
end
end
def self.load_file(filename, options={})
if SafeYAML::MULTI_ARGUMENT_YAML_LOAD
File.open(filename, 'r:bom|utf-8') { |f| self.load(f, filename, options) }
else
# Ruby pukes on 1.9.2 if we try to open an empty file w/ 'r:bom|utf-8';
# so we'll not specify those flags here. This mirrors the behavior for
# unsafe_load_file so it's probably preferable anyway.
self.load File.open(filename), nil, options
end
end
else
require "safe_yaml/syck_resolver"
require "safe_yaml/syck_node_monkeypatch"
def self.load(yaml, options={})
resolver = SafeYAML::SyckResolver.new(SafeYAML::OPTIONS.merge(options || {}))
tree = YAML.parse(yaml)
return resolver.resolve_node(tree)
end
def self.load_file(filename, options={})
File.open(filename) { |f| self.load(f, options) }
end
end
end
@@ -0,0 +1,37 @@
require 'time'
module SafeYAML
class Parse
class Date
# This one's easy enough :)
DATE_MATCHER = /\A(\d{4})-(\d{2})-(\d{2})\Z/.freeze
# This unbelievable little gem is taken basically straight from the YAML spec, but made
# slightly more readable (to my poor eyes at least) to me:
# http://yaml.org/type/timestamp.html
TIME_MATCHER = /\A\d{4}-\d{1,2}-\d{1,2}(?:[Tt]|\s+)\d{1,2}:\d{2}:\d{2}(?:\.\d*)?\s*(?:Z|[-+]\d{1,2}(?::?\d{2})?)?\Z/.freeze
SECONDS_PER_DAY = 60 * 60 * 24
MICROSECONDS_PER_SECOND = 1000000
# So this is weird. In Ruby 1.8.7, the DateTime#sec_fraction method returned fractional
# seconds in units of DAYS for some reason. In 1.9.2, they changed the units -- much more
# reasonably -- to seconds.
SEC_FRACTION_MULTIPLIER = RUBY_VERSION == "1.8.7" ? (SECONDS_PER_DAY * MICROSECONDS_PER_SECOND) : MICROSECONDS_PER_SECOND
# The DateTime class has a #to_time method in Ruby 1.9+;
# Before that we'll just need to convert DateTime to Time ourselves.
TO_TIME_AVAILABLE = DateTime.instance_methods.include?(:to_time)
def self.value(value)
d = DateTime.parse(value)
return d.to_time if TO_TIME_AVAILABLE
usec = d.sec_fraction * SEC_FRACTION_MULTIPLIER
time = Time.utc(d.year, d.month, d.day, d.hour, d.min, d.sec, usec) - (d.offset * SECONDS_PER_DAY)
time.getlocal
end
end
end
end
@@ -0,0 +1,12 @@
module SafeYAML
class Parse
class Hexadecimal
MATCHER = /\A[-+]?0x[0-9a-fA-F_]+\Z/.freeze
def self.value(value)
# This is safe to do since we already validated the value.
return Integer(value.gsub(/_/, ""))
end
end
end
end
@@ -0,0 +1,26 @@
module SafeYAML
class Parse
class Sexagesimal
INTEGER_MATCHER = /\A[-+]?[0-9][0-9_]*(:[0-5]?[0-9])+\Z/.freeze
FLOAT_MATCHER = /\A[-+]?[0-9][0-9_]*(:[0-5]?[0-9])+\.[0-9_]*\Z/.freeze
def self.value(value)
before_decimal, after_decimal = value.split(".")
whole_part = 0
multiplier = 1
before_decimal = before_decimal.split(":")
until before_decimal.empty?
whole_part += (Float(before_decimal.pop) * multiplier)
multiplier *= 60
end
result = whole_part
result += Float("." + after_decimal) unless after_decimal.nil?
result *= -1 if value[0] == "-"
result
end
end
end
end
@@ -0,0 +1,99 @@
require "psych"
require "base64"
module SafeYAML
class PsychHandler < Psych::Handler
def initialize(options, &block)
@options = SafeYAML::OPTIONS.merge(options || {})
@block = block
@initializers = @options[:custom_initializers] || {}
@anchors = {}
@stack = []
@current_key = nil
@result = nil
@begun = false
end
def result
@begun ? @result : false
end
def add_to_current_structure(value, anchor=nil, quoted=nil, tag=nil)
value = Transform.to_proper_type(value, quoted, tag, @options)
@anchors[anchor] = value if anchor
if !@begun
@begun = true
@result = value
@current_structure = @result
return
end
if @current_structure.respond_to?(:<<)
@current_structure << value
elsif @current_structure.respond_to?(:[]=)
if @current_key.nil?
@current_key = value
else
if @current_key == "<<"
@current_structure.merge!(value)
else
@current_structure[@current_key] = value
end
@current_key = nil
end
else
raise "Don't know how to add to a #{@current_structure.class}!"
end
end
def end_current_structure
@stack.pop
@current_structure = @stack.last
end
def streaming?
true
end
# event handlers
def alias(anchor)
add_to_current_structure(@anchors[anchor])
end
def scalar(value, anchor, tag, plain, quoted, style)
add_to_current_structure(value, anchor, quoted, tag)
end
def end_document(implicit)
@block.call(@result)
end
def start_mapping(anchor, tag, implicit, style)
map = @initializers.include?(tag) ? @initializers[tag].call : {}
self.add_to_current_structure(map, anchor)
@current_structure = map
@stack.push(map)
end
def end_mapping
self.end_current_structure()
end
def start_sequence(anchor, tag, implicit, style)
seq = @initializers.include?(tag) ? @initializers[tag].call : []
self.add_to_current_structure(seq, anchor)
@current_structure = seq
@stack.push(seq)
end
def end_sequence
self.end_current_structure()
end
end
end
@@ -0,0 +1,52 @@
module SafeYAML
class PsychResolver < Resolver
NODE_TYPES = {
Psych::Nodes::Document => :root,
Psych::Nodes::Mapping => :map,
Psych::Nodes::Sequence => :seq,
Psych::Nodes::Scalar => :scalar,
Psych::Nodes::Alias => :alias
}.freeze
def initialize(options={})
super
@aliased_nodes = {}
end
def resolve_root(root)
resolve_seq(root).first
end
def resolve_alias(node)
resolve_node(@aliased_nodes[node.anchor])
end
def native_resolve(node)
@visitor ||= SafeYAML::SafeToRubyVisitor.new(self)
@visitor.accept(node)
end
def get_node_type(node)
NODE_TYPES[node.class]
end
def get_node_tag(node)
node.tag
end
def get_node_value(node)
@aliased_nodes[node.anchor] = node if node.respond_to?(:anchor) && node.anchor
case get_node_type(node)
when :root, :map, :seq
node.children
when :scalar
node.value
end
end
def value_is_quoted?(node)
node.quoted
end
end
end
@@ -0,0 +1,94 @@
module SafeYAML
class Resolver
def initialize(options)
@options = SafeYAML::OPTIONS.merge(options || {})
@whitelist = @options[:whitelisted_tags] || []
@initializers = @options[:custom_initializers] || {}
@raise_on_unknown_tag = @options[:raise_on_unknown_tag]
end
def resolve_node(node)
return node if !node
return self.native_resolve(node) if tag_is_whitelisted?(self.get_node_tag(node))
case self.get_node_type(node)
when :root
resolve_root(node)
when :map
resolve_map(node)
when :seq
resolve_seq(node)
when :scalar
resolve_scalar(node)
when :alias
resolve_alias(node)
else
raise "Don't know how to resolve this node: #{node.inspect}"
end
end
def resolve_map(node)
tag = get_and_check_node_tag(node)
hash = @initializers.include?(tag) ? @initializers[tag].call : {}
map = normalize_map(self.get_node_value(node))
# Take the "<<" key nodes first, as these are meant to approximate a form of inheritance.
inheritors = map.select { |key_node, value_node| resolve_node(key_node) == "<<" }
inheritors.each do |key_node, value_node|
merge_into_hash(hash, resolve_node(value_node))
end
# All that's left should be normal (non-"<<") nodes.
(map - inheritors).each do |key_node, value_node|
hash[resolve_node(key_node)] = resolve_node(value_node)
end
return hash
end
def resolve_seq(node)
seq = self.get_node_value(node)
tag = get_and_check_node_tag(node)
arr = @initializers.include?(tag) ? @initializers[tag].call : []
seq.inject(arr) { |array, n| array << resolve_node(n) }
end
def resolve_scalar(node)
Transform.to_proper_type(self.get_node_value(node), self.value_is_quoted?(node), get_and_check_node_tag(node), @options)
end
def get_and_check_node_tag(node)
tag = self.get_node_tag(node)
SafeYAML.tag_safety_check!(tag, @options)
tag
end
def tag_is_whitelisted?(tag)
@whitelist.include?(tag)
end
def options
@options
end
private
def normalize_map(map)
# Syck creates Hashes from maps.
if map.is_a?(Hash)
map.inject([]) { |arr, key_and_value| arr << key_and_value }
# Psych is really weird; it flattens out a Hash completely into: [key, value, key, value, ...]
else
map.each_slice(2).to_a
end
end
def merge_into_hash(hash, array)
array.each do |key, value|
hash[key] = value
end
end
end
end
@@ -0,0 +1,29 @@
module SafeYAML
class SafeToRubyVisitor < Psych::Visitors::ToRuby
INITIALIZE_ARITY = superclass.instance_method(:initialize).arity
def initialize(resolver)
case INITIALIZE_ARITY
when 2
# https://github.com/tenderlove/psych/blob/v2.0.0/lib/psych/visitors/to_ruby.rb#L14-L28
loader = Psych::ClassLoader.new
scanner = Psych::ScalarScanner.new(loader)
super(scanner, loader)
else
super()
end
@resolver = resolver
end
def accept(node)
if node.tag
SafeYAML.tag_safety_check!(node.tag, @resolver.options)
return super
end
@resolver.resolve_node(node)
end
end
end
@@ -0,0 +1,39 @@
require 'safe_yaml/load'
require 'yaml/store'
module SafeYAML
class Store < YAML::Store
# Override YAML::Store#initialize to accept additional option
# +safe_yaml_opts+.
def initialize(file_name, yaml_opts = {}, safe_yaml_opts = {})
@safe_yaml_opts = safe_yaml_opts
super(file_name, yaml_opts)
end
# Override YAML::Store#load to use SafeYAML.load instead of
# YAML.load (via #safe_yaml_load).
#--
# PStore#load is private, while YAML::Store#load is public.
#++
def load(content)
table = safe_yaml_load(content)
table == false ? {} : table
end
private
if SafeYAML::YAML_ENGINE == 'psych'
def safe_yaml_load(content)
SafeYAML.load(content, nil, @safe_yaml_opts)
end
else
def safe_yaml_load(content)
SafeYAML.load(content, @safe_yaml_opts)
end
end
end
end
@@ -0,0 +1,36 @@
# Hack to JRuby 1.8's YAML Parser Yecht
#
# This file is always loaded AFTER either syck or psych are already
# loaded. It then looks at what constants are available and creates
# a consistent view on all rubys.
#
# Taken from rubygems and modified.
# See https://github.com/rubygems/rubygems/blob/master/lib/rubygems/syck_hack.rb
module YAML
# In newer 1.9.2, there is a Syck toplevel constant instead of it
# being underneith YAML. If so, reference it back under YAML as
# well.
if defined? ::Syck
# for tests that change YAML::ENGINE
# 1.8 does not support the second argument to const_defined?
remove_const :Syck rescue nil
Syck = ::Syck
# JRuby's "Syck" is called "Yecht"
elsif defined? YAML::Yecht
Syck = YAML::Yecht
end
end
# Sometime in the 1.9 dev cycle, the Syck constant was moved from under YAML
# to be a toplevel constant. So gemspecs created under these versions of Syck
# will have references to Syck::DefaultKey.
#
# So we need to be sure that we reference Syck at the toplevel too so that
# we can always load these kind of gemspecs.
#
if !defined?(Syck)
Syck = YAML::Syck
end
@@ -0,0 +1,43 @@
# This is, admittedly, pretty insane. Fundamentally the challenge here is this: if we want to allow
# whitelisting of tags (while still leveraging Syck's internal functionality), then we have to
# change how Syck::Node#transform works. But since we (SafeYAML) do not control instantiation of
# Syck::Node objects, we cannot, for example, subclass Syck::Node and override #tranform the "easy"
# way. So the only choice is to monkeypatch, like this. And the only way to make this work
# recursively with potentially call-specific options (that my feeble brain can think of) is to set
# pseudo-global options on the first call and unset them once the recursive stack has fully unwound.
monkeypatch = <<-EORUBY
class Node
@@safe_transform_depth = 0
@@safe_transform_whitelist = nil
def safe_transform(options={})
begin
@@safe_transform_depth += 1
@@safe_transform_whitelist ||= options[:whitelisted_tags]
if self.type_id
SafeYAML.tag_safety_check!(self.type_id, options)
return unsafe_transform if @@safe_transform_whitelist.include?(self.type_id)
end
SafeYAML::SyckResolver.new.resolve_node(self)
ensure
@@safe_transform_depth -= 1
if @@safe_transform_depth == 0
@@safe_transform_whitelist = nil
end
end
end
alias_method :unsafe_transform, :transform
alias_method :transform, :safe_transform
end
EORUBY
if defined?(YAML::Syck::Node)
YAML::Syck.module_eval monkeypatch
else
Syck.module_eval monkeypatch
end
@@ -0,0 +1,38 @@
module SafeYAML
class SyckResolver < Resolver
QUOTE_STYLES = [
:quote1,
:quote2
].freeze
NODE_TYPES = {
Hash => :map,
Array => :seq,
String => :scalar
}.freeze
def initialize(options={})
super
end
def native_resolve(node)
node.transform(self.options)
end
def get_node_type(node)
NODE_TYPES[node.value.class]
end
def get_node_tag(node)
node.type_id
end
def get_node_value(node)
node.value
end
def value_is_quoted?(node)
QUOTE_STYLES.include?(node.instance_variable_get(:@style))
end
end
end
@@ -0,0 +1,41 @@
require 'base64'
module SafeYAML
class Transform
TRANSFORMERS = [
Transform::ToSymbol.new,
Transform::ToInteger.new,
Transform::ToFloat.new,
Transform::ToNil.new,
Transform::ToBoolean.new,
Transform::ToDate.new
]
def self.to_guessed_type(value, quoted=false, options=nil)
return value if quoted
if value.is_a?(String)
TRANSFORMERS.each do |transformer|
success, transformed_value = transformer.method(:transform?).arity == 1 ?
transformer.transform?(value) :
transformer.transform?(value, options)
return transformed_value if success
end
end
value
end
def self.to_proper_type(value, quoted=false, tag=nil, options=nil)
case tag
when "tag:yaml.org,2002:binary", "x-private:binary", "!binary"
decoded = Base64.decode64(value)
decoded = decoded.force_encoding(value.encoding) if decoded.respond_to?(:force_encoding)
decoded
else
self.to_guessed_type(value, quoted, options)
end
end
end
end
@@ -0,0 +1,21 @@
module SafeYAML
class Transform
class ToBoolean
include TransformationMap
set_predefined_values({
"yes" => true,
"on" => true,
"true" => true,
"no" => false,
"off" => false,
"false" => false
})
def transform?(value)
return false if value.length > 5
return PREDEFINED_VALUES.include?(value), PREDEFINED_VALUES[value]
end
end
end
end
@@ -0,0 +1,13 @@
module SafeYAML
class Transform
class ToDate
def transform?(value)
return true, Date.parse(value) if Parse::Date::DATE_MATCHER.match(value)
return true, Parse::Date.value(value) if Parse::Date::TIME_MATCHER.match(value)
false
rescue ArgumentError
return true, value
end
end
end
end
@@ -0,0 +1,33 @@
module SafeYAML
class Transform
class ToFloat
Infinity = 1.0 / 0.0
NaN = 0.0 / 0.0
PREDEFINED_VALUES = {
".inf" => Infinity,
".Inf" => Infinity,
".INF" => Infinity,
"-.inf" => -Infinity,
"-.Inf" => -Infinity,
"-.INF" => -Infinity,
".nan" => NaN,
".NaN" => NaN,
".NAN" => NaN,
}.freeze
MATCHER = /\A[-+]?(?:\d[\d_]*)?\.[\d_]+(?:[eE][-+][\d]+)?\Z/.freeze
def transform?(value)
return true, Float(value) if MATCHER.match(value)
try_edge_cases?(value)
end
def try_edge_cases?(value)
return true, PREDEFINED_VALUES[value] if PREDEFINED_VALUES.include?(value)
return true, Parse::Sexagesimal.value(value) if Parse::Sexagesimal::FLOAT_MATCHER.match(value)
return false
end
end
end
end
@@ -0,0 +1,26 @@
module SafeYAML
class Transform
class ToInteger
MATCHERS = Deep.freeze([
/\A[-+]?(0|([1-9][0-9_,]*))\Z/, # decimal
/\A0[0-7]+\Z/, # octal
/\A0x[0-9a-f]+\Z/i, # hexadecimal
/\A0b[01_]+\Z/ # binary
])
def transform?(value)
MATCHERS.each_with_index do |matcher, idx|
value = value.gsub(/[_,]/, "") if idx == 0
return true, Integer(value) if matcher.match(value)
end
try_edge_cases?(value)
end
def try_edge_cases?(value)
return true, Parse::Hexadecimal.value(value) if Parse::Hexadecimal::MATCHER.match(value)
return true, Parse::Sexagesimal.value(value) if Parse::Sexagesimal::INTEGER_MATCHER.match(value)
return false
end
end
end
end
@@ -0,0 +1,18 @@
module SafeYAML
class Transform
class ToNil
include TransformationMap
set_predefined_values({
"" => nil,
"~" => nil,
"null" => nil
})
def transform?(value)
return false if value.length > 4
return PREDEFINED_VALUES.include?(value), PREDEFINED_VALUES[value]
end
end
end
end
@@ -0,0 +1,17 @@
module SafeYAML
class Transform
class ToSymbol
def transform?(value, options=SafeYAML::OPTIONS)
if options[:deserialize_symbols] && value =~ /\A:./
if value =~ /\A:(["'])(.*)\1\Z/
return true, $2.sub(/^:/, "").to_sym
else
return true, value.sub(/^:/, "").to_sym
end
end
return false
end
end
end
end
@@ -0,0 +1,47 @@
module SafeYAML
class Transform
module TransformationMap
def self.included(base)
base.extend(ClassMethods)
end
class CaseAgnosticMap < Hash
def initialize(*args)
super
end
def include?(key)
super(key.downcase)
end
def [](key)
super(key.downcase)
end
# OK, I actually don't think it's all that important that this map be
# frozen.
def freeze
self
end
end
module ClassMethods
def set_predefined_values(predefined_values)
if SafeYAML::YAML_ENGINE == "syck"
expanded_map = predefined_values.inject({}) do |hash, (key, value)|
hash[key] = value
hash[key.capitalize] = value
hash[key.upcase] = value
hash
end
else
expanded_map = CaseAgnosticMap.new
expanded_map.merge!(predefined_values)
end
self.const_set(:PREDEFINED_VALUES, expanded_map.freeze)
end
end
end
end
end
@@ -0,0 +1,3 @@
module SafeYAML
VERSION = "1.0.5"
end