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.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -14
- data/lib/abide_dev_utils/cem/benchmark.rb +330 -136
- data/lib/abide_dev_utils/cem/generate/coverage_report.rb +380 -0
- data/lib/abide_dev_utils/cem/generate/reference.rb +157 -33
- data/lib/abide_dev_utils/cem/generate.rb +5 -4
- data/lib/abide_dev_utils/cem/hiera_data/mapping_data/map_data.rb +110 -0
- data/lib/abide_dev_utils/cem/hiera_data/mapping_data/mixins.rb +46 -0
- data/lib/abide_dev_utils/cem/hiera_data/mapping_data.rb +146 -0
- data/lib/abide_dev_utils/cem/hiera_data/resource_data/control.rb +127 -0
- data/lib/abide_dev_utils/cem/hiera_data/resource_data/parameters.rb +90 -0
- data/lib/abide_dev_utils/cem/hiera_data/resource_data/resource.rb +102 -0
- data/lib/abide_dev_utils/cem/hiera_data/resource_data.rb +310 -0
- data/lib/abide_dev_utils/cem/hiera_data.rb +7 -0
- data/lib/abide_dev_utils/cem/mapping/mapper.rb +161 -34
- data/lib/abide_dev_utils/cem/validate/resource_data.rb +33 -0
- data/lib/abide_dev_utils/cem/validate.rb +10 -0
- data/lib/abide_dev_utils/cem.rb +0 -1
- data/lib/abide_dev_utils/cli/cem.rb +20 -2
- data/lib/abide_dev_utils/dot_number_comparable.rb +75 -0
- data/lib/abide_dev_utils/errors/cem.rb +10 -0
- data/lib/abide_dev_utils/ppt/class_utils.rb +1 -1
- data/lib/abide_dev_utils/ppt/code_gen/data_types.rb +51 -0
- data/lib/abide_dev_utils/ppt/code_gen/generate.rb +15 -0
- data/lib/abide_dev_utils/ppt/code_gen/resource.rb +59 -0
- data/lib/abide_dev_utils/ppt/code_gen/resource_types/base.rb +93 -0
- data/lib/abide_dev_utils/ppt/code_gen/resource_types/class.rb +17 -0
- data/lib/abide_dev_utils/ppt/code_gen/resource_types/manifest.rb +16 -0
- data/lib/abide_dev_utils/ppt/code_gen/resource_types/parameter.rb +16 -0
- data/lib/abide_dev_utils/ppt/code_gen/resource_types/strings.rb +13 -0
- data/lib/abide_dev_utils/ppt/code_gen/resource_types.rb +6 -0
- data/lib/abide_dev_utils/ppt/code_gen.rb +15 -0
- data/lib/abide_dev_utils/ppt/code_introspection.rb +102 -0
- data/lib/abide_dev_utils/ppt/hiera.rb +4 -1
- data/lib/abide_dev_utils/ppt/puppet_module.rb +2 -1
- data/lib/abide_dev_utils/ppt.rb +3 -0
- data/lib/abide_dev_utils/version.rb +1 -1
- data/lib/abide_dev_utils/xccdf/parser/helpers.rb +146 -0
- data/lib/abide_dev_utils/xccdf/parser/objects.rb +87 -144
- data/lib/abide_dev_utils/xccdf/parser.rb +5 -0
- data/lib/abide_dev_utils/xccdf/utils.rb +89 -0
- data/lib/abide_dev_utils/xccdf.rb +3 -0
- metadata +27 -3
- 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.
|
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-
|
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
|