Module: Ruby::Rego::Builtins::Yaml::ScalarResolver

Defined in:
lib/ruby/rego/builtins/yaml/scalar_resolver.rb,
lib/ruby/rego/builtins/yaml/scalar_resolver/tags.rb,
lib/ruby/rego/builtins/yaml/scalar_resolver/numbers.rb,
lib/ruby/rego/builtins/yaml/scalar_resolver/document.rb

Overview

Walks a parsed Psych document into JSON-compatible Ruby values: node dispatch, anchors/aliases, << merge keys, JSON object-key stringification, and the non-finite/DoS rejection pass. Lives apart from the resolution core so the main file stays under RubyCritic’s complexity budget; constants and self-dispatched helpers (scalar_value, MAX_DEPTH, …) resolve via lexical scope / the module.

Defined Under Namespace

Classes: ResolveError

Constant Summary collapse

MAX_YAML_SOURCE =

DoS bounds. OPA relies on Go runtime limits absent here, so an untrusted document exceeding these yields undefined rather than exhausting memory/stack: source byte length, nesting depth (guards a Ruby stack overflow), and total expanded nodes (guards against alias-expansion “billion laughs” bombs).

1_000_000
MAX_DEPTH =
1_000
MAX_NODES =
5_000_000
INT64_MIN =

go-yaml resolves an integer via strconv ParseInt (int64) then ParseUint (uint64). ParseUint rejects a sign, so an explicitly +/- signed value is bounded by int64; only an unsigned value may reach uint64 max. A prefixed 0x/0o/0b literal beyond the accepted range can’t be reparsed as a float, so it falls back to its string text; an out-of-range !!int (any base) is undefined. A bare integer key in the positive uint64-only band decodes to a Go uint64, which sigs.k8s.io/yaml cannot stringify as a JSON key, so that document is undefined.

-(2**63)
INT64_MAX =
(2**63) - 1
UINT64_MAX =
(2**64) - 1
FLOAT64_FINITE_MAX_DIGITS =

A decimal token of at most this many characters is below 10**308 (< float64 max, ~1.8e308), so it cannot overflow float64 — decimal_overflows_float64? uses this to skip the Float() overflow probe for the common (small-integer) case.

308
RESOLVE_MAP =

Plain scalars with a fixed meaning (yaml.v2 resolveMap).

{
  "" => nil, "~" => nil, "null" => nil, "Null" => nil, "NULL" => nil,
  "y" => true, "Y" => true, "yes" => true, "Yes" => true, "YES" => true,
  "true" => true, "True" => true, "TRUE" => true, "on" => true, "On" => true, "ON" => true,
  "n" => false, "N" => false, "no" => false, "No" => false, "NO" => false,
  "false" => false, "False" => false, "FALSE" => false, "off" => false, "Off" => false, "OFF" => false,
  ".nan" => Float::NAN, ".NaN" => Float::NAN, ".NAN" => Float::NAN,
  ".inf" => Float::INFINITY, ".Inf" => Float::INFINITY, ".INF" => Float::INFINITY,
  "+.inf" => Float::INFINITY, "+.Inf" => Float::INFINITY, "+.INF" => Float::INFINITY,
  "-.inf" => -Float::INFINITY, "-.Inf" => -Float::INFINITY, "-.INF" => -Float::INFINITY
}.freeze
TAG_PREFIX =

Prefix of an explicit core YAML schema tag (e.g. “tag:yaml.org,2002:int”).

"tag:yaml.org,2002:"
NUMBER_LEADS =

First-byte hints that a scalar might be a number (yaml.v2 resolveTable).

"+-.0123456789"
FLOAT_RE =
/\A[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?\z/

Class Method Summary collapse

Class Method Details

.ambiguous?(string) ⇒ bool

True when a plain scalar would resolve to something other than a string (so the emitter must quote it).

Returns:

  • (bool)


80
81
82
83
# File 'lib/ruby/rego/builtins/yaml/scalar_resolver.rb', line 80

def self.ambiguous?(string)
  resolved = resolve(string)
  !(resolved.is_a?(String) && resolved == string)
end

.load(string) ⇒ Object

Parses YAML and returns the first document as JSON-compatible Ruby values.

Returns:

  • (Object)

Raises:



87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/ruby/rego/builtins/yaml/scalar_resolver.rb', line 87

def self.load(string)
  raise ResolveError, "yaml too long" if string.bytesize > MAX_YAML_SOURCE

  document = Psych.parse_stream(string).children.first
  # An absent document or one with no root node is an empty document, which
  # decodes to null (matching yaml.v2 / sigs.k8s.io/yaml), not an error.
  return nil if document.nil? || document.root.nil?

  value = build(document.root, {}, 0)
  reject_non_finite(value, [MAX_NODES], 0)
  value
end

.resolve(string) ⇒ Object

Resolves a plain scalar string to its Ruby value (or itself, as a string).

Returns:

  • (Object)


70
71
72
73
74
75
# File 'lib/ruby/rego/builtins/yaml/scalar_resolver.rb', line 70

def self.resolve(string)
  return RESOLVE_MAP[string] if RESOLVE_MAP.key?(string)
  return string unless numeric_lead?(string)

  numeric(string) || string
end