inspec_tools 2.0.7 → 2.3.3

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.
@@ -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
@@ -17,7 +17,7 @@ module InspecTools
17
17
  @xccdf = replace_tags_in_xccdf(replace_tags, @xccdf) unless replace_tags.nil?
18
18
  cci_list_path = File.join(File.dirname(__FILE__), '../data/U_CCI_List.xml')
19
19
  @cci_items = HappyMapperTools::CCIAttributes::CCI_List.parse(File.read(cci_list_path))
20
- # @cci_items = HappyMapperTools::CCIAttributes::CCI_List.parse(File.read('./data/U_CCI_List.xml'))
20
+ register_after_parse_callbacks
21
21
  @benchmark = HappyMapperTools::StigAttributes::Benchmark.parse(@xccdf)
22
22
  end
23
23
 
@@ -89,6 +89,14 @@ module InspecTools
89
89
 
90
90
  private
91
91
 
92
+ def register_after_parse_callbacks
93
+ # Determine if the parsed Ident is refrencing a legacy ID number.
94
+ HappyMapperTools::StigAttributes::Ident.after_parse do |object|
95
+ object.cci = object.system.eql?('http://cyber.mil/cci')
96
+ object.legacy = !object.cci
97
+ end
98
+ end
99
+
92
100
  def replace_tags_in_xccdf(replace_tags, xccdf_xml)
93
101
  replace_tags.each do |tag|
94
102
  xccdf_xml = xccdf_xml.gsub(/(&lt;|<)#{tag}(&gt;|>)/, "$#{tag}")
@@ -133,8 +141,9 @@ module InspecTools
133
141
  control['tags']['rid'] = group.rule.id
134
142
  control['tags']['stig_id'] = group.rule.version
135
143
  control['tags']['fix_id'] = group.rule.fix.id
136
- control['tags']['cci'] = group.rule.idents
137
- control['tags']['nist'] = @cci_items.fetch_nists(group.rule.idents)
144
+ control['tags']['cci'] = group.rule.idents.select { |i| i.cci }.map { |i| i.ident }
145
+ control['tags']['legacy'] = group.rule.idents.select { |i| i.legacy}.map { |i| i.ident }
146
+ control['tags']['nist'] = @cci_items.fetch_nists(control['tags']['cci'])
138
147
  control['tags']['false_negatives'] = group.rule.description.false_negatives if group.rule.description.false_negatives != ''
139
148
  control['tags']['false_positives'] = group.rule.description.false_positives if group.rule.description.false_positives != ''
140
149
  control['tags']['documentable'] = group.rule.description.documentable if group.rule.description.documentable != ''
@@ -6,6 +6,7 @@ require 'digest'
6
6
 
7
7
  require_relative '../utilities/inspec_util'
8
8
  require_relative '../utilities/cis_to_nist'
9
+ require_relative '../utilities/mapping_validator'
9
10
 
10
11
  # rubocop:disable Metrics/AbcSize
11
12
  # rubocop:disable Metrics/PerceivedComplexity
@@ -19,7 +20,7 @@ module InspecTools
19
20
  def initialize(xlsx, mapping, name, verbose = false)
20
21
  @name = name
21
22
  @xlsx = xlsx
22
- @mapping = mapping
23
+ @mapping = Utils::MappingValidator.validate(mapping)
23
24
  @verbose = verbose
24
25
  @cis_to_nist = Utils::CisToNist.get_mapping('cis_to_nist_mapping')
25
26
  end
@@ -0,0 +1,13 @@
1
+ require 'nokogiri'
2
+
3
+ module Utils
4
+ class CciXml
5
+ def self.get_cci_list(cci_list_file)
6
+ path = File.expand_path(File.join(File.expand_path(__dir__), '..', 'data', cci_list_file))
7
+ raise "CCI list does not exist at #{path}" unless File.exist?(path)
8
+
9
+ cci_list = Nokogiri::XML(File.open(path))
10
+ cci_list.remove_namespaces!
11
+ end
12
+ end
13
+ end
@@ -13,15 +13,8 @@ require 'overrides/object'
13
13
  require 'overrides/string'
14
14
  require 'rubocop'
15
15
 
16
- # rubocop:disable Metrics/ClassLength
17
- # rubocop:disable Metrics/AbcSize
18
- # rubocop:disable Metrics/PerceivedComplexity
19
- # rubocop:disable Metrics/CyclomaticComplexity
20
- # rubocop:disable Metrics/MethodLength
21
-
22
16
  module Utils
23
- class InspecUtil
24
- DATA_NOT_FOUND_MESSAGE = 'N/A'.freeze
17
+ class InspecUtil # rubocop:disable Metrics/ClassLength
25
18
  WIDTH = 80
26
19
  IMPACT_SCORES = {
27
20
  'none' => 0.0,
@@ -31,56 +24,7 @@ module Utils
31
24
  'critical' => 0.9
32
25
  }.freeze
33
26
 
34
- def self.parse_data_for_xccdf(json)
35
- data = {}
36
-
37
- controls = []
38
- if json['profiles'].nil?
39
- controls = json['controls']
40
- elsif json['profiles'].length == 1
41
- controls = json['profiles'].last['controls']
42
- else
43
- json['profiles'].each do |profile|
44
- controls.concat(profile['controls'])
45
- end
46
- end
47
- c_data = {}
48
-
49
- controls.each do |control|
50
- c_id = control['id'].to_sym
51
- c_data[c_id] = {}
52
- c_data[c_id]['id'] = control['id'] || DATA_NOT_FOUND_MESSAGE
53
- c_data[c_id]['title'] = control['title'] || DATA_NOT_FOUND_MESSAGE
54
- c_data[c_id]['desc'] = control['desc'] || DATA_NOT_FOUND_MESSAGE
55
- c_data[c_id]['severity'] = control['tags']['severity'] || DATA_NOT_FOUND_MESSAGE
56
- c_data[c_id]['gid'] = control['tags']['gid'] || DATA_NOT_FOUND_MESSAGE
57
- c_data[c_id]['gtitle'] = control['tags']['gtitle'] || DATA_NOT_FOUND_MESSAGE
58
- c_data[c_id]['gdescription'] = control['tags']['gdescription'] || DATA_NOT_FOUND_MESSAGE
59
- c_data[c_id]['rid'] = control['tags']['rid'] || DATA_NOT_FOUND_MESSAGE
60
- c_data[c_id]['rversion'] = control['tags']['rversion'] || DATA_NOT_FOUND_MESSAGE
61
- c_data[c_id]['rweight'] = control['tags']['rweight'] || DATA_NOT_FOUND_MESSAGE
62
- c_data[c_id]['stig_id'] = control['tags']['stig_id'] || DATA_NOT_FOUND_MESSAGE
63
- c_data[c_id]['cci'] = control['tags']['cci'] || DATA_NOT_FOUND_MESSAGE
64
- c_data[c_id]['nist'] = control['tags']['nist'] || ['unmapped']
65
- c_data[c_id]['check'] = control['tags']['check'] || DATA_NOT_FOUND_MESSAGE
66
- c_data[c_id]['checkref'] = control['tags']['checkref'] || DATA_NOT_FOUND_MESSAGE
67
- c_data[c_id]['fix'] = control['tags']['fix'] || DATA_NOT_FOUND_MESSAGE
68
- c_data[c_id]['fixref'] = control['tags']['fixref'] || DATA_NOT_FOUND_MESSAGE
69
- c_data[c_id]['fix_id'] = control['tags']['fix_id'] || DATA_NOT_FOUND_MESSAGE
70
- c_data[c_id]['rationale'] = control['tags']['rationale'] || DATA_NOT_FOUND_MESSAGE
71
- c_data[c_id]['cis_family'] = control['tags']['cis_family'] || DATA_NOT_FOUND_MESSAGE
72
- c_data[c_id]['cis_rid'] = control['tags']['cis_rid'] || DATA_NOT_FOUND_MESSAGE
73
- c_data[c_id]['cis_level'] = control['tags']['cis_level'] || DATA_NOT_FOUND_MESSAGE
74
- c_data[c_id]['impact'] = control['impact'].to_s || DATA_NOT_FOUND_MESSAGE
75
- c_data[c_id]['code'] = control['code'].to_s || DATA_NOT_FOUND_MESSAGE
76
- end
77
-
78
- data['controls'] = c_data.values
79
- data['status'] = 'success'
80
- data
81
- end
82
-
83
- def self.parse_data_for_ckl(json)
27
+ def self.parse_data_for_ckl(json) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
84
28
  data = {}
85
29
 
86
30
  # Parse for inspec profile results json
@@ -104,8 +48,8 @@ module Utils
104
48
  end
105
49
 
106
50
  if control['descriptions'].respond_to?(:find)
107
- data[c_id][:check_content] = control['descriptions'].find { |c| c['label'] == 'fix' }&.dig('data')
108
- data[c_id][:fix_text] = control['descriptions'].find { |c| c['label'] == 'check' }&.dig('data')
51
+ data[c_id][:check_content] = control['descriptions'].find { |c| c['label'] == 'check' }&.dig('data')
52
+ data[c_id][:fix_text] = control['descriptions'].find { |c| c['label'] == 'fix' }&.dig('data')
109
53
  end
110
54
 
111
55
  data[c_id][:impact] = control['impact'].to_s unless control['impact'].nil?
@@ -221,7 +165,7 @@ module Utils
221
165
  end
222
166
 
223
167
  private_class_method def self.string_to_impact(severity, use_cvss_terms)
224
- if /none|na|n\/a|not[_|(\s*)]?applicable/i.match?(severity)
168
+ if %r{none|na|n/a|not[_|(\s*)]?applicable}i.match?(severity)
225
169
  impact = 0.0 # Informative
226
170
  elsif /low|cat(egory)?\s*(iii|3)/i.match?(severity)
227
171
  impact = 0.3 # Low Impact
@@ -248,13 +192,10 @@ module Utils
248
192
  end
249
193
 
250
194
  IMPACT_SCORES.reverse_each do |name, impact_score|
251
- if name == 'critical' && value >= impact_score && use_cvss_terms
252
- return 'high'
253
- elsif value >= impact_score
254
- return name
255
- else
256
- next
257
- end
195
+ return 'high' if name == 'critical' && value >= impact_score && use_cvss_terms
196
+ return name if value >= impact_score
197
+
198
+ next
258
199
  end
259
200
  end
260
201
 
@@ -277,7 +218,7 @@ module Utils
277
218
  WordWrap.ww(str.to_s, width)
278
219
  end
279
220
 
280
- private_class_method def self.generate_controls(inspec_json)
221
+ private_class_method def self.generate_controls(inspec_json) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
281
222
  controls = []
282
223
  inspec_json['controls'].each do |json_control|
283
224
  control = ::Inspec::Object::Control.new
@@ -305,6 +246,7 @@ module Utils
305
246
  control.add_tag(::Inspec::Object::Tag.new('stig_id', json_control['tags']['stig_id']))
306
247
  control.add_tag(::Inspec::Object::Tag.new('fix_id', json_control['tags']['fix_id']))
307
248
  control.add_tag(::Inspec::Object::Tag.new('cci', json_control['tags']['cci']))
249
+ control.add_tag(::Inspec::Object::Tag.new('legacy', json_control['tags']['legacy']))
308
250
  control.add_tag(::Inspec::Object::Tag.new('nist', json_control['tags']['nist']))
309
251
  control.add_tag(::Inspec::Object::Tag.new('cis_level', json_control['tags']['cis_level'])) unless json_control['tags']['cis_level'].blank?
310
252
  control.add_tag(::Inspec::Object::Tag.new('cis_controls', json_control['tags']['cis_controls'])) unless json_control['tags']['cis_controls'].blank?
@@ -384,7 +326,7 @@ module Utils
384
326
  myfile.puts readme_contents
385
327
  end
386
328
 
387
- private_class_method def self.unpack_profile(directory, controls, separated, output_format)
329
+ private_class_method def self.unpack_profile(directory, controls, separated, output_format) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
388
330
  FileUtils.rm_rf(directory) if Dir.exist?(directory)
389
331
  Dir.mkdir directory unless Dir.exist?(directory)
390
332
  Dir.mkdir "#{directory}/controls" unless Dir.exist?("#{directory}/controls")
@@ -433,9 +375,3 @@ module Utils
433
375
  end
434
376
  end
435
377
  end
436
-
437
- # rubocop:enable Metrics/ClassLength
438
- # rubocop:enable Metrics/AbcSize
439
- # rubocop:enable Metrics/PerceivedComplexity
440
- # rubocop:enable Metrics/CyclomaticComplexity
441
- # rubocop:enable Metrics/MethodLength