inspec_tools 2.0.5 → 2.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,35 @@
1
+ module InspecTools
2
+ class GenerateMap
3
+ attr_accessor :text
4
+
5
+ def initialize(text = nil)
6
+ @text = text.nil? ? default_text : text
7
+ end
8
+
9
+ def generate_example(file)
10
+ File.write(file, @text)
11
+ end
12
+
13
+ private
14
+
15
+ def default_text
16
+ <<~YML
17
+ # Setting csv_header to true will skip the csv file header
18
+ skip_csv_header: true
19
+ width : 80
20
+
21
+
22
+ control.id: 0
23
+ control.title: 15
24
+ control.desc: 16
25
+ control.tags:
26
+ severity: 1
27
+ rid: 8
28
+ stig_id: 3
29
+ cci: 2
30
+ check: 12
31
+ fix: 10
32
+ YML
33
+ end
34
+ end
35
+ end
@@ -9,17 +9,14 @@ require_relative '../happy_mapper_tools/stig_checklist'
9
9
  require_relative '../happy_mapper_tools/benchmark'
10
10
  require_relative '../utilities/inspec_util'
11
11
  require_relative 'csv'
12
-
13
- # rubocop:disable Metrics/ClassLength
14
- # rubocop:disable Metrics/AbcSize
15
- # rubocop:disable Metrics/BlockLength
16
- # rubocop:disable Style/GuardClause
12
+ require_relative '../utilities/xccdf/from_inspec'
13
+ require_relative '../utilities/xccdf/to_xccdf'
17
14
 
18
15
  module InspecTools
19
- class Inspec
20
- def initialize(inspec_json, metadata = '{}')
16
+ class Inspec # rubocop:disable Metrics/ClassLength
17
+ def initialize(inspec_json, metadata = {})
21
18
  @json = JSON.parse(inspec_json.gsub(/\\+u0000/, ''))
22
- @metadata = JSON.parse(metadata)
19
+ @metadata = metadata
23
20
  end
24
21
 
25
22
  def to_ckl(title = nil, date = nil, cklist = nil)
@@ -36,16 +33,15 @@ module InspecTools
36
33
  @checklist.to_xml.encode('UTF-8').gsub('<?xml version="1.0"?>', '<?xml version="1.0" encoding="UTF-8"?>').chomp
37
34
  end
38
35
 
36
+ # Convert Inspec result data to XCCDF
37
+ #
38
+ # @param attributes [Hash] Optional input attributes
39
+ # @return [String] XML formatted String
39
40
  def to_xccdf(attributes, verbose = false)
40
- @data = Utils::InspecUtil.parse_data_for_xccdf(@json)
41
- @attribute = attributes
42
- @attribute = {} if @attribute.eql? false
41
+ data = Utils::FromInspec.new.parse_data_for_xccdf(@json)
43
42
  @verbose = verbose
44
- @benchmark = HappyMapperTools::Benchmark::Benchmark.new
45
- populate_header
46
- # populate_profiles @todo populate profiles; not implemented now because its use is deprecated
47
- populate_groups
48
- @benchmark.to_xml
43
+
44
+ Utils::ToXCCDF.new(attributes || {}, data).to_xml(@metadata)
49
45
  end
50
46
 
51
47
  ####
@@ -70,7 +66,7 @@ module InspecTools
70
66
  #
71
67
  # @param inspec_json : an inspec profile formatted as a json object
72
68
  ###
73
- def inspec_json_to_array(inspec_json)
69
+ def inspec_json_to_array(inspec_json) # rubocop:disable Metrics/CyclomaticComplexity
74
70
  data = []
75
71
  headers = {}
76
72
  inspec_json['controls'].each do |control|
@@ -97,10 +93,11 @@ module InspecTools
97
93
  @data['controls'] << control
98
94
  end
99
95
  end
100
- if json['profiles'].nil?
101
- json['controls'].each do |control|
102
- @data['controls'] << control
103
- end
96
+
97
+ return unless json['profiles'].nil?
98
+
99
+ json['controls'].each do |control|
100
+ @data['controls'] << control
104
101
  end
105
102
  end
106
103
 
@@ -161,7 +158,7 @@ module InspecTools
161
158
  vuln
162
159
  end
163
160
 
164
- def generate_asset
161
+ def generate_asset # rubocop:disable Metrics/AbcSize
165
162
  asset = HappyMapperTools::StigChecklist::Asset.new
166
163
  asset.role = !@metadata['role'].nil? ? @metadata['role'] : 'Workstation'
167
164
  asset.type = !@metadata['type'].nil? ? @metadata['type'] : 'Computing'
@@ -223,75 +220,6 @@ module InspecTools
223
220
  ip
224
221
  end
225
222
 
226
- def populate_header
227
- @benchmark.title = @attribute['benchmark.title']
228
- @benchmark.id = @attribute['benchmark.id']
229
- @benchmark.description = @attribute['benchmark.description']
230
- @benchmark.version = @attribute['benchmark.version']
231
-
232
- @benchmark.status = HappyMapperTools::Benchmark::Status.new
233
- @benchmark.status.status = @attribute['benchmark.status']
234
- @benchmark.status.date = @attribute['benchmark.status.date']
235
-
236
- @benchmark.notice = HappyMapperTools::Benchmark::Notice.new
237
- @benchmark.notice.id = @attribute['benchmark.notice.id']
238
-
239
- @benchmark.plaintext = HappyMapperTools::Benchmark::Plaintext.new
240
- @benchmark.plaintext.plaintext = @attribute['benchmark.plaintext']
241
- @benchmark.plaintext.id = @attribute['benchmark.plaintext.id']
242
-
243
- @benchmark.reference = HappyMapperTools::Benchmark::ReferenceBenchmark.new
244
- @benchmark.reference.href = @attribute['reference.href']
245
- @benchmark.reference.dc_publisher = @attribute['reference.dc.publisher']
246
- @benchmark.reference.dc_source = @attribute['reference.dc.source']
247
- end
248
-
249
- def populate_groups
250
- group_array = []
251
- @data['controls'].each do |control|
252
- group = HappyMapperTools::Benchmark::Group.new
253
- group.id = control['id']
254
- group.title = control['gtitle']
255
- group.description = "<GroupDescription>#{control['gdescription']}</GroupDescription>"
256
-
257
- group.rule = HappyMapperTools::Benchmark::Rule.new
258
- group.rule.id = control['rid']
259
- group.rule.severity = control['severity']
260
- group.rule.weight = control['rweight']
261
- group.rule.version = control['rversion']
262
- group.rule.title = control['title'].tr("\n", ' ')
263
- group.rule.description = "<VulnDiscussion>#{control['desc'].tr("\n", ' ')}</VulnDiscussion><FalsePositives></FalsePositives><FalseNegatives></FalseNegatives><Documentable>false</Documentable><Mitigations></Mitigations><SeverityOverrideGuidance></SeverityOverrideGuidance><PotentialImpacts></PotentialImpacts><ThirdPartyTools></ThirdPartyTools><MitigationControl></MitigationControl><Responsibility></Responsibility><IAControls></IAControls>"
264
-
265
- group.rule.reference = HappyMapperTools::Benchmark::ReferenceGroup.new
266
- group.rule.reference.dc_publisher = @attribute['reference.dc.publisher']
267
- group.rule.reference.dc_title = @attribute['reference.dc.title']
268
- group.rule.reference.dc_subject = @attribute['reference.dc.subject']
269
- group.rule.reference.dc_type = @attribute['reference.dc.type']
270
- group.rule.reference.dc_identifier = @attribute['reference.dc.identifier']
271
-
272
- group.rule.ident = HappyMapperTools::Benchmark::Ident.new
273
- group.rule.ident.system = 'https://public.cyber.mil/stigs/cci/'
274
- group.rule.ident.ident = control['cci']
275
-
276
- group.rule.fixtext = HappyMapperTools::Benchmark::Fixtext.new
277
- group.rule.fixtext.fixref = control['fixref']
278
- group.rule.fixtext.fixtext = control['fix']
279
-
280
- group.rule.fix = HappyMapperTools::Benchmark::Fix.new
281
- group.rule.fix.id = control['fixref']
282
-
283
- group.rule.check = HappyMapperTools::Benchmark::Check.new
284
- group.rule.check.system = control['checkref']
285
- group.rule.check.content_ref = HappyMapperTools::Benchmark::ContentRef.new
286
- group.rule.check.content_ref.name = @attribute['content_ref.name']
287
- group.rule.check.content_ref.href = @attribute['content_ref.href']
288
- group.rule.check.content = control['check']
289
-
290
- group_array << group
291
- end
292
- @benchmark.group = group_array
293
- end
294
-
295
223
  def generate_title(title, json, date)
296
224
  title ||= "Untitled - Checklist Created from Automated InSpec Results JSON; Profiles: #{json['profiles'].map { |x| x['name'] }.join(' | ')}"
297
225
  title + " Checklist Date: #{date || Date.today.to_s}"
@@ -2,9 +2,9 @@ require 'digest'
2
2
 
3
3
  require_relative '../utilities/inspec_util'
4
4
  require_relative '../utilities/extract_pdf_text'
5
- require_relative '../utilities/extract_nist_cis_mapping'
6
5
  require_relative '../utilities/parser'
7
6
  require_relative '../utilities/text_cleaner'
7
+ require_relative '../utilities/cis_to_nist'
8
8
 
9
9
  # rubocop:disable Metrics/AbcSize
10
10
  # rubocop:disable Metrics/PerceivedComplexity
@@ -24,7 +24,7 @@ module InspecTools
24
24
  @controls = []
25
25
  @csv_handle = nil
26
26
  @cci_xml = nil
27
- @nist_mapping = nil
27
+ @nist_mapping = Utils::CisToNist.get_mapping('cis_to_nist_critical_controls')
28
28
  @pdf_text = ''
29
29
  @clean_text = ''
30
30
  @transformed_data = ''
@@ -33,7 +33,6 @@ module InspecTools
33
33
  @title ||= extract_title
34
34
  clean_pdf_text
35
35
  transform_data
36
- read_excl
37
36
  insert_json_metadata
38
37
  @profile['controls'] = parse_controls
39
38
  @profile['sha256'] = Digest::SHA256.hexdigest @profile.to_s
@@ -122,15 +121,5 @@ module InspecTools
122
121
  def write_clean_text
123
122
  File.write('debug_text', @clean_text)
124
123
  end
125
-
126
- def read_excl
127
- nist_map_path = File.join(File.dirname(__FILE__), '../data/NIST_Map_09212017B_CSC-CIS_Critical_Security_Controls_VER_6.1_Excel_9.1.2016.xlsx')
128
- excel = Util::ExtractNistMappings.new(nist_map_path)
129
- @nist_mapping = excel.full_excl
130
- rescue StandardError => e
131
- puts "Exception: #{e.message}"
132
- puts 'Existing...'
133
- exit
134
- end
135
124
  end
136
125
  end
@@ -1,4 +1,3 @@
1
- require 'yaml'
2
1
  require 'json'
3
2
  require 'roo'
4
3
  require_relative '../utilities/inspec_util'
@@ -13,8 +12,8 @@ module InspecTools
13
12
  autoload :CKL, 'inspec_tools/ckl'
14
13
  autoload :Inspec, 'inspec_tools/inspec'
15
14
  autoload :Summary, 'inspec_tools/summary'
16
- autoload :Threshold, 'inspec_tools/threshold'
17
15
  autoload :XLSXTool, 'inspec_tools/xlsx_tool'
16
+ autoload :GenerateMap, 'inspec_tools/generate_map'
18
17
  end
19
18
 
20
19
  # rubocop:disable Style/GuardClause
@@ -23,7 +22,7 @@ module InspecPlugins
23
22
  class CliCommand < Inspec.plugin(2, :cli_command) # rubocop:disable Metrics/ClassLength
24
23
  POSSIBLE_LOG_LEVELS = %w{debug info warn error fatal}.freeze
25
24
 
26
- class_option :log_directory, type: :string, aliases: :l, desc: 'Provie log location'
25
+ class_option :log_directory, type: :string, aliases: :l, desc: 'Provide log location'
27
26
  class_option :log_level, type: :string, desc: "Set the logging level: #{POSSIBLE_LOG_LEVELS}"
28
27
 
29
28
  subcommand_desc 'tools [COMMAND]', 'Runs inspec_tools commands through Inspec'
@@ -54,12 +53,18 @@ module InspecPlugins
54
53
 
55
54
  desc 'inspec2xccdf', 'inspec2xccdf translates an inspec profile and attributes files to an xccdf file'
56
55
  long_desc InspecTools::Help.text(:inspec2xccdf)
57
- option :inspec_json, required: true, aliases: '-j'
58
- option :attributes, required: true, aliases: '-a'
59
- option :output, required: true, aliases: '-o'
56
+ option :inspec_json, required: true, aliases: '-j',
57
+ desc: 'path to InSpec JSON file created'
58
+ option :attributes, required: true, aliases: '-a',
59
+ desc: 'path to yml file that provides the required attributes for the XCCDF document. These attributes are parts of XCCDF document which do not fit into the InSpec schema.'
60
+ option :output, required: true, aliases: '-o',
61
+ desc: 'name or path to create the XCCDF and title to give the XCCDF'
62
+ option :metadata, required: false, type: :string, aliases: '-m',
63
+ desc: 'path to JSON file with additional host metadata for the XCCDF file'
60
64
  def inspec2xccdf
61
65
  json = File.read(options[:inspec_json])
62
- inspec_tool = InspecTools::Inspec.new(json)
66
+ metadata = options[:metadata] ? JSON.parse(File.read(options[:metadata])) : {}
67
+ inspec_tool = InspecTools::Inspec.new(json, metadata)
63
68
  attr_hsh = YAML.load_file(options[:attributes])
64
69
  xccdf = inspec_tool.to_xccdf(attr_hsh)
65
70
  File.write(options[:output], xccdf)
@@ -73,10 +78,11 @@ module InspecPlugins
73
78
  option :output, required: false, aliases: '-o', default: 'profile'
74
79
  option :format, required: false, aliases: '-f', enum: %w{ruby hash}, default: 'ruby'
75
80
  option :separate_files, required: false, type: :boolean, default: true, aliases: '-s'
81
+ option :control_name_prefix, required: false, type: :string, aliases: '-p'
76
82
  def csv2inspec
77
83
  csv = CSV.read(options[:csv], encoding: 'ISO8859-1')
78
84
  mapping = YAML.load_file(options[:mapping])
79
- profile = InspecTools::CSVTool.new(csv, mapping, options[:csv].split('/')[-1].split('.')[0], options[:verbose]).to_inspec
85
+ profile = InspecTools::CSVTool.new(csv, mapping, options[:csv].split('/')[-1].split('.')[0], options[:verbose]).to_inspec(control_name_prefix: options[:control_name_prefix])
80
86
  Utils::InspecUtil.unpack_inspec_json(options[:output], profile, options[:separate_files], options[:format])
81
87
  end
82
88
 
@@ -136,26 +142,8 @@ module InspecPlugins
136
142
 
137
143
  desc 'generate_map', 'Generates mapping template from CSV to Inspec Controls'
138
144
  def generate_map
139
- template = '
140
- # Setting csv_header to true will skip the csv file header
141
- skip_csv_header: true
142
- width : 80
143
-
144
-
145
- control.id: 0
146
- control.title: 15
147
- control.desc: 16
148
- control.tags:
149
- severity: 1
150
- rid: 8
151
- stig_id: 3
152
- cci: 2
153
- check: 12
154
- fix: 10
155
- '
156
- myfile = File.new('mapping.yml', 'w')
157
- myfile.puts template
158
- myfile.close
145
+ generator = InspecTools::GenerateMap.new
146
+ generator.generate_example('mapping.yml')
159
147
  end
160
148
 
161
149
  desc 'generate_ckl_metadata', 'Generate metadata file that can be passed to inspec2ckl'
@@ -200,26 +188,14 @@ module InspecPlugins
200
188
  desc 'summary', 'summary parses an inspec results json to create a summary json'
201
189
  long_desc InspecTools::Help.text(:summary)
202
190
  option :inspec_json, required: true, aliases: '-j'
203
- option :verbose, type: :boolean, aliases: '-V'
204
191
  option :json_full, type: :boolean, required: false, aliases: '-f'
205
192
  option :json_counts, type: :boolean, required: false, aliases: '-k'
193
+ option :threshold_file, required: false, aliases: '-t'
194
+ option :threshold_inline, required: false, aliases: '-i'
206
195
 
207
196
  def summary
208
- summary = InspecTools::Summary.new(File.read(options[:inspec_json])).to_summary
209
-
210
- unless options.include?('json_full') || options.include?('json_counts')
211
- puts "\nOverall compliance: #{summary[:compliance]}%\n\n"
212
- summary[:status].keys.each do |category|
213
- puts category
214
- summary[:status][category].keys.each do |impact|
215
- puts "\t#{impact} : #{summary[:status][category][impact]}"
216
- end
217
- end
218
- end
219
-
220
- json_summary = summary.to_json
221
- puts json_summary if options[:json_full]
222
- puts summary[:status].to_json if options[:json_counts]
197
+ summary = InspecTools::Summary.new(options: options)
198
+ summary.output_summary
223
199
  end
224
200
 
225
201
  desc 'compliance', 'compliance parses an inspec results json to check if the compliance level meets a specified threshold'
@@ -227,17 +203,10 @@ module InspecPlugins
227
203
  option :inspec_json, required: true, aliases: '-j'
228
204
  option :threshold_file, required: false, aliases: '-f'
229
205
  option :threshold_inline, required: false, aliases: '-i'
230
- option :verbose, type: :boolean, aliases: '-V'
231
206
 
232
207
  def compliance
233
- if options[:threshold_file].nil? && options[:threshold_inline].nil?
234
- puts 'Please provide threshold as a yaml file or inline yaml'
235
- exit(1)
236
- end
237
- threshold = YAML.load_file(options[:threshold_file]) unless options[:threshold_file].nil?
238
- threshold = YAML.safe_load(options[:threshold_inline]) unless options[:threshold_inline].nil?
239
- compliance = InspecTools::Summary.new(File.read(options[:inspec_json])).threshold(threshold)
240
- compliance ? exit(0) : exit(1)
208
+ compliance = InspecTools::Summary.new(options: options)
209
+ compliance.results_meet_threshold? ? exit(0) : exit(1)
241
210
  end
242
211
  end
243
212
  end
@@ -2,8 +2,6 @@ require 'json'
2
2
  require 'yaml'
3
3
  require_relative '../utilities/inspec_util'
4
4
 
5
- # rubocop:disable Metrics/AbcSize
6
-
7
5
  # Impact Definitions
8
6
  CRITICAL = 0.9
9
7
  HIGH = 0.7
@@ -16,48 +14,118 @@ TALLYS = %i(total critical high medium low).freeze
16
14
  THRESHOLD_TEMPLATE = File.expand_path('../data/threshold.yaml', File.dirname(__FILE__))
17
15
 
18
16
  module InspecTools
17
+ # rubocop:disable Metrics/ClassLength
19
18
  class Summary
20
- def initialize(inspec_json)
21
- @json = JSON.parse(inspec_json)
19
+ attr_reader :json
20
+ attr_reader :json_full
21
+ attr_reader :json_counts
22
+ attr_reader :threshold_file
23
+ attr_reader :threshold_inline
24
+ attr_reader :summary
25
+ attr_reader :threshold
26
+
27
+ def initialize(**options)
28
+ options = options[:options]
29
+ @json = JSON.parse(File.read(options[:inspec_json]))
30
+ @json_full = false || options[:json_full]
31
+ @json_counts = false || options[:json_counts]
32
+ @threshold = parse_threshold(options[:threshold_inline], options[:threshold_file])
33
+ @threshold_provided = options[:threshold_inline] || options[:threshold_file]
34
+ @summary = compute_summary
22
35
  end
23
36
 
24
- def to_summary
25
- @data = Utils::InspecUtil.parse_data_for_ckl(@json)
26
- @summary = {}
27
- @data.keys.each do |control_id|
28
- current_control = @data[control_id]
29
- current_control[:compliance_status] = Utils::InspecUtil.control_status(current_control, true)
30
- current_control[:finding_details] = Utils::InspecUtil.control_finding_details(current_control, current_control[:compliance_status])
37
+ def output_summary
38
+ unless @json_full || @json_counts
39
+ puts "\nThreshold compliance: #{@threshold['compliance.min']}%"
40
+ puts "\nOverall compliance: #{@summary[:compliance]}%\n\n"
41
+ @summary[:status].keys.each do |category|
42
+ puts category
43
+ @summary[:status][category].keys.each do |impact|
44
+ puts "\t#{impact} : #{@summary[:status][category][impact]}"
45
+ end
46
+ end
31
47
  end
32
- compute_summary
33
- @summary
48
+
49
+ puts @summary.to_json if @json_full
50
+ puts @summary[:status].to_json if @json_counts
34
51
  end
35
52
 
36
- def threshold(threshold = nil)
37
- @summary = to_summary
38
- @threshold = Utils::InspecUtil.to_dotted_hash(YAML.load_file(THRESHOLD_TEMPLATE))
39
- parse_threshold(Utils::InspecUtil.to_dotted_hash(threshold))
40
- threshold_compliance
53
+ def results_meet_threshold?
54
+ raise 'Please provide threshold as a yaml file or inline yaml' unless @threshold_provided
55
+
56
+ compliance = true
57
+ failure = []
58
+ failure << check_max_compliance(@threshold['compliance.max'], @summary[:compliance], '', 'compliance')
59
+ failure << check_min_compliance(@threshold['compliance.min'], @summary[:compliance], '', 'compliance')
60
+
61
+ BUCKETS.each do |bucket|
62
+ TALLYS.each do |tally|
63
+ failure << check_min_compliance(@threshold["#{bucket}.#{tally}.min"], @summary[:status][bucket][tally], bucket, tally)
64
+ failure << check_max_compliance(@threshold["#{bucket}.#{tally}.max"], @summary[:status][bucket][tally], bucket, tally)
65
+ end
66
+ end
67
+
68
+ failure.reject!(&:nil?)
69
+ compliance = false if failure.length.positive?
70
+ output(compliance, failure)
71
+ compliance
41
72
  end
42
73
 
43
74
  private
44
75
 
76
+ def check_min_compliance(min, data, bucket, tally)
77
+ expected_to_string(bucket, tally, 'min', min, data) if min != -1 and data < min
78
+ end
79
+
80
+ def check_max_compliance(max, data, bucket, tally)
81
+ expected_to_string(bucket, tally, 'max', max, data) if max != -1 and data > max
82
+ end
83
+
84
+ def output(passed_threshold, what_failed)
85
+ if passed_threshold
86
+ puts "Overall compliance threshold of #{@threshold['compliance.min']}\% met. Current compliance at #{@summary[:compliance]}\%"
87
+ else
88
+ puts 'Compliance threshold was not met: '
89
+ puts what_failed.join("\n")
90
+ end
91
+ end
92
+
93
+ def expected_to_string(bucket, tally, maxmin, value, got)
94
+ return "Expected #{bucket}.#{tally}.#{maxmin}:#{value} got:#{got}" unless bucket.empty? || bucket.nil?
95
+
96
+ "Expected #{tally}.#{maxmin}:#{value}\% got:#{got}\%"
97
+ end
98
+
99
+ def parse_threshold(threshold_inline, threshold_file)
100
+ threshold = Utils::InspecUtil.to_dotted_hash(YAML.load_file(THRESHOLD_TEMPLATE))
101
+ threshold.merge!(Utils::InspecUtil.to_dotted_hash(YAML.load_file(threshold_file))) if threshold_file
102
+ threshold.merge!(Utils::InspecUtil.to_dotted_hash(YAML.safe_load(threshold_inline))) if threshold_inline
103
+ threshold
104
+ end
105
+
45
106
  def compute_summary
46
- @summary[:buckets] = {}
47
- @summary[:buckets][:failed] = select_by_status(@data, 'Open')
48
- @summary[:buckets][:passed] = select_by_status(@data, 'NotAFinding')
49
- @summary[:buckets][:no_impact] = select_by_status(@data, 'Not_Applicable')
50
- @summary[:buckets][:skipped] = select_by_status(@data, 'Not_Reviewed')
51
- @summary[:buckets][:error] = select_by_status(@data, 'Profile_Error')
52
-
53
- @summary[:status] = {}
54
- @summary[:status][:failed] = tally_by_impact(@summary[:buckets][:failed])
55
- @summary[:status][:passed] = tally_by_impact(@summary[:buckets][:passed])
56
- @summary[:status][:no_impact] = tally_by_impact(@summary[:buckets][:no_impact])
57
- @summary[:status][:skipped] = tally_by_impact(@summary[:buckets][:skipped])
58
- @summary[:status][:error] = tally_by_impact(@summary[:buckets][:error])
59
-
60
- @summary[:compliance] = compute_compliance
107
+ data = Utils::InspecUtil.parse_data_for_ckl(@json)
108
+
109
+ data.keys.each do |control_id|
110
+ current_control = data[control_id]
111
+ current_control[:compliance_status] = Utils::InspecUtil.control_status(current_control, true)
112
+ current_control[:finding_details] = Utils::InspecUtil.control_finding_details(current_control, current_control[:compliance_status])
113
+ end
114
+
115
+ summary = {}
116
+ summary[:buckets] = {}
117
+ summary[:buckets][:failed] = select_by_status(data, 'Open')
118
+ summary[:buckets][:passed] = select_by_status(data, 'NotAFinding')
119
+ summary[:buckets][:no_impact] = select_by_status(data, 'Not_Applicable')
120
+ summary[:buckets][:skipped] = select_by_status(data, 'Not_Reviewed')
121
+ summary[:buckets][:error] = select_by_status(data, 'Profile_Error')
122
+
123
+ summary[:status] = {}
124
+ %i(failed passed no_impact skipped error).each do |key|
125
+ summary[:status][key] = tally_by_impact(summary[:buckets][key])
126
+ end
127
+ summary[:compliance] = compute_compliance(summary)
128
+ summary
61
129
  end
62
130
 
63
131
  def select_by_impact(controls, impact)
@@ -78,49 +146,13 @@ module InspecTools
78
146
  tally
79
147
  end
80
148
 
81
- def compute_compliance
82
- (@summary[:status][:passed][:total]*100.0/
83
- (@summary[:status][:passed][:total]+
84
- @summary[:status][:failed][:total]+
85
- @summary[:status][:skipped][:total]+
86
- @summary[:status][:error][:total])).floor
87
- end
88
-
89
- def threshold_compliance
90
- compliance = true
91
- failure = []
92
- max = @threshold['compliance.max']
93
- min = @threshold['compliance.min']
94
- if max != -1 and @summary[:compliance] > max
95
- compliance = false
96
- failure << "Expected compliance.max:#{max} got:#{@summary[:compliance]}"
97
- end
98
- if min != -1 and @summary[:compliance] < min
99
- compliance = false
100
- failure << "Expected compliance.min:#{min} got:#{@summary[:compliance]}"
101
- end
102
- status = @summary[:status]
103
- BUCKETS.each do |bucket|
104
- TALLYS.each do |tally|
105
- max = @threshold["#{bucket}.#{tally}.max"]
106
- min = @threshold["#{bucket}.#{tally}.min"]
107
- if max != -1 and status[bucket][tally] > max
108
- compliance = false
109
- failure << "Expected #{bucket}.#{tally}.max:#{max} got:#{status[bucket][tally]}"
110
- end
111
- if min != -1 and status[bucket][tally] < min
112
- compliance = false
113
- failure << "Expected #{bucket}.#{tally}.min:#{min} got:#{status[bucket][tally]}"
114
- end
115
- end
116
- end
117
- puts failure.join("\n") unless compliance
118
- puts 'Compliance threshold met' if compliance
119
- compliance
120
- end
121
-
122
- def parse_threshold(new_threshold)
123
- @threshold.merge!(new_threshold)
149
+ def compute_compliance(summary)
150
+ (summary[:status][:passed][:total]*100.0/
151
+ (summary[:status][:passed][:total]+
152
+ summary[:status][:failed][:total]+
153
+ summary[:status][:skipped][:total]+
154
+ summary[:status][:error][:total])).floor
124
155
  end
125
156
  end
157
+ # rubocop:enable Metrics/ClassLength
126
158
  end