Class: PuppetSyntax::Hiera

Inherits:
Object
  • Object
show all
Defined in:
lib/puppet-syntax/hiera.rb

Instance Method Summary collapse

Instance Method Details

#check(filelist) ⇒ Object



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/puppet-syntax/hiera.rb', line 89

def check(filelist)
  raise 'Expected an array of files' unless filelist.is_a?(Array)

  errors = []

  yamlargs = (Psych::VERSION >= '4.0') ? { aliases: true } : {}

  filelist.each do |hiera_file|
    begin
      yamldata = YAML.load_file(hiera_file, **yamlargs)
    rescue Exception => e
      errors << "ERROR: Failed to parse #{hiera_file}: #{e}"
      next
    end
    next unless yamldata

    unless yamldata.is_a?(Hash)
      errors << "ERROR: #{hiera_file} doesn't contain a valid Hash, datatype is #{yamldata.class}"
      next
    end

    yamldata.each do |k, v|
      if PuppetSyntax.check_hiera_keys
        key_msg = check_hiera_key(k)
        errors << "WARNING: #{hiera_file}: Key :#{k}: #{key_msg}" if key_msg
      end
      if PuppetSyntax.check_hiera_data
        check_hiera_data(k, v).each do |value_msg|
          errors << "WARNING: #{hiera_file}: Key :#{k}: #{value_msg}"
        end
      end
      eyaml_msg = check_eyaml_data(k, v)
      errors << "WARNING: #{hiera_file}: #{eyaml_msg}" if eyaml_msg
    end
  end

  errors.map! { |e| e.to_s }

  errors
end

#check_eyaml_blob(val) ⇒ Object



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/puppet-syntax/hiera.rb', line 55

def check_eyaml_blob(val)
  # strip newlines and extra spaces
  val.gsub!(/\s+/, '')

  encodes_length = val.scan('ENC[').length

  # Return if there's no encoded material
  return if encodes_length == 0

  found_encodes = val.scan(/ENC\[([^,\]]+,)?([^\]]+)?\]/)

  return 'has unterminated eyaml value' unless found_encodes.length == encodes_length

  known_methods = %w[PKCS7 GPG GKMS KMS TWOFAC SecretBox VAULT GCPKMS RSA SSHAGENT VAULT_RS cli]

  found_encodes.each do |match|
    # if no method is found we use the default PKCS7 method
    method = match[0]
    method = +'PKCS7' if method.nil?
    method.delete_suffix!(',')
    base64 = match[1]

    return 'has invalid eyaml encoded format' if base64.nil?

    return "has unknown eyaml method #{method}" unless known_methods.include? method

    return 'has unpadded or truncated base64 data' unless base64.length % 4 == 0

    return 'has corrupt base64 data' unless base64.match?(%r{^[a-zA-Z0-9+/=]+$})
  end
  # all good
  nil
end

#check_eyaml_data(name, val) ⇒ Object

Recurse through complex data structures. Return on first error.



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/puppet-syntax/hiera.rb', line 36

def check_eyaml_data(name, val)
  error = nil
  if val.is_a? String
    err = check_eyaml_blob(val)
    error = "Key #{name} #{err}" if err
  elsif val.is_a? Array
    val.each_with_index do |v, idx|
      error = check_eyaml_data("#{name}[#{idx}]", v)
      break if error
    end
  elsif val.is_a? Hash
    val.each do |k, v|
      error = check_eyaml_data("#{name}['#{k}']", v)
      break if error
    end
  end
  error
end

#check_hiera_data(_key, value) ⇒ Object



25
26
27
28
29
30
31
32
33
# File 'lib/puppet-syntax/hiera.rb', line 25

def check_hiera_data(_key, value)
  # using filter_map to remove nil values
  # there will be nil values if check_broken_function_call didn't return a string
  # this is a shorthand for filter.compact
  # https://blog.saeloun.com/2019/05/25/ruby-2-7-enumerable-filter-map/
  keys_and_values(value).filter_map do |element|
    check_broken_function_call(element)
  end
end

#check_hiera_key(key) ⇒ Object



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/puppet-syntax/hiera.rb', line 8

def check_hiera_key(key)
  if key.is_a? Symbol
    if key.to_s.start_with?(':')
      "Puppet automatic lookup will not use leading '::'"
    elsif !/^[a-z]+$/.match?(key) # we allow Hiera's own configuration
      'Puppet automatic lookup will not look up symbols'
    end
  elsif !/^([a-z][a-z0-9_]+::)*[a-z0-9_][a-zA-Z0-9_]+$/.match?(key) # adapted from Puppet docs combining namespaced and un-namespaced regex
    return 'Looks like a missing colon' if /[^:]:[^:]/.match?(key)

    # be extra helpful

    'Not a valid Puppet variable name for automatic lookup'

  end
end