abide_dev_utils 0.11.0 → 0.11.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 +1 -14
  3. data/lib/abide_dev_utils/cem/benchmark.rb +330 -136
  4. data/lib/abide_dev_utils/cem/generate/coverage_report.rb +380 -0
  5. data/lib/abide_dev_utils/cem/generate/reference.rb +157 -33
  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 +51 -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 +3 -0
  43. metadata +27 -3
  44. data/lib/abide_dev_utils/cem/coverage_report.rb +0 -348
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'abide_dev_utils/validate'
4
+
5
+ module AbideDevUtils
6
+ module XCCDF
7
+ module Utils
8
+ # Class for working with directories that contain XCCDF files
9
+ class FileDir
10
+ CIS_FILE_NAME_PARTS_PATTERN = /^CIS_(?<subject>[A-Za-z0-9._()-]+)_Benchmark_v(?<version>[0-9.]+)-xccdf$/.freeze
11
+ def initialize(path)
12
+ @path = File.expand_path(path)
13
+ AbideDevUtils::Validate.directory(@path)
14
+ end
15
+
16
+ def files
17
+ @files ||= Dir.glob(File.join(@path, '*-xccdf.xml')).map { |f| FileNameData.new(f) }
18
+ end
19
+
20
+ def fuzzy_find(label, value)
21
+ files.find { |f| f.fuzzy_match?(label, value) }
22
+ end
23
+
24
+ def fuzzy_select(label, value)
25
+ files.select { |f| f.fuzzy_match?(label, value) }
26
+ end
27
+
28
+ def fuzzy_reject(label, value)
29
+ files.reject { |f| f.fuzzy_match?(label, value) }
30
+ end
31
+
32
+ def label?(label)
33
+ files.select { |f| f.has?(label) }
34
+ end
35
+
36
+ def no_label?(label)
37
+ files.reject { |f| f.has?(label) }
38
+ end
39
+ end
40
+
41
+ # Parses XCCDF file names into labeled parts
42
+ class FileNameData
43
+ CIS_PATTERN = /^CIS_(?<subject>[A-Za-z0-9._()-]+?)(?<stig>_STIG)?_Benchmark_v(?<version>[0-9.]+)-xccdf$/.freeze
44
+
45
+ attr_reader :path, :name, :labeled_parts
46
+
47
+ def initialize(path)
48
+ @path = path
49
+ @name = File.basename(path, '.xml')
50
+ @labeled_parts = File.basename(name, '.xml').match(CIS_PATTERN)&.named_captures
51
+ end
52
+
53
+ def subject
54
+ @subject ||= labeled_parts&.fetch('subject', nil)
55
+ end
56
+
57
+ def stig
58
+ @stig ||= labeled_parts&.fetch('subject', nil)
59
+ end
60
+
61
+ def version
62
+ @version ||= labeled_parts&.fetch('version', nil)
63
+ end
64
+
65
+ def has?(label)
66
+ val = send(label.to_sym)
67
+ !val.nil? && !val.empty?
68
+ end
69
+
70
+ def fuzzy_match?(label, value)
71
+ return false unless has?(label)
72
+
73
+ this_val = normalize_char_array(send(label.to_sym).chars)
74
+ other_val = normalize_char_array(value.chars)
75
+ other_val.each_with_index do |c, idx|
76
+ return false unless this_val[idx] == c
77
+ end
78
+ true
79
+ end
80
+
81
+ private
82
+
83
+ def normalize_char_array(char_array)
84
+ char_array.grep_v(/[^A-Za-z0-9]/).map(&:downcase)[3..]
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -70,6 +70,7 @@ module AbideDevUtils
70
70
  CIS_LEVEL_CODE = /(?:_|^)([Ll]evel_[0-9]|[Ll]1|[Ll]2|[NnBb][GgLl]|#{CIS_NEXT_GEN_WINDOWS})/.freeze
71
71
  CIS_CONTROL_PARTS = /#{CIS_CONTROL_NUMBER}#{CIS_LEVEL_CODE}?_+([A-Za-z].*)/.freeze
72
72
  CIS_PROFILE_PARTS = /#{CIS_LEVEL_CODE}[_-]+([A-Za-z].*)/.freeze
73
+ STIG_PROFILE_PARTS = /(STIG)/.freeze
73
74
 
74
75
  def xpath(path)
75
76
  @xml.xpath(path)
@@ -119,6 +120,8 @@ module AbideDevUtils
119
120
  end
120
121
 
121
122
  def profile_parts(profile)
123
+ return ['STIG', ''] if profile == 'STIG'
124
+
122
125
  parts = control_profile_text(profile).match(CIS_PROFILE_PARTS)
123
126
  raise AbideDevUtils::Errors::ProfilePartsError, profile if parts.nil?
124
127
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: abide_dev_utils
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.0
4
+ version: 0.11.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - abide-team
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-07-25 00:00:00.000000000 Z
11
+ date: 2022-08-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -360,10 +360,20 @@ files:
360
360
  - lib/abide_dev_utils.rb
361
361
  - lib/abide_dev_utils/cem.rb
362
362
  - lib/abide_dev_utils/cem/benchmark.rb
363
- - lib/abide_dev_utils/cem/coverage_report.rb
364
363
  - lib/abide_dev_utils/cem/generate.rb
364
+ - lib/abide_dev_utils/cem/generate/coverage_report.rb
365
365
  - lib/abide_dev_utils/cem/generate/reference.rb
366
+ - lib/abide_dev_utils/cem/hiera_data.rb
367
+ - lib/abide_dev_utils/cem/hiera_data/mapping_data.rb
368
+ - lib/abide_dev_utils/cem/hiera_data/mapping_data/map_data.rb
369
+ - lib/abide_dev_utils/cem/hiera_data/mapping_data/mixins.rb
370
+ - lib/abide_dev_utils/cem/hiera_data/resource_data.rb
371
+ - lib/abide_dev_utils/cem/hiera_data/resource_data/control.rb
372
+ - lib/abide_dev_utils/cem/hiera_data/resource_data/parameters.rb
373
+ - lib/abide_dev_utils/cem/hiera_data/resource_data/resource.rb
366
374
  - lib/abide_dev_utils/cem/mapping/mapper.rb
375
+ - lib/abide_dev_utils/cem/validate.rb
376
+ - lib/abide_dev_utils/cem/validate/resource_data.rb
367
377
  - lib/abide_dev_utils/cli.rb
368
378
  - lib/abide_dev_utils/cli/abstract.rb
369
379
  - lib/abide_dev_utils/cli/cem.rb
@@ -375,6 +385,7 @@ files:
375
385
  - lib/abide_dev_utils/comply.rb
376
386
  - lib/abide_dev_utils/config.rb
377
387
  - lib/abide_dev_utils/constants.rb
388
+ - lib/abide_dev_utils/dot_number_comparable.rb
378
389
  - lib/abide_dev_utils/errors.rb
379
390
  - lib/abide_dev_utils/errors/base.rb
380
391
  - lib/abide_dev_utils/errors/cem.rb
@@ -393,6 +404,17 @@ files:
393
404
  - lib/abide_dev_utils/ppt.rb
394
405
  - lib/abide_dev_utils/ppt/api.rb
395
406
  - lib/abide_dev_utils/ppt/class_utils.rb
407
+ - lib/abide_dev_utils/ppt/code_gen.rb
408
+ - lib/abide_dev_utils/ppt/code_gen/data_types.rb
409
+ - lib/abide_dev_utils/ppt/code_gen/generate.rb
410
+ - lib/abide_dev_utils/ppt/code_gen/resource.rb
411
+ - lib/abide_dev_utils/ppt/code_gen/resource_types.rb
412
+ - lib/abide_dev_utils/ppt/code_gen/resource_types/base.rb
413
+ - lib/abide_dev_utils/ppt/code_gen/resource_types/class.rb
414
+ - lib/abide_dev_utils/ppt/code_gen/resource_types/manifest.rb
415
+ - lib/abide_dev_utils/ppt/code_gen/resource_types/parameter.rb
416
+ - lib/abide_dev_utils/ppt/code_gen/resource_types/strings.rb
417
+ - lib/abide_dev_utils/ppt/code_introspection.rb
396
418
  - lib/abide_dev_utils/ppt/facter_utils.rb
397
419
  - lib/abide_dev_utils/ppt/hiera.rb
398
420
  - lib/abide_dev_utils/ppt/new_obj.rb
@@ -411,9 +433,11 @@ files:
411
433
  - lib/abide_dev_utils/xccdf/diff/benchmark/property_existence.rb
412
434
  - lib/abide_dev_utils/xccdf/diff/utils.rb
413
435
  - lib/abide_dev_utils/xccdf/parser.rb
436
+ - lib/abide_dev_utils/xccdf/parser/helpers.rb
414
437
  - lib/abide_dev_utils/xccdf/parser/objects.rb
415
438
  - lib/abide_dev_utils/xccdf/parser/objects/digest_object.rb
416
439
  - lib/abide_dev_utils/xccdf/parser/objects/numbered_object.rb
440
+ - lib/abide_dev_utils/xccdf/utils.rb
417
441
  - new_diff.rb
418
442
  homepage: https://github.com/puppetlabs/abide_dev_utils
419
443
  licenses:
@@ -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