abide_dev_utils 0.11.0 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +18 -31
  3. data/lib/abide_dev_utils/cem/benchmark.rb +335 -136
  4. data/lib/abide_dev_utils/cem/generate/coverage_report.rb +380 -0
  5. data/lib/abide_dev_utils/cem/generate/reference.rb +238 -35
  6. data/lib/abide_dev_utils/cem/generate.rb +5 -4
  7. data/lib/abide_dev_utils/cem/hiera_data/mapping_data/map_data.rb +110 -0
  8. data/lib/abide_dev_utils/cem/hiera_data/mapping_data/mixins.rb +46 -0
  9. data/lib/abide_dev_utils/cem/hiera_data/mapping_data.rb +146 -0
  10. data/lib/abide_dev_utils/cem/hiera_data/resource_data/control.rb +127 -0
  11. data/lib/abide_dev_utils/cem/hiera_data/resource_data/parameters.rb +90 -0
  12. data/lib/abide_dev_utils/cem/hiera_data/resource_data/resource.rb +102 -0
  13. data/lib/abide_dev_utils/cem/hiera_data/resource_data.rb +310 -0
  14. data/lib/abide_dev_utils/cem/hiera_data.rb +7 -0
  15. data/lib/abide_dev_utils/cem/mapping/mapper.rb +161 -34
  16. data/lib/abide_dev_utils/cem/validate/resource_data.rb +33 -0
  17. data/lib/abide_dev_utils/cem/validate.rb +10 -0
  18. data/lib/abide_dev_utils/cem.rb +0 -1
  19. data/lib/abide_dev_utils/cli/cem.rb +20 -2
  20. data/lib/abide_dev_utils/dot_number_comparable.rb +75 -0
  21. data/lib/abide_dev_utils/errors/cem.rb +10 -0
  22. data/lib/abide_dev_utils/ppt/class_utils.rb +1 -1
  23. data/lib/abide_dev_utils/ppt/code_gen/data_types.rb +64 -0
  24. data/lib/abide_dev_utils/ppt/code_gen/generate.rb +15 -0
  25. data/lib/abide_dev_utils/ppt/code_gen/resource.rb +59 -0
  26. data/lib/abide_dev_utils/ppt/code_gen/resource_types/base.rb +93 -0
  27. data/lib/abide_dev_utils/ppt/code_gen/resource_types/class.rb +17 -0
  28. data/lib/abide_dev_utils/ppt/code_gen/resource_types/manifest.rb +16 -0
  29. data/lib/abide_dev_utils/ppt/code_gen/resource_types/parameter.rb +16 -0
  30. data/lib/abide_dev_utils/ppt/code_gen/resource_types/strings.rb +13 -0
  31. data/lib/abide_dev_utils/ppt/code_gen/resource_types.rb +6 -0
  32. data/lib/abide_dev_utils/ppt/code_gen.rb +15 -0
  33. data/lib/abide_dev_utils/ppt/code_introspection.rb +102 -0
  34. data/lib/abide_dev_utils/ppt/hiera.rb +4 -1
  35. data/lib/abide_dev_utils/ppt/puppet_module.rb +2 -1
  36. data/lib/abide_dev_utils/ppt.rb +3 -0
  37. data/lib/abide_dev_utils/version.rb +1 -1
  38. data/lib/abide_dev_utils/xccdf/parser/helpers.rb +146 -0
  39. data/lib/abide_dev_utils/xccdf/parser/objects.rb +87 -144
  40. data/lib/abide_dev_utils/xccdf/parser.rb +5 -0
  41. data/lib/abide_dev_utils/xccdf/utils.rb +89 -0
  42. data/lib/abide_dev_utils/xccdf.rb +193 -63
  43. metadata +27 -3
  44. data/lib/abide_dev_utils/cem/coverage_report.rb +0 -348
@@ -1,348 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'date'
4
- require 'json'
5
- require 'pathname'
6
- require 'yaml'
7
- require 'abide_dev_utils/ppt'
8
- require 'abide_dev_utils/validate'
9
- require 'abide_dev_utils/cem/benchmark'
10
-
11
- module AbideDevUtils
12
- module CEM
13
- # Methods and objects used to construct a report of what CEM enforces versus what
14
- # the various compliance frameworks expect to be enforced.
15
- module CoverageReport
16
- # def self.generate(outfile: 'cem_coverage.yaml', **_filters)
17
- # pupmod = AbideDevUtils::Ppt::PuppetModule.new
18
- # # filter = Filter.new(pupmod, **filters)
19
- # benchmarks = AbideDevUtils::CEM::Benchmark.benchmarks_from_puppet_module(pupmod)
20
- # Report.new(benchmarks).generate(outfile: outfile)
21
- # end
22
-
23
- def self.basic_coverage(format_func: :to_yaml, ignore_benchmark_errors: false)
24
- pupmod = AbideDevUtils::Ppt::PuppetModule.new
25
- # filter = Filter.new(pupmod, **filters)
26
- benchmarks = AbideDevUtils::CEM::Benchmark.benchmarks_from_puppet_module(pupmod,
27
- ignore_all_errors: ignore_benchmark_errors)
28
- benchmarks.map do |b|
29
- AbideDevUtils::CEM::CoverageReport::BenchmarkReport.new(b).basic_coverage.send(format_func)
30
- end
31
- end
32
-
33
- class Filter
34
- KEY_FACT_MAP = {
35
- os_family: 'os.family',
36
- os_name: 'os.name',
37
- os_release_major: 'os.release.major',
38
- }.freeze
39
-
40
- attr_reader(*KEY_FACT_MAP.keys)
41
-
42
- def initialize(pupmod, **filters)
43
- @pupmod = pupmod
44
- @benchmark = filters[:benchmark]
45
- @profile = filters[:profile]
46
- @level = filters[:level]
47
- KEY_FACT_MAP.each_key do |k|
48
- instance_variable_set "@#{k}", filters[k]
49
- end
50
- end
51
-
52
- def resource_data
53
- @resource_data ||= find_resource_data
54
- end
55
-
56
- def mapping_data
57
- @mapping_data ||= find_mapping_data
58
- end
59
-
60
- private
61
-
62
- def find_resource_data
63
- fact_array = fact_array_for(:os_family, :os_name, :os_release_major)
64
- @pupmod.hiera_conf.local_hiera_files_with_facts(*fact_array, hierarchy_name: 'Resource Data').map do |f|
65
- YAML.load_file(f.path)
66
- end
67
- rescue NoMethodError
68
- @pupmod.hiera_conf.local_hiera_files(hierarchy_name: 'Resource Data').map { |f| YAML.load_file(f.path) }
69
- end
70
-
71
- def find_mapping_data
72
- fact_array = fact_array_for(:os_name, :os_release_major)
73
- begin
74
- data_array = @pupmod.hiera_conf.local_hiera_files_with_facts(*fact_array, hierarchy_name: 'Mapping Data').map do |f|
75
- YAML.load_file(f.path)
76
- end
77
- rescue NoMethodError
78
- data_array = @pupmod.hiera_conf.local_hiera_files(hierarchy_name: 'Mapping Data').map { |f| YAML.load_file(f.path) }
79
- end
80
- filter_mapping_data_array_by_benchmark!(data_array)
81
- filter_mapping_data_array_by_profile!(data_array)
82
- filter_mapping_data_array_by_level!(data_array)
83
- data_array
84
- end
85
-
86
- def filter_mapping_data_array_by_benchmark!(data_array)
87
- return unless @benchmark
88
-
89
- data_array.select! do |d|
90
- d.keys.all? do |k|
91
- k == 'benchmark' || k.match?(/::#{@benchmark}::/)
92
- end
93
- end
94
- end
95
-
96
- def filter_mapping_data_array_by_profile!(data_array)
97
- return unless @profile
98
-
99
- data_array.reject! { |d| nested_hash_value(d, @profile).nil? }
100
- end
101
-
102
- def filter_mapping_data_array_by_level!(data_array)
103
- return unless @level
104
-
105
- data_array.reject! { |d| nested_hash_value(d, @level).nil? }
106
- end
107
-
108
- def nested_hash_value(obj, key)
109
- if obj.respond_to?(:key?) && obj.key?(key)
110
- obj[key]
111
- elsif obj.respond_to?(:each)
112
- r = nil
113
- obj.find { |*a| r = nested_hash_value(a.last, key) }
114
- r
115
- end
116
- end
117
-
118
- def filter_stig_mapping_data(data_array); end
119
-
120
- def fact_array_for(*keys)
121
- keys.each_with_object([]) { |(k, _), a| a << fact_filter_value(k) }.compact
122
- end
123
-
124
- def fact_filter_value(key)
125
- value = instance_variable_get("@#{key}")
126
- return if value.nil? || value.empty?
127
-
128
- [KEY_FACT_MAP[key], value]
129
- end
130
- end
131
-
132
- class OldReport
133
- def initialize(benchmarks)
134
- @benchmarks = benchmarks
135
- end
136
-
137
- def self.generate
138
- coverage = {}
139
- coverage['classes'] = {}
140
- all_cap = ClassUtils.find_all_classes_and_paths(puppet_class_dir)
141
- invalid_classes = find_invalid_classes(all_cap)
142
- valid_classes = find_valid_classes(all_cap, invalid_classes)
143
- coverage['classes']['invalid'] = invalid_classes
144
- coverage['classes']['valid'] = valid_classes
145
- hiera = YAML.safe_load(File.open(hiera_path))
146
- profile&.gsub!(/^profile_/, '') unless profile.nil?
147
-
148
- matcher = profile.nil? ? /^profile_/ : /^profile_#{profile}/
149
- hiera.each do |k, v|
150
- key_base = k.split('::')[-1]
151
- coverage['benchmark'] = v if key_base == 'title'
152
- next unless key_base.match?(matcher)
153
-
154
- coverage[key_base] = generate_uncovered_data(v, valid_classes)
155
- end
156
- coverage
157
- end
158
-
159
- def self.generate_uncovered_data(ctrl_list, valid_classes)
160
- out_hash = {}
161
- out_hash[:num_total] = ctrl_list.length
162
- out_hash[:uncovered] = []
163
- out_hash[:covered] = []
164
- ctrl_list.each do |c|
165
- if valid_classes.include?(c)
166
- out_hash[:covered] << c
167
- else
168
- out_hash[:uncovered] << c
169
- end
170
- end
171
- out_hash[:num_covered] = out_hash[:covered].length
172
- out_hash[:num_uncovered] = out_hash[:uncovered].length
173
- out_hash[:coverage] = Float(
174
- (Float(out_hash[:num_covered]) / Float(out_hash[:num_total])) * 100.0
175
- ).floor(3)
176
- out_hash
177
- end
178
-
179
- def self.find_valid_classes(all_cap, invalid_classes)
180
- all_classes = all_cap.dup.transpose[0]
181
- return [] if all_classes.nil?
182
-
183
- return all_classes - invalid_classes unless invalid_classes.nil?
184
-
185
- all_classes
186
- end
187
-
188
- def self.find_invalid_classes(all_cap)
189
- invalid_classes = []
190
- all_cap.each do |cap|
191
- invalid_classes << cap[0] unless class_valid?(cap[1])
192
- end
193
- invalid_classes
194
- end
195
-
196
- def self.class_valid?(manifest_path)
197
- compiler = Puppet::Pal::Compiler.new(nil)
198
- ast = compiler.parse_file(manifest_path)
199
- ast.body.body.statements.each do |s|
200
- next unless s.respond_to?(:arguments)
201
- next unless s.arguments.respond_to?(:each)
202
-
203
- s.arguments.each do |i|
204
- return false if i.value == 'Not implemented'
205
- end
206
- end
207
- true
208
- end
209
- end
210
-
211
- # Class manages organizing report data into various output formats
212
- class ReportOutput
213
- attr_reader :controls_in_resource_data, :rules_in_map, :timestamp,
214
- :title
215
-
216
- def initialize(benchmark, controls_in_resource_data, rules_in_map)
217
- @benchmark = benchmark
218
- @controls_in_resource_data = controls_in_resource_data
219
- @rules_in_map = rules_in_map
220
- @timestamp = DateTime.now.iso8601
221
- @title = "Coverage Report for #{@benchmark.title_key}"
222
- end
223
-
224
- def uncovered
225
- @uncovered ||= rules_in_map - controls_in_resource_data
226
- end
227
-
228
- def uncovered_count
229
- @uncovered_count ||= uncovered.length
230
- end
231
-
232
- def covered
233
- @covered ||= rules_in_map - uncovered
234
- end
235
-
236
- def covered_count
237
- @covered_count ||= covered.length
238
- end
239
-
240
- def total_count
241
- @total_count ||= rules_in_map.length
242
- end
243
-
244
- def percentage
245
- @percentage ||= covered_count.to_f / total_count
246
- end
247
-
248
- def to_h
249
- {
250
- title: title,
251
- timestamp: timestamp,
252
- benchmark: benchmark_hash,
253
- coverage: coverage_hash,
254
- }
255
- end
256
-
257
- def to_json(opts = nil)
258
- JSON.generate(to_h, opts)
259
- end
260
-
261
- def to_yaml
262
- to_h.to_yaml
263
- end
264
-
265
- def benchmark_hash
266
- {
267
- title: @benchmark.title,
268
- version: @benchmark.version,
269
- framework: @benchmark.framework,
270
- }
271
- end
272
-
273
- def coverage_hash
274
- {
275
- total_count: total_count,
276
- uncovered_count: uncovered_count,
277
- uncovered: uncovered,
278
- covered_count: covered_count,
279
- covered: covered,
280
- percentage: percentage,
281
- controls_in_resource_data: controls_in_resource_data,
282
- rules_in_map: rules_in_map,
283
- }
284
- end
285
- end
286
-
287
- # Creates ReportOutput objects based on the given Benchmark
288
- class BenchmarkReport
289
- def initialize(benchmark)
290
- @benchmark = benchmark
291
- end
292
-
293
- def controls_in_resource_data
294
- @controls_in_resource_data ||= find_controls_in_resource_data
295
- end
296
-
297
- def controls_in_mapping_data
298
- @controls_in_mapping_data ||= find_controls_in_mapping_data
299
- end
300
-
301
- def basic_coverage(level: nil, profile: nil)
302
- map_type = @benchmark.map_type(controls_in_resource_data[0])
303
- rules_in_map = @benchmark.rules_in_map(map_type, level: level, profile: profile)
304
- AbideDevUtils::CEM::CoverageReport::ReportOutput.new(@benchmark, controls_in_resource_data, rules_in_map)
305
- end
306
-
307
- private
308
-
309
- def find_controls_in_resource_data
310
- controls = @benchmark.resource_data["#{@benchmark.module_name}::resources"].each_with_object([]) do |(rname, rval), arr|
311
- arr << case rval['controls'].class.to_s
312
- when 'Hash'
313
- rval['controls'].keys
314
- when 'Array'
315
- rval['controls']
316
- else
317
- raise "Invalid controls type: #{rval['controls'].class}"
318
- end
319
- end
320
- controls.flatten.uniq.select do |c|
321
- case @benchmark.framework
322
- when 'cis'
323
- @benchmark.map_type(c) != 'vulnid'
324
- when 'stig'
325
- @benchmark.map_type(c) == 'vulnid'
326
- else
327
- raise "Cannot find controls for framework #{@benchmark.framework}"
328
- end
329
- end
330
- end
331
-
332
- def find_controls_in_mapping_data
333
- controls = @benchmark.map_data[0].each_with_object([]) do |(_, mapping), arr|
334
- mapping.each do |level, profs|
335
- next if level == 'benchmark'
336
-
337
- profs.each do |_, ctrls|
338
- arr << ctrls.keys
339
- arr << ctrls.values
340
- end
341
- end
342
- end
343
- controls.flatten.uniq
344
- end
345
- end
346
- end
347
- end
348
- end