abide_dev_utils 0.11.0 → 0.12.1

Sign up to get free protection for your applications and to get access to all the features.
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