Module: Philiprehberger::SafeYaml::Loader

Defined in:
lib/philiprehberger/safe_yaml/loader.rb

Overview

Wraps YAML.safe_load with safe defaults and size limits.

Class Method Summary collapse

Class Method Details

.deep_merge(base, overlay) ⇒ Hash

Deep merges two hashes, with the overlay taking precedence.

Parameters:

  • base (Hash)

    the default values

  • overlay (Hash)

    the parsed values that override defaults

Returns:

  • (Hash)

    the merged result



89
90
91
92
93
94
95
96
97
# File 'lib/philiprehberger/safe_yaml/loader.rb', line 89

def self.deep_merge(base, overlay)
  base.merge(overlay) do |_key, old_val, new_val|
    if old_val.is_a?(Hash) && new_val.is_a?(Hash)
      deep_merge(old_val, new_val)
    else
      new_val
    end
  end
end

.dump(data, permitted_classes: []) ⇒ String

Safely dumps data to a YAML string with type validation.

Parameters:

  • data (Object)

    the data to serialize

  • permitted_classes (Array<Class>) (defaults to: [])

    additional classes allowed for serialization

Returns:

  • (String)

    the YAML string

Raises:

  • (Error)

    if data contains unsafe types



105
106
107
108
# File 'lib/philiprehberger/safe_yaml/loader.rb', line 105

def self.dump(data, permitted_classes: [])
  validate_dumpable!(data, permitted_classes)
  YAML.dump(data)
end

.dump_file(data, path, permitted_classes: []) ⇒ String

Safely dumps data to a YAML file with type validation.

Parameters:

  • data (Object)

    the data to serialize

  • path (String)

    path to write the YAML file

  • permitted_classes (Array<Class>) (defaults to: [])

    additional classes allowed for serialization

Returns:

  • (String)

    the YAML string written to the file

Raises:

  • (Error)

    if data contains unsafe types



117
118
119
120
121
# File 'lib/philiprehberger/safe_yaml/loader.rb', line 117

def self.dump_file(data, path, permitted_classes: [])
  content = dump(data, permitted_classes: permitted_classes)
  File.write(path, content)
  content
end

.load(string, permitted_classes: [], max_aliases: 0, max_size: nil) ⇒ Object

Safely loads a YAML string with restricted types.

Parameters:

  • string (String)

    the YAML string to parse

  • permitted_classes (Array<Class>) (defaults to: [])

    classes allowed during deserialization

  • max_aliases (Integer) (defaults to: 0)

    maximum number of aliases allowed (0 disables aliases)

  • max_size (Integer, nil) (defaults to: nil)

    maximum byte size of the input string

Returns:

  • (Object)

    the parsed YAML object

Raises:

  • (SizeError)

    if string exceeds max_size

  • (Error)

    if alias count exceeds max_aliases

  • (Psych::DisallowedClass)

    if YAML contains disallowed classes



20
21
22
23
24
25
26
27
28
29
30
# File 'lib/philiprehberger/safe_yaml/loader.rb', line 20

def self.load(string, permitted_classes: [], max_aliases: 0, max_size: nil)
  validate_size!(string, max_size)
  validate_alias_count!(string, max_aliases) if max_aliases.positive?

  YAML.safe_load(
    string,
    permitted_classes: permitted_classes,
    permitted_symbols: [],
    aliases: max_aliases.positive?
  )
end

.load_file(path, **opts) ⇒ Object

Safely loads a YAML file with restricted types.

Parameters:

  • path (String)

    path to the YAML file

  • opts (Hash)

    options forwarded to load

Returns:

  • (Object)

    the parsed YAML object

Raises:

  • (SizeError)

    if file content exceeds max_size

  • (Errno::ENOENT)

    if the file does not exist



39
40
41
42
# File 'lib/philiprehberger/safe_yaml/loader.rb', line 39

def self.load_file(path, **opts)
  content = File.read(path)
  load(content, **opts)
end

.sanitize(string) ⇒ String

Sanitizes a YAML string by stripping full-line comments and normalizing whitespace.

Parameters:

  • string (String)

    the raw YAML string

Returns:

  • (String)

    the cleaned YAML string

Raises:

  • (Error)

    if the sanitized string is not valid YAML



77
78
79
80
81
82
# File 'lib/philiprehberger/safe_yaml/loader.rb', line 77

def self.sanitize(string)
  lines = string.each_line.grep_v(/\A\s*#/)
  cleaned = lines.join.gsub(/[^\S\n]+$/, '')
  YAML.safe_load(cleaned) # validate syntax
  cleaned
end