abide_dev_utils 0.10.1 → 0.11.0
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +7 -1
- data/Gemfile.lock +22 -3
- data/Rakefile +28 -0
- data/abide_dev_utils.gemspec +1 -0
- data/lib/abide_dev_utils/cem/benchmark.rb +291 -0
- data/lib/abide_dev_utils/cem/coverage_report.rb +348 -0
- data/lib/abide_dev_utils/cem/generate/reference.rb +116 -0
- data/lib/abide_dev_utils/cem/generate.rb +10 -0
- data/lib/abide_dev_utils/cem/mapping/mapper.rb +155 -0
- data/lib/abide_dev_utils/cem.rb +2 -0
- data/lib/abide_dev_utils/cli/cem.rb +80 -0
- data/lib/abide_dev_utils/errors/cem.rb +22 -0
- data/lib/abide_dev_utils/errors/general.rb +8 -2
- data/lib/abide_dev_utils/errors/ppt.rb +4 -0
- data/lib/abide_dev_utils/errors.rb +6 -0
- data/lib/abide_dev_utils/markdown.rb +104 -0
- data/lib/abide_dev_utils/ppt/facter_utils.rb +140 -0
- data/lib/abide_dev_utils/ppt/hiera.rb +297 -0
- data/lib/abide_dev_utils/ppt/puppet_module.rb +74 -0
- data/lib/abide_dev_utils/ppt.rb +3 -5
- data/lib/abide_dev_utils/validate.rb +14 -0
- data/lib/abide_dev_utils/version.rb +1 -1
- metadata +26 -3
- data/lib/abide_dev_utils/ppt/coverage.rb +0 -86
@@ -0,0 +1,348 @@
|
|
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
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'yaml'
|
5
|
+
require 'abide_dev_utils/markdown'
|
6
|
+
require 'abide_dev_utils/ppt'
|
7
|
+
require 'abide_dev_utils/cem/benchmark'
|
8
|
+
|
9
|
+
module AbideDevUtils
|
10
|
+
module CEM
|
11
|
+
module Generate
|
12
|
+
# Holds objects and methods for generating a reference doc
|
13
|
+
module Reference
|
14
|
+
MAPPING_PATH_KEY = 'Mapping Data'
|
15
|
+
RESOURCE_DATA_PATH_KEY = 'Resource Data'
|
16
|
+
|
17
|
+
def self.generate(data = {})
|
18
|
+
pupmod = AbideDevUtils::Ppt::PuppetModule.new
|
19
|
+
doc_title = case pupmod.name
|
20
|
+
when 'puppetlabs-cem_linux'
|
21
|
+
'CEM Linux Reference'
|
22
|
+
when 'puppetlabs-cem_windows'
|
23
|
+
'CEM Windows Reference'
|
24
|
+
else
|
25
|
+
'Reference'
|
26
|
+
end
|
27
|
+
benchmarks = AbideDevUtils::CEM::Benchmark.benchmarks_from_puppet_module(pupmod)
|
28
|
+
case data.fetch(:format, 'markdown')
|
29
|
+
when 'markdown'
|
30
|
+
MarkdownGenerator.new(benchmarks).generate(doc_title)
|
31
|
+
else
|
32
|
+
raise "Format #{data[:format]} is unsupported! Only `markdown` format supported"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.generate_markdown
|
37
|
+
AbideDevUtils::Markdown.new('REFERENCE.md').generate
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.config_example(control, params_array)
|
41
|
+
out_str = ['cem_windows::config:', ' control_configs:', " \"#{control}\":"]
|
42
|
+
indent = ' '
|
43
|
+
params_array.each do |param_hash|
|
44
|
+
val = case param_hash[:type]
|
45
|
+
when 'String'
|
46
|
+
"'#{param_hash[:default]}'"
|
47
|
+
else
|
48
|
+
param_hash[:default]
|
49
|
+
end
|
50
|
+
|
51
|
+
out_str << "#{indent}#{param_hash[:name]}: #{val}"
|
52
|
+
end
|
53
|
+
out_str.join("\n")
|
54
|
+
end
|
55
|
+
|
56
|
+
# Generates a markdown reference doc
|
57
|
+
class MarkdownGenerator
|
58
|
+
def initialize(benchmarks)
|
59
|
+
@benchmarks = benchmarks
|
60
|
+
@md = AbideDevUtils::Markdown.new('REFERENCE.md')
|
61
|
+
end
|
62
|
+
|
63
|
+
def generate(doc_title = 'Reference')
|
64
|
+
md.add_title(doc_title)
|
65
|
+
benchmarks.each do |benchmark|
|
66
|
+
md.add_h1(benchmark.title_key)
|
67
|
+
benchmark.rules.each do |title, rule|
|
68
|
+
md.add_h2("#{rule['number']} #{title}")
|
69
|
+
md.add_ul('Parameters:')
|
70
|
+
rule['params'].each do |p|
|
71
|
+
md.add_ul("#{md.code(p[:name])} - [ #{md.code(p[:type])} ] - #{md.italic('Default:')} #{md.code(p[:default])}", indent: 1)
|
72
|
+
end
|
73
|
+
md.add_ul('Config Example:')
|
74
|
+
example = config_example(benchmark.module_name, title, rule['params'])
|
75
|
+
md.add_code_block(example, language: 'yaml')
|
76
|
+
md.add_ul('Supported Levels:')
|
77
|
+
rule['level'].each do |l|
|
78
|
+
md.add_ul(md.code(l), indent: 1)
|
79
|
+
end
|
80
|
+
md.add_ul('Supported Profiles:')
|
81
|
+
rule['profile'].each do |l|
|
82
|
+
md.add_ul(md.code(l), indent: 1)
|
83
|
+
end
|
84
|
+
md.add_ul('Alternate Config IDs:')
|
85
|
+
rule['alternate_ids'].each do |l|
|
86
|
+
md.add_ul(md.code(l), indent: 1)
|
87
|
+
end
|
88
|
+
md.add_ul("Resource: #{md.code(rule['resource'].capitalize)}")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
md.to_file
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
attr_reader :benchmarks, :md
|
97
|
+
|
98
|
+
def config_example(module_name, control, params_array)
|
99
|
+
out_str = ["#{module_name}::config:", ' control_configs:', " \"#{control}\":"]
|
100
|
+
indent = ' '
|
101
|
+
params_array.each do |param_hash|
|
102
|
+
val = case param_hash[:type]
|
103
|
+
when 'String'
|
104
|
+
"'#{param_hash[:default]}'"
|
105
|
+
else
|
106
|
+
param_hash[:default]
|
107
|
+
end
|
108
|
+
out_str << "#{indent}#{param_hash[:name]}: #{val}"
|
109
|
+
end
|
110
|
+
out_str.join("\n")
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AbideDevUtils
|
4
|
+
module CEM
|
5
|
+
module Mapping
|
6
|
+
# Handles interacting with mapping data
|
7
|
+
class Mapper
|
8
|
+
MAP_TYPES = %w[hiera_title_num number hiera_title vulnid title].freeze
|
9
|
+
|
10
|
+
attr_reader :module_name, :framework, :map_data
|
11
|
+
|
12
|
+
def initialize(module_name, framework, map_data)
|
13
|
+
@module_name = module_name
|
14
|
+
@framework = framework
|
15
|
+
load_framework(@framework)
|
16
|
+
@map_data = map_data
|
17
|
+
@cache = {}
|
18
|
+
@rule_cache = {}
|
19
|
+
end
|
20
|
+
|
21
|
+
def title
|
22
|
+
@title ||= benchmark_data['title']
|
23
|
+
end
|
24
|
+
|
25
|
+
def version
|
26
|
+
@version ||= benchmark_data['version']
|
27
|
+
end
|
28
|
+
|
29
|
+
def each_like(identifier)
|
30
|
+
mtype, mtop = map_type_and_top_key(identifier)
|
31
|
+
map_data[mtype][mtop].each { |key, val| yield key, val }
|
32
|
+
end
|
33
|
+
|
34
|
+
def each_with_array_like(identifier)
|
35
|
+
mtype, mtop = map_type_and_top_key(identifier)
|
36
|
+
map_data[mtype][mtop].each_with_object([]) { |(key, val), ary| yield [key, val], ary }
|
37
|
+
end
|
38
|
+
|
39
|
+
def get(control_id, level: nil, profile: nil)
|
40
|
+
return cache_get(control_id, level, profile) if cached?(control_id, level, profile)
|
41
|
+
|
42
|
+
value = get_map(control_id, level: level, profile: profile)
|
43
|
+
return if value.nil? || value.empty?
|
44
|
+
|
45
|
+
cache_set(value, control_id, level, profile)
|
46
|
+
value
|
47
|
+
end
|
48
|
+
|
49
|
+
def map_type(control_id)
|
50
|
+
return control_id if MAP_TYPES.include?(control_id)
|
51
|
+
|
52
|
+
case control_id
|
53
|
+
when %r{^c[0-9_]+$}
|
54
|
+
'hiera_title_num'
|
55
|
+
when %r{^[0-9][0-9.]*$}
|
56
|
+
'number'
|
57
|
+
when %r{^[a-z][a-z0-9_]+$}
|
58
|
+
'hiera_title'
|
59
|
+
when %r{^V-[0-9]{6}$}
|
60
|
+
'vulnid'
|
61
|
+
else
|
62
|
+
'title'
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def load_framework(framework)
|
69
|
+
case framework.downcase
|
70
|
+
when 'cis'
|
71
|
+
self.class.include AbideDevUtils::CEM::Mapping::MixinCIS
|
72
|
+
extend AbideDevUtils::CEM::Mapping::MixinCIS
|
73
|
+
when 'stig'
|
74
|
+
self.class.include AbideDevUtils::CEM::Mapping::MixinSTIG
|
75
|
+
extend AbideDevUtils::CEM::Mapping::MixinSTIG
|
76
|
+
else
|
77
|
+
raise "Invalid framework: #{framework}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def map_type_and_top_key(identifier)
|
82
|
+
mtype = MAP_TYPES.include?(identifier) ? identifier : map_type(identifier)
|
83
|
+
[mtype, map_top_key(mtype)]
|
84
|
+
end
|
85
|
+
|
86
|
+
def cached?(control_id, *args)
|
87
|
+
@cache.key?(cache_key(control_id, *args))
|
88
|
+
end
|
89
|
+
|
90
|
+
def cache_get(control_id, *args)
|
91
|
+
ckey = cache_key(control_id, *args)
|
92
|
+
@cache[ckey] if cached?(control_id, *args)
|
93
|
+
end
|
94
|
+
|
95
|
+
def cache_set(value, control_id, *args)
|
96
|
+
@cache[cache_key(control_id, *args)] = value unless value.nil?
|
97
|
+
end
|
98
|
+
|
99
|
+
def default_map_type
|
100
|
+
@default_map_type ||= (framework == 'stig' ? 'vulnid' : map_data.keys.first)
|
101
|
+
end
|
102
|
+
|
103
|
+
def benchmark_data
|
104
|
+
@benchmark_data ||= map_data[default_map_type][map_top_key(default_map_type)]['benchmark']
|
105
|
+
end
|
106
|
+
|
107
|
+
def cache_key(control_id, *args)
|
108
|
+
args.unshift(control_id).compact.join('-')
|
109
|
+
end
|
110
|
+
|
111
|
+
def map_top_key(mtype)
|
112
|
+
[module_name, 'mappings', framework, mtype].join('::')
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Mixin module used by Mapper to implement CIS-specific mapping behavior
|
117
|
+
module MixinCIS
|
118
|
+
def get_map(control_id, level: nil, profile: nil, **_)
|
119
|
+
mtype, mtop = map_type_and_top_key(control_id)
|
120
|
+
return if mtype == 'vulnid'
|
121
|
+
|
122
|
+
return map_data[mtype][mtop][level][profile][control_id] unless level.nil? || profile.nil?
|
123
|
+
|
124
|
+
map_data[mtype][mtop].each do |lvl, profile_hash|
|
125
|
+
next if lvl == 'benchmark'
|
126
|
+
|
127
|
+
profile_hash.each do |prof, control_hash|
|
128
|
+
return map_data[mtype][mtop][lvl][prof][control_id] if control_hash.key?(control_id)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# Mixin module used by Mapper to implement STIG-specific mapping behavior
|
135
|
+
module MixinSTIG
|
136
|
+
def get_map(control_id, level: nil, **_)
|
137
|
+
mtype, mtop = map_type_and_top_key(control_id)
|
138
|
+
return map_data[mtype][mtop][level][control_id] unless level.nil?
|
139
|
+
|
140
|
+
begin
|
141
|
+
map_data[mtype][mtop].each do |lvl, control_hash|
|
142
|
+
next if lvl == 'benchmark'
|
143
|
+
|
144
|
+
return control_hash[control_id] if control_hash.key?(control_id)
|
145
|
+
end
|
146
|
+
rescue NoMethodError => e
|
147
|
+
require 'pry'
|
148
|
+
binding.pry
|
149
|
+
#raise "Control ID: #{control_id}, Level: #{level}, #{e.message}"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
data/lib/abide_dev_utils/cem.rb
CHANGED