inspec_tools 2.0.3 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d99887f62c18c23143d73ceb049277beaf493a765dc1df5beec9fee9b9bb7dca
4
- data.tar.gz: c2613ab9b76c9dae510ba8c57a27664989161cc8dd0cfec099f1365a9743030f
3
+ metadata.gz: eee290c08f68bf8bb7894537a1871e0bdf5144266ee50932ea70af3018d5f94e
4
+ data.tar.gz: 42ed4ee43680aaceb6fa12c03bfe7c29dd88a33399787b7053510716182c627e
5
5
  SHA512:
6
- metadata.gz: e8f1fd2c3b491e7c3ef65c76c250e16e6e00cbb03ae0d3ed8c6c23bc8598ba31f8315bd72e6bcfb76b22c4a054f881852de0f290789c6d5bab304afc616003f4
7
- data.tar.gz: ad843429c15e5e10c655860a6178b20d088decbd4f6bc92817183635b45c7478be16b30baf6883d3e1565f47c0926226e117482351137811829c9e147de4be3e
6
+ metadata.gz: 43e9b4038ff40f6aee88a16fb63e536b7d3918f15b8a3f9b3fcf87688eb1a167cfd08a7f45cac6fcdd68a2cdd1b5f1ac9ab9a85a2bd42c983b9e963c0b18d96d
7
+ data.tar.gz: cd750538af91a05636ad406b80f1adc83bbacfa382143d472bbced9265227f3da245a8a23dd7de0af3636c1650b1c563c6621dc48f524db1550518e7bcc55ae5
data/README.md CHANGED
@@ -65,12 +65,12 @@ For Docker usage, replace the `inspec_tools` command with the correct Docker com
65
65
 
66
66
  - **On Linux and Mac**: `docker run -it -v$(pwd):/share mitre/inspec_tools`
67
67
  - **On Windows CMD**: `docker run -it -v%cd%:/share mitre/inspec_tools`
68
-
68
+
69
69
  Note that all of the above Docker commands will mount your current directory on the Docker container. Ensure that you have navigated to the directory you intend to convert files in before executing the command.
70
70
 
71
71
  ### generate_map
72
72
 
73
- This command will generate a `mapping.xml` file that can be passed in to the `csv2inspec` command with the `--m` option.
73
+ This command will generate a `mapping.yml` file that can be passed in to the `csv2inspec` command with the `--m` option.
74
74
 
75
75
  ```
76
76
  USAGE: inspec_tools generate_map
@@ -181,9 +181,9 @@ error
181
181
  low : 0
182
182
  ```
183
183
 
184
- Using additional flags will override the normal output and only display the output that flag specifies.
184
+ Using additional flags will override the normal output and only display the output that flag specifies.
185
185
 
186
- USAGE: inspec_tools summary [OPTIONS] -j <inspec-json>
186
+ USAGE: inspec_tools summary [OPTIONS] -j <inspec-json>
187
187
 
188
188
  ```
189
189
  FLAGS:
@@ -211,7 +211,7 @@ FLAGS:
211
211
  -f --format [ruby | hash] : the format you would like (default: ruby) [optional]
212
212
  -s --separate-files [true | false] : output the resulting controls as one or mutiple files (default: true) [optional]
213
213
  -m --metadata <metadata-json> : path to json file with additional metadata for the inspec.yml file [optional]
214
- -r --replace-tags <array> : A case-sensitive, comma separated list to replace tags with a $ if found in a group rules description tag [optional]
214
+ -r --replace-tags <array> : A case-sensitive, space separated list to replace tags with a $ if found in a group rules description tag [optional]
215
215
 
216
216
  example: inspec_tools xccdf2inspec -x xccdf_file.xml -a attributes.yml -o myprofile -f ruby -s false
217
217
  ```
@@ -220,16 +220,18 @@ example: inspec_tools xccdf2inspec -x xccdf_file.xml -a attributes.yml -o myprof
220
220
 
221
221
  `inspec2xccdf` converts an InSpec profile in json format to a STIG XCCDF Document
222
222
 
223
+ See [examples documentation](./examples/inspec2xccdf/README.md) for additional guidance on usage including attribute details.
224
+
223
225
  ```
224
226
  USAGE: inspec_tools inspec2xccdf [OPTIONS] -j <inspec-json> -a <xccdf-attr-yml> -o <xccdf-xml>
225
227
 
226
228
  FLAGS:
227
229
  -j --inspec-json <inspec-json> : path to InSpec Json file created using command 'inspec json <profile> > example.json'
228
- -a --attributes <xccdf-attr-yml> : 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
229
- -o --output <xccdf-xml> : name or path to create the xccdf and title to give the xccdf
230
- -V --verbose : verbose run [optional]
230
+ -a --attributes <xccdf-attr-yml> : 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.
231
+ -o --output <xccdf-xml> : name or path to create the XCCDF and title to give the XCCDF
232
+ -m, [--metadata=METADATA] : path to json file with additional host metadata for the XCCDF file
231
233
 
232
- example: inspec_tools inspec2xccdf -j example.json -a attributes.yml -o xccdf.xml
234
+ example: inspec_tools inspec2xccdf -j examples/sample_json/good_nginxresults.json -a lib/data/attributes.yml -o output.xccdf
233
235
  ```
234
236
 
235
237
  ## csv2inspec
@@ -295,7 +297,7 @@ FLAGS:
295
297
  -s --separate-files [true | false] : output the resulting controls as multiple files (default: true) [optional]
296
298
  -d --debug : debug run [optional]
297
299
 
298
- example: inspec_tools pdf2inspec -p benchmark.pdf -o /path/to/myprofile -f ruby -s true
300
+ example: inspec_tools pdf2inspec -p examples/CIS_Ubuntu_Linux_16.04_LTS_Benchmark_v1.0.0.pdf -o /path/to/myprofile -f ruby -s true
299
301
  ```
300
302
 
301
303
  ## xlsx2inspec
data/Rakefile CHANGED
@@ -1,10 +1,9 @@
1
- require "bundler/gem_tasks"
2
- require "rake/testtask"
1
+ require 'rake/testtask'
3
2
  require File.expand_path('../lib/inspec_tools/version', __FILE__)
4
3
 
5
4
  Rake::TestTask.new(:test) do |t|
6
- t.libs << "test"
7
- t.libs << "lib"
5
+ t.libs << 'test'
6
+ t.libs << 'lib'
8
7
  t.test_files = FileList['test/**/*_test.rb']
9
8
  end
10
9
 
@@ -20,11 +19,86 @@ namespace :test do
20
19
  'test/unit/inspec_tools_test.rb'
21
20
  ])
22
21
  end
22
+
23
+ Rake::TestTask.new(:exclude_slow) do |t|
24
+ t.description = 'Excluding all tests that take more than 3 seconds to complete'
25
+ t.libs << 'test'
26
+ t.libs << "lib"
27
+ t.verbose = true
28
+ t.test_files = FileList['test/**/*_test.rb'].reject{|file| file.include? 'pdf_test.rb'}.reverse
29
+ end
30
+ end
31
+
32
+ desc 'Build for release'
33
+ task :build_release do
34
+
35
+ Rake::Task["generate_mapping_objects"].reenable
36
+ Rake::Task["generate_mapping_objects"].invoke
37
+
38
+ system('gem build inspec_tools.gemspec')
39
+ end
40
+
41
+ desc 'Generate mapping objects'
42
+ task :generate_mapping_objects do
43
+ require 'roo'
44
+
45
+ nist_mapping_cis_controls = ENV['NIST_MAPPING_CIS_CONTROLS'] || 'NIST_Map_02052020_CIS_Controls_Version_7.1_Implementation_Groups_1.2.xlsx'.freeze
46
+ nist_mapping_cis_critical_controls = ENV['NIST_MAPPING_CIS_CRITICAL_CONTROLS'] || 'NIST_Map_09212017B_CSC-CIS_Critical_Security_Controls_VER_6.1_Excel_9.1.2016.xlsx'.freeze
47
+
48
+ data_root_path = File.join(File.expand_path(__dir__), 'lib', 'data')
49
+ cis_controls_path = File.join(data_root_path, nist_mapping_cis_controls)
50
+ cis_critical_controls_path = File.join(data_root_path, nist_mapping_cis_critical_controls)
51
+
52
+ raise "#{cis_controls_path} does not exist" unless File.exist?(cis_controls_path)
53
+
54
+ raise "#{cis_critical_controls_path} does not exist" unless File.exist?(cis_critical_controls_path)
55
+
56
+ marshal_cis_controls(cis_controls_path, data_root_path)
57
+ marshal_cis_critical_controls(cis_critical_controls_path, data_root_path)
23
58
  end
24
59
 
25
- desc 'Build and publish the gem'
26
- task publish: :build do
27
- system("gem push pkg/inspec_tools-#{InspecTools::VERSION}.gem")
60
+ def marshal_cis_controls(cis_controls_path, data_root_path)
61
+ cis_to_nist = {}
62
+ Roo::Spreadsheet.open(cis_controls_path).sheet(3).each do |row|
63
+ if row[3].is_a?(Numeric)
64
+ cis_to_nist[row[3].to_s] = row[0]
65
+ else
66
+ cis_to_nist[row[2].to_s] = row[0] unless (row[2] == '') || row[2].to_i.nil?
67
+ end
68
+ end
69
+ output_file = File.new(File.join(data_root_path, 'cis_to_nist_mapping'), 'w')
70
+ Marshal.dump(cis_to_nist, output_file)
71
+ output_file.close
28
72
  end
29
73
 
30
- task :default => :test
74
+ def marshal_cis_critical_controls(cis_critical_controls_path, data_root_path)
75
+ controls_spreadsheet = Roo::Spreadsheet.open(cis_critical_controls_path)
76
+ controls_spreadsheet.default_sheet = 'VER 6.1 Controls'
77
+ headings = {}
78
+ controls_spreadsheet.row(3).each_with_index { |header, idx| headings[header] = idx }
79
+
80
+ nist_ver = 4
81
+ cis_ver = controls_spreadsheet.row(2)[4].split(' ')[-1]
82
+ control_count = 1
83
+ mapping = []
84
+ ((controls_spreadsheet.first_row + 3)..controls_spreadsheet.last_row).each do |row_value|
85
+ current_row = {}
86
+ if controls_spreadsheet.row(row_value)[headings['NIST SP 800-53 Control #']].to_s != ''
87
+ current_row[:nist] = controls_spreadsheet.row(row_value)[headings['NIST SP 800-53 Control #']].to_s
88
+ else
89
+ current_row[:nist] = 'Not Mapped'
90
+ end
91
+ current_row[:nist_ver] = nist_ver
92
+ if controls_spreadsheet.row(row_value)[headings['Control']].to_s == ''
93
+ current_row[:cis] = control_count.to_s
94
+ control_count += 1
95
+ else
96
+ current_row[:cis] = controls_spreadsheet.row(row_value)[headings['Control']].to_s
97
+ end
98
+ current_row[:cis_ver] = cis_ver
99
+ mapping << current_row
100
+ end
101
+ output_file = File.new(File.join(data_root_path, 'cis_to_nist_critical_controls'), 'w')
102
+ Marshal.dump(mapping, output_file)
103
+ output_file.close
104
+ end
@@ -0,0 +1,4 @@
1
+ AllCops:
2
+ DisabledByDefault: true
3
+ Style/StringLiterals:
4
+ Enabled: true
@@ -99,6 +99,88 @@ module HappyMapperTools
99
99
  element :content, String, tag: 'check-content'
100
100
  end
101
101
 
102
+ class MessageType
103
+ include HappyMapper
104
+ attribute :severity, String, tag: 'severity'
105
+ content :message, String
106
+ end
107
+
108
+ class RuleResultType
109
+ include HappyMapper
110
+ attribute :idref, String, tag: 'idref'
111
+ attribute :severity, String, tag: 'severity'
112
+ attribute :time, String, tag: 'time'
113
+ attribute :weight, String, tag: 'weight'
114
+ element :result, String, tag: 'result'
115
+ # element override - Not implemented. Does not apply to Inspec execution
116
+ has_many :ident, Ident, tag: 'ident'
117
+ # Note: element metadata not implemented at this time
118
+ has_many :message, MessageType, tag: 'message'
119
+ has_many :instance, String, tag: 'instance'
120
+ element :fix, Fix, tag: 'fix'
121
+ element :check, Check, tag: 'check'
122
+ end
123
+
124
+ class ScoreType
125
+ include HappyMapper
126
+
127
+ def initialize(system, maximum, score)
128
+ @system = system
129
+ @maximum = maximum
130
+ @score = score
131
+ end
132
+
133
+ attribute :system, String, tag: 'system'
134
+ attribute :maximum, String, tag: 'maximum' # optional attribute
135
+ content :score, String
136
+ end
137
+
138
+ class CPE2idrefType
139
+ include HappyMapper
140
+ attribute :idref, String, tag: 'idref'
141
+ end
142
+
143
+ class IdentityType
144
+ include HappyMapper
145
+ attribute :authenticated, Boolean, tag: 'authenticated'
146
+ attribute :privileged, Boolean, tag: 'privileged'
147
+ content :identity, String
148
+ end
149
+
150
+ class Fact
151
+ include HappyMapper
152
+ attribute :name, String, tag: 'name'
153
+ attribute :type, String, tag: 'type'
154
+ content :fact, String
155
+ end
156
+
157
+ class TargetFact
158
+ include HappyMapper
159
+ has_many :fact, Fact, tag: 'fact'
160
+ end
161
+
162
+ class TestResult
163
+ include HappyMapper
164
+ # Note: element benchmark not implemented at this time since this is same file
165
+ # Note: element title not implemented due to no mapping from Chef Inspec
166
+ element :remark, String, tag: 'remark'
167
+ has_many :organization, String, tag: 'organization'
168
+ element :identity, IdentityType, tag: 'identity'
169
+ element :target, String, tag: 'target'
170
+ has_many :target_address, String, tag: 'target-address'
171
+ element :target_facts, TargetFact, tag: 'target-facts'
172
+ element :platform, CPE2idrefType, tag: 'platform'
173
+ # Note: element profile not implemented since Benchmark profile is also not implemented
174
+ has_many :rule_result, RuleResultType, tag: 'rule-result'
175
+ has_many :score, ScoreType, tag: 'score' # One minimum
176
+ # Note: element signature not implemented due to no mapping from Chef Inspec
177
+ attribute :id, String, tag: 'id'
178
+ attribute :starttime, String, tag: 'start-time'
179
+ attribute :endtime, String, tag: 'end-time'
180
+ # Note: attribute test-system not implemented at this time due to unknown CPE value for Chef Inspec
181
+ attribute :version, String, tag: 'version'
182
+ end
183
+
102
184
  # Class Profile maps from the 'Profile' from Benchmark XML file using HappyMapper
103
185
  class Profile
104
186
  include HappyMapper
@@ -156,6 +238,7 @@ module HappyMapperTools
156
238
  element :version, String, tag: 'version'
157
239
  has_many :profile, Profile, tag: 'Profile'
158
240
  has_many :group, Group, tag: 'Group'
241
+ element :testresult, TestResult, tag: 'TestResult'
159
242
  end
160
243
  end
161
244
  end
@@ -38,6 +38,7 @@ module HappyMapperTools
38
38
  element :documentable, Boolean, tag: 'Documentable'
39
39
  element :mitigations, String, tag: 'Mitigations'
40
40
  element :severity_override_guidance, String, tag: 'SeverityOverrideGuidance'
41
+ element :security_override_guidance, String, tag: 'SecurityOverrideGuidance'
41
42
  element :potential_impacts, String, tag: 'PotentialImpacts'
42
43
  element :third_party_tools, String, tag: 'ThirdPartyTools'
43
44
  element :mitigation_controls, String, tag: 'MitigationControl'
@@ -53,7 +54,8 @@ module HappyMapperTools
53
54
 
54
55
  detail_tags = %i(vuln_discussion false_positives false_negatives documentable
55
56
  mitigations severity_override_guidance potential_impacts
56
- third_party_tools mitigation_controls responsibility ia_controls)
57
+ third_party_tools mitigation_controls responsibility ia_controls
58
+ security_override_guidance)
57
59
 
58
60
  detail_tags.each do |name|
59
61
  define_method name do
@@ -140,57 +142,75 @@ module HappyMapperTools
140
142
  end
141
143
 
142
144
  class DescriptionDetailsType
143
- def self.type
144
- DescriptionDetails
145
- end
145
+ class << self
146
+ def type
147
+ DescriptionDetails
148
+ end
146
149
 
147
- def self.apply(value) # rubocop:disable Metrics/AbcSize
148
- value = value.gsub('&', 'and')
149
- DescriptionDetails.parse "<Details>#{value}</Details>"
150
- rescue Nokogiri::XML::SyntaxError
151
- allowed_tags = %w{VulnDiscussion FalsePositives FalseNegatives Documentable
152
- Mitigations SeverityOverrideGuidance PotentialImpacts
153
- PotentialImpacts ThirdPartyTools MitigationControl
154
- Responsibility IAControls}
155
-
156
- tags_found = value.scan(%r{(?<=<)([^\/]*?)((?= \/>)|(?=>))}).to_a
157
-
158
- tags_found = tags_found.uniq.flatten.reject!(&:empty?)
159
- offending_tags = tags_found - allowed_tags
160
-
161
- if offending_tags.count > 1
162
- puts "\n\nThe non-standard tags: #{offending_tags.to_s.colorize(:red)}" \
163
- ' were found in: ' + "\n\n#{value}"
164
- else
165
- puts "\n\nThe non-standard tag: #{offending_tags.to_s.colorize(:red)}" \
166
- ' was found in: ' + "\n\n#{value}"
150
+ def apply(value)
151
+ value = value.gsub('&', 'and')
152
+ DescriptionDetails.parse "<Details>#{value}</Details>"
153
+ rescue Nokogiri::XML::SyntaxError => e
154
+ if e.to_s.include?('StartTag')
155
+ report_invalid_start_tag(value, e)
156
+ else
157
+ report_disallowed_tags(value)
158
+ end
159
+ end
160
+
161
+ def apply?(value, _convert_to_type)
162
+ value.is_a?(String)
167
163
  end
168
- puts "\n\nPlease:\n "
169
- option_one = '(1) ' + '(best)'.colorize(:green) + ' Use the ' +
170
- '`-r --replace-tags array` '.colorize(:light_yellow) +
171
- '(case sensitive) option to replace the offending tags ' \
172
- 'during processing of the XCCDF ' \
173
- 'file to use the ' +
174
- "`$#{offending_tags[0]}` " .colorize(:light_green) +
175
- 'syntax in your InSpec profile.'
176
- option_two = '(2) Update your XCCDF file to *not use* non-standard XCCDF ' \
177
- 'elements within ' +
178
- '`&lt;`,`&gt;`, `<` '.colorize(:red) +
179
- 'or '.colorize(:default) +
180
- '`>` '.colorize(:red) +
181
- 'as "placeholders", and use something that doesn\'t confuse ' \
182
- 'the XML parser, such as : ' +
183
- "`$#{offending_tags[0]}`" .colorize(:light_green)
184
- puts option_one
185
- puts "\n"
186
- puts option_two
187
- # exit
188
- end
189
164
 
190
- def self.apply?(value, _convert_to_type)
191
- value.is_a?(String)
165
+ private
166
+
167
+ def report_invalid_start_tag(value, error)
168
+ puts error.to_s.colorize(:red)
169
+ column = error.column - '<Details>'.length - 2
170
+ puts "Error around #{value[column-10..column+10].colorize(:light_yellow)}"
171
+ exit(1)
172
+ end
173
+
174
+ def report_disallowed_tags(value)
175
+ allowed_tags = %w{VulnDiscussion FalsePositives FalseNegatives Documentable
176
+ Mitigations SeverityOverrideGuidance PotentialImpacts
177
+ PotentialImpacts ThirdPartyTools MitigationControl
178
+ Responsibility IAControl SecurityOverrideGuidance}
179
+
180
+ tags_found = value.scan(%r{(?<=<)([^\/]*?)((?= \/>)|(?=>))}).to_a
181
+
182
+ tags_found = tags_found.uniq.flatten.reject!(&:empty?)
183
+ offending_tags = tags_found - allowed_tags
184
+
185
+ if offending_tags.count > 1
186
+ puts "\n\nThe non-standard tags: #{offending_tags.to_s.colorize(:red)}" \
187
+ ' were found in: ' + "\n\n#{value}"
188
+ else
189
+ puts "\n\nThe non-standard tag: #{offending_tags.to_s.colorize(:red)}" \
190
+ ' was found in: ' + "\n\n#{value}"
191
+ end
192
+ puts "\n\nPlease:\n "
193
+ option_one = '(1) ' + '(best)'.colorize(:green) + ' Use the ' +
194
+ '`-r --replace-tags array` '.colorize(:light_yellow) +
195
+ '(case sensitive) option to replace the offending tags ' \
196
+ 'during processing of the XCCDF ' \
197
+ 'file to use the ' +
198
+ "`$#{offending_tags[0]}` " .colorize(:light_green) +
199
+ 'syntax in your InSpec profile.'
200
+ option_two = '(2) Update your XCCDF file to *not use* non-standard XCCDF ' \
201
+ 'elements within ' +
202
+ '`&lt;`,`&gt;`, `<` '.colorize(:red) +
203
+ 'or '.colorize(:default) +
204
+ '`>` '.colorize(:red) +
205
+ 'as "placeholders", and use something that doesn\'t confuse ' \
206
+ 'the XML parser, such as : ' +
207
+ "`$#{offending_tags[0]}`" .colorize(:light_green)
208
+ puts option_one
209
+ puts "\n"
210
+ puts option_two
211
+ end
192
212
  end
213
+ HappyMapper::SupportedTypes.register DescriptionDetailsType
193
214
  end
194
- HappyMapper::SupportedTypes.register DescriptionDetailsType
195
215
  end
196
216
  end
@@ -1,10 +1,10 @@
1
1
  require 'csv'
2
- require 'nokogiri'
3
- require 'word_wrap'
4
2
  require 'yaml'
5
3
  require 'digest'
6
4
 
7
5
  require_relative '../utilities/inspec_util'
6
+ require_relative '../utilities/cci_xml'
7
+ require_relative '../utilities/mapping_validator'
8
8
 
9
9
  # rubocop:disable Metrics/AbcSize
10
10
  # rubocop:disable Metrics/PerceivedComplexity
@@ -16,34 +16,25 @@ module InspecTools
16
16
  def initialize(csv, mapping, name, verbose = false)
17
17
  @name = name
18
18
  @csv = csv
19
- @mapping = mapping
19
+ @mapping = Utils::MappingValidator.validate(mapping)
20
20
  @verbose = verbose
21
21
  @csv.shift if @mapping['skip_csv_header']
22
22
  end
23
23
 
24
- def to_ckl
25
- # TODO
26
- end
27
-
28
- def to_xccdf
29
- # TODO
30
- end
31
-
32
24
  def to_inspec
33
25
  @controls = []
34
- @cci_xml = nil
35
26
  @profile = {}
36
- read_cci_xml
37
- insert_json_metadata
27
+ @cci_xml = Utils::CciXml.get_cci_list('U_CCI_List.xml')
28
+ insert_metadata
38
29
  parse_controls
39
30
  @profile['controls'] = @controls
40
- @profile['sha256'] = Digest::SHA256.hexdigest @profile.to_s
31
+ @profile['sha256'] = Digest::SHA256.hexdigest(@profile.to_s)
41
32
  @profile
42
33
  end
43
34
 
44
35
  private
45
36
 
46
- def insert_json_metadata
37
+ def insert_metadata
47
38
  @profile['name'] = @name
48
39
  @profile['title'] = 'InSpec Profile'
49
40
  @profile['maintainer'] = 'The Authors'
@@ -60,35 +51,37 @@ module InspecTools
60
51
  }
61
52
  end
62
53
 
63
- def read_cci_xml
64
- cci_list_path = File.join(File.dirname(__FILE__), '../data/U_CCI_List.xml')
65
- @cci_xml = Nokogiri::XML(File.open(cci_list_path))
66
- @cci_xml.remove_namespaces!
67
- rescue StandardError => e
68
- puts "Exception: #{e.message}"
69
- end
70
-
71
54
  def get_nist_reference(cci_number)
72
55
  item_node = @cci_xml.xpath("//cci_list/cci_items/cci_item[@id='#{cci_number}']")[0] unless @cci_xml.nil?
73
- unless item_node.nil?
74
- nist_ref = item_node.xpath('./references/reference[not(@version <= preceding-sibling::reference/@version) and not(@version <=following-sibling::reference/@version)]/@index').text
75
- nist_ver = item_node.xpath('./references/reference[not(@version <= preceding-sibling::reference/@version) and not(@version <=following-sibling::reference/@version)]/@version').text
76
- end
77
- [nist_ref, nist_ver]
56
+ return nil if item_node.nil?
57
+
58
+ [] << item_node.xpath('./references/reference[not(@version <= preceding-sibling::reference/@version) and not(@version <=following-sibling::reference/@version)]/@index').text
59
+ end
60
+
61
+ def get_cci_number(cell)
62
+ # Return nil if a mapping to the CCI was not provided or if there is not content in the CSV cell.
63
+ return nil if cell.nil? || @mapping['control.tags']['cci'].nil?
64
+
65
+ # If the content has been exported from STIG Viewer, the cell will have extra information
66
+ cell.split("\n").first
78
67
  end
79
68
 
80
69
  def parse_controls
81
70
  @csv.each do |row|
82
- print '.'
83
71
  control = {}
84
72
  control['id'] = row[@mapping['control.id']] unless @mapping['control.id'].nil? || row[@mapping['control.id']].nil?
85
73
  control['title'] = row[@mapping['control.title']] unless @mapping['control.title'].nil? || row[@mapping['control.title']].nil?
86
74
  control['desc'] = row[@mapping['control.desc']] unless @mapping['control.desc'].nil? || row[@mapping['control.desc']].nil?
87
75
  control['tags'] = {}
88
- nist, nist_rev = get_nist_reference(row[@mapping['control.tags']['cci']]) unless @mapping['control.tags']['cci'].nil? || row[@mapping['control.tags']['cci']].nil?
89
- control['tags']['nist'] = [nist, 'Rev_' + nist_rev] unless nist.nil? || nist_rev.nil?
76
+ cci_number = get_cci_number(row[@mapping['control.tags']['cci']])
77
+ nist = get_nist_reference(cci_number) unless cci_number.nil?
78
+ control['tags']['nist'] = nist unless nist.nil? || nist.include?(nil)
90
79
  @mapping['control.tags'].each do |tag|
91
- control['tags'][tag.first.to_s] = row[tag.last] unless row[tag.last].nil?
80
+ if tag.first == 'cci'
81
+ control['tags'][tag.first] = cci_number
82
+ next
83
+ end
84
+ control['tags'][tag.first] = row[tag.last] unless row[tag.last].nil?
92
85
  end
93
86
  unless @mapping['control.tags']['severity'].nil? || row[@mapping['control.tags']['severity']].nil?
94
87
  control['impact'] = Utils::InspecUtil.get_impact(row[@mapping['control.tags']['severity']])