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 +4 -4
- data/README.md +12 -10
- data/Rakefile +82 -8
- data/lib/data/cis_to_nist_critical_controls +0 -0
- data/lib/data/cis_to_nist_mapping +0 -0
- data/lib/data/rubocop.yml +4 -0
- data/lib/happy_mapper_tools/benchmark.rb +83 -0
- data/lib/happy_mapper_tools/stig_attributes.rb +68 -48
- data/lib/inspec_tools/csv.rb +26 -33
- data/lib/inspec_tools/generate_map.rb +35 -0
- data/lib/inspec_tools/inspec.rb +19 -91
- data/lib/inspec_tools/pdf.rb +2 -13
- data/lib/inspec_tools/plugin_cli.rb +14 -25
- data/lib/inspec_tools/xccdf.rb +1 -0
- data/lib/inspec_tools/xlsx_tool.rb +4 -16
- data/lib/utilities/cci_xml.rb +13 -0
- data/lib/utilities/cis_to_nist.rb +11 -0
- data/lib/utilities/inspec_util.rb +16 -75
- data/lib/utilities/mapping_validator.rb +10 -0
- data/lib/utilities/xccdf/from_inspec.rb +89 -0
- data/lib/utilities/xccdf/to_xccdf.rb +388 -0
- data/lib/utilities/xccdf/xccdf_score.rb +116 -0
- metadata +47 -27
- data/CHANGELOG.md +0 -704
- data/lib/data/NIST_Map_02052020_CIS_Controls_Version_7.1_Implementation_Groups_1.2.xlsx +0 -0
- data/lib/data/NIST_Map_09212017B_CSC-CIS_Critical_Security_Controls_VER_6.1_Excel_9.1.2016.xlsx +0 -0
- data/lib/utilities/extract_nist_cis_mapping.rb +0 -57
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eee290c08f68bf8bb7894537a1871e0bdf5144266ee50932ea70af3018d5f94e
|
4
|
+
data.tar.gz: 42ed4ee43680aaceb6fa12c03bfe7c29dd88a33399787b7053510716182c627e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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,
|
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
|
229
|
-
-o --output <xccdf-xml> : name or path to create the
|
230
|
-
|
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
|
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
|
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
|
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 <<
|
7
|
-
t.libs <<
|
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
|
-
|
26
|
-
|
27
|
-
|
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
|
-
|
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
|
Binary file
|
Binary file
|
@@ -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
|
-
|
144
|
-
|
145
|
-
|
145
|
+
class << self
|
146
|
+
def type
|
147
|
+
DescriptionDetails
|
148
|
+
end
|
146
149
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
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
|
-
'`<`,`>`, `<` '.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
|
-
|
191
|
-
|
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
|
+
'`<`,`>`, `<` '.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
|
data/lib/inspec_tools/csv.rb
CHANGED
@@ -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
|
-
|
37
|
-
|
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
|
31
|
+
@profile['sha256'] = Digest::SHA256.hexdigest(@profile.to_s)
|
41
32
|
@profile
|
42
33
|
end
|
43
34
|
|
44
35
|
private
|
45
36
|
|
46
|
-
def
|
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
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
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
|
-
|
89
|
-
|
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
|
-
|
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']])
|