inspec_tools 2.3.4 → 2.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -0
- data/exe/inspec_tools +2 -2
- data/lib/happy_mapper_tools/benchmark.rb +14 -7
- data/lib/happy_mapper_tools/cci_attributes.rb +0 -4
- data/lib/happy_mapper_tools/stig_attributes.rb +32 -32
- data/lib/happy_mapper_tools/stig_checklist.rb +19 -0
- data/lib/inspec_tools/cli.rb +1 -1
- data/lib/inspec_tools/csv.rb +2 -10
- data/lib/inspec_tools/generate_map.rb +13 -13
- data/lib/inspec_tools/help.rb +8 -6
- data/lib/inspec_tools/inspec.rb +35 -25
- data/lib/inspec_tools/pdf.rb +4 -8
- data/lib/inspec_tools/plugin_cli.rb +3 -5
- data/lib/inspec_tools/summary.rb +1 -9
- data/lib/inspec_tools/xccdf.rb +9 -12
- data/lib/inspec_tools/xlsx_tool.rb +5 -12
- data/lib/overrides/string.rb +1 -1
- data/lib/utilities/inspec_util.rb +14 -13
- data/lib/utilities/parser.rb +4 -5
- data/lib/utilities/text_cleaner.rb +8 -13
- data/lib/utilities/xccdf/from_inspec.rb +2 -2
- data/lib/utilities/xccdf/to_xccdf.rb +8 -8
- metadata +20 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b6f754f4816775d8bb360ed2ce768b1f4a443f647a440a1fe04a371388af6fe0
|
|
4
|
+
data.tar.gz: d31e79fb1477ab0b9fed85df8847f59f487d1ca5dc0bfc369e201dd2e0b87ba8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5f4fe6b5f733d4654d179e0bbe651276afa3ccfff6527e6088f50c13da23324b1be9c6b695cad464895f13e53b442dd674d546c826b31632def647b73d50d26e
|
|
7
|
+
data.tar.gz: bee2b9f2a53efa941413308ea6eaeb340493c8d581b07a4d348094ba1278c4177fd3de7ee6575226896d41d0156bf6eaf0934a5f0eb597956c1b4c5ff2c07c67
|
data/README.md
CHANGED
|
@@ -218,6 +218,7 @@ FLAGS:
|
|
|
218
218
|
-s --separate-files [true | false] : output the resulting controls as one or mutiple files (default: true) [optional]
|
|
219
219
|
-m --metadata <metadata-json> : path to json file with additional metadata for the inspec.yml file [optional]
|
|
220
220
|
-r --replace-tags <array> : A case-sensitive, space separated list to replace tags with a $ if found in a group rules description tag [optional]
|
|
221
|
+
-c --control-id [vulnID | ruleID] : use either legacy Vuln ID (ex. 'V-XXXXX') or Rule ID (ex. 'SV-XXXXX') as the overall Control ID (default: vulnID) [optional]
|
|
221
222
|
|
|
222
223
|
example: inspec_tools xccdf2inspec -x xccdf_file.xml -a attributes.yml -o myprofile -f ruby -s false
|
|
223
224
|
```
|
data/exe/inspec_tools
CHANGED
|
@@ -67,11 +67,18 @@ module HappyMapperTools
|
|
|
67
67
|
content :ident, String
|
|
68
68
|
def initialize(ident_str)
|
|
69
69
|
@ident = ident_str
|
|
70
|
-
|
|
70
|
+
case ident_str
|
|
71
|
+
when /^(CCI-[0-9]{6})$/
|
|
72
|
+
# Match CCI IDs; e.g. CCI-123456
|
|
71
73
|
@system = 'http://cyber.mil/cci'
|
|
72
|
-
|
|
74
|
+
when /^(S?V-[0-9]{5})$/
|
|
75
|
+
# Match SV- IDs; e.g. SV-12345
|
|
76
|
+
# Match V- IDs; e.g. V-12345
|
|
73
77
|
@system = 'http://cyber.mil/legacy'
|
|
74
|
-
|
|
78
|
+
else
|
|
79
|
+
# for all other ident_str, use the old identifier
|
|
80
|
+
@system = 'https://public.cyber.mil/stigs/cci/'
|
|
81
|
+
end
|
|
75
82
|
end
|
|
76
83
|
end
|
|
77
84
|
|
|
@@ -122,7 +129,7 @@ module HappyMapperTools
|
|
|
122
129
|
element :result, String, tag: 'result'
|
|
123
130
|
# element override - Not implemented. Does not apply to Inspec execution
|
|
124
131
|
has_many :ident, Ident, tag: 'ident'
|
|
125
|
-
#
|
|
132
|
+
# NOTE: element metadata not implemented at this time
|
|
126
133
|
has_many :message, MessageType, tag: 'message'
|
|
127
134
|
has_many :instance, String, tag: 'instance'
|
|
128
135
|
element :fix, Fix, tag: 'fix'
|
|
@@ -169,7 +176,7 @@ module HappyMapperTools
|
|
|
169
176
|
|
|
170
177
|
class TestResult
|
|
171
178
|
include HappyMapper
|
|
172
|
-
#
|
|
179
|
+
# NOTE: element benchmark not implemented at this time since this is same file
|
|
173
180
|
# Note: element title not implemented due to no mapping from Chef Inspec
|
|
174
181
|
element :remark, String, tag: 'remark'
|
|
175
182
|
has_many :organization, String, tag: 'organization'
|
|
@@ -178,14 +185,14 @@ module HappyMapperTools
|
|
|
178
185
|
has_many :target_address, String, tag: 'target-address'
|
|
179
186
|
element :target_facts, TargetFact, tag: 'target-facts'
|
|
180
187
|
element :platform, CPE2idrefType, tag: 'platform'
|
|
181
|
-
#
|
|
188
|
+
# NOTE: element profile not implemented since Benchmark profile is also not implemented
|
|
182
189
|
has_many :rule_result, RuleResultType, tag: 'rule-result'
|
|
183
190
|
has_many :score, ScoreType, tag: 'score' # One minimum
|
|
184
191
|
# Note: element signature not implemented due to no mapping from Chef Inspec
|
|
185
192
|
attribute :id, String, tag: 'id'
|
|
186
193
|
attribute :starttime, String, tag: 'start-time'
|
|
187
194
|
attribute :endtime, String, tag: 'end-time'
|
|
188
|
-
#
|
|
195
|
+
# NOTE: attribute test-system not implemented at this time due to unknown CPE value for Chef Inspec
|
|
189
196
|
attribute :version, String, tag: 'version'
|
|
190
197
|
end
|
|
191
198
|
|
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
require 'happymapper'
|
|
4
4
|
require 'nokogiri'
|
|
5
5
|
|
|
6
|
-
# rubocop:disable Naming/ClassAndModuleCamelCase
|
|
7
|
-
|
|
8
6
|
module HappyMapperTools
|
|
9
7
|
module CCIAttributes
|
|
10
8
|
class Reference
|
|
@@ -62,5 +60,3 @@ module HappyMapperTools
|
|
|
62
60
|
end
|
|
63
61
|
end
|
|
64
62
|
end
|
|
65
|
-
|
|
66
|
-
# rubocop:enable Naming/ClassAndModuleCamelCase
|
|
@@ -79,8 +79,8 @@ module HappyMapperTools
|
|
|
79
79
|
|
|
80
80
|
class Ident
|
|
81
81
|
include HappyMapper
|
|
82
|
-
attr_accessor :legacy
|
|
83
|
-
|
|
82
|
+
attr_accessor :legacy, :cci
|
|
83
|
+
|
|
84
84
|
tag 'ident'
|
|
85
85
|
attribute :system, String, tag: 'system'
|
|
86
86
|
content :ident, String
|
|
@@ -158,12 +158,13 @@ module HappyMapperTools
|
|
|
158
158
|
|
|
159
159
|
def apply(value)
|
|
160
160
|
value = value.gsub('&', 'and')
|
|
161
|
-
|
|
161
|
+
value = value.gsub('"<"', 'less than (converted less than)')
|
|
162
|
+
DescriptionDetails.parse("<Details>#{value}</Details>")
|
|
162
163
|
rescue Nokogiri::XML::SyntaxError => e
|
|
163
|
-
if
|
|
164
|
-
|
|
164
|
+
if report_disallowed_tags(value) # if there was a bad tag
|
|
165
|
+
exit(1)
|
|
165
166
|
else
|
|
166
|
-
|
|
167
|
+
report_error(value, e)
|
|
167
168
|
end
|
|
168
169
|
end
|
|
169
170
|
|
|
@@ -173,7 +174,7 @@ module HappyMapperTools
|
|
|
173
174
|
|
|
174
175
|
private
|
|
175
176
|
|
|
176
|
-
def
|
|
177
|
+
def report_error(value, error)
|
|
177
178
|
puts error.to_s.colorize(:red)
|
|
178
179
|
column = error.column - '<Details>'.length - 2
|
|
179
180
|
puts "Error around #{value[column-10..column+10].colorize(:light_yellow)}"
|
|
@@ -184,39 +185,38 @@ module HappyMapperTools
|
|
|
184
185
|
allowed_tags = %w{VulnDiscussion FalsePositives FalseNegatives Documentable
|
|
185
186
|
Mitigations SeverityOverrideGuidance PotentialImpacts
|
|
186
187
|
PotentialImpacts ThirdPartyTools MitigationControl
|
|
187
|
-
Responsibility IAControl SecurityOverrideGuidance}
|
|
188
|
+
Responsibility IAControl IAControls SecurityOverrideGuidance}
|
|
188
189
|
|
|
189
190
|
tags_found = value.scan(%r{(?<=<)([^\/]*?)((?= \/>)|(?=>))}).to_a
|
|
190
191
|
|
|
191
192
|
tags_found = tags_found.uniq.flatten.reject!(&:empty?)
|
|
192
193
|
offending_tags = tags_found - allowed_tags
|
|
193
194
|
|
|
194
|
-
|
|
195
|
-
puts "\n\nThe non-standard
|
|
195
|
+
unless offending_tags.count.zero?
|
|
196
|
+
puts "\n\nThe non-standard tag(s): #{offending_tags.to_s.colorize(:red)}" \
|
|
196
197
|
' were found in: ' + "\n\n#{value}"
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
198
|
+
puts "\n\nPlease:\n "
|
|
199
|
+
option_one = '(1) ' + '(best)'.colorize(:green) + ' Use the ' +
|
|
200
|
+
'`-r --replace-tags array` '.colorize(:light_yellow) +
|
|
201
|
+
'(case sensitive) option to replace the offending tags ' \
|
|
202
|
+
'during processing of the XCCDF ' \
|
|
203
|
+
'file to use the ' +
|
|
204
|
+
"`$#{offending_tags[0]}` ".colorize(:light_green) +
|
|
205
|
+
'syntax in your InSpec profile.'
|
|
206
|
+
option_two = '(2) Update your XCCDF file to *not use* non-standard XCCDF ' \
|
|
207
|
+
'elements within ' +
|
|
208
|
+
'`<`,`>`, `<` '.colorize(:red) +
|
|
209
|
+
'or '.colorize(:default) +
|
|
210
|
+
'`>` '.colorize(:red) +
|
|
211
|
+
'as "placeholders", and use something that doesn\'t confuse ' \
|
|
212
|
+
'the XML parser, such as : ' +
|
|
213
|
+
"`$#{offending_tags[0]}`".colorize(:light_green)
|
|
214
|
+
puts option_one
|
|
215
|
+
puts "\n"
|
|
216
|
+
puts option_two
|
|
217
|
+
return true
|
|
200
218
|
end
|
|
201
|
-
|
|
202
|
-
option_one = '(1) ' + '(best)'.colorize(:green) + ' Use the ' +
|
|
203
|
-
'`-r --replace-tags array` '.colorize(:light_yellow) +
|
|
204
|
-
'(case sensitive) option to replace the offending tags ' \
|
|
205
|
-
'during processing of the XCCDF ' \
|
|
206
|
-
'file to use the ' +
|
|
207
|
-
"`$#{offending_tags[0]}` " .colorize(:light_green) +
|
|
208
|
-
'syntax in your InSpec profile.'
|
|
209
|
-
option_two = '(2) Update your XCCDF file to *not use* non-standard XCCDF ' \
|
|
210
|
-
'elements within ' +
|
|
211
|
-
'`<`,`>`, `<` '.colorize(:red) +
|
|
212
|
-
'or '.colorize(:default) +
|
|
213
|
-
'`>` '.colorize(:red) +
|
|
214
|
-
'as "placeholders", and use something that doesn\'t confuse ' \
|
|
215
|
-
'the XML parser, such as : ' +
|
|
216
|
-
"`$#{offending_tags[0]}`" .colorize(:light_green)
|
|
217
|
-
puts option_one
|
|
218
|
-
puts "\n"
|
|
219
|
-
puts option_two
|
|
219
|
+
false
|
|
220
220
|
end
|
|
221
221
|
end
|
|
222
222
|
HappyMapper::SupportedTypes.register DescriptionDetailsType
|
|
@@ -27,6 +27,11 @@ module HappyMapperTools
|
|
|
27
27
|
# Class Asset maps from the 'SI_DATA' from Checklist XML file using HappyMapper
|
|
28
28
|
class SiData
|
|
29
29
|
include HappyMapper
|
|
30
|
+
|
|
31
|
+
def initialize(name, data)
|
|
32
|
+
self.name = name
|
|
33
|
+
self.data = data
|
|
34
|
+
end
|
|
30
35
|
tag 'SI_DATA'
|
|
31
36
|
element :name, String, tag: 'SID_NAME'
|
|
32
37
|
element :data, String, tag: 'SID_DATA'
|
|
@@ -35,6 +40,11 @@ module HappyMapperTools
|
|
|
35
40
|
# Class Asset maps from the 'STIG_INFO' from Checklist XML file using HappyMapper
|
|
36
41
|
class StigInfo
|
|
37
42
|
include HappyMapper
|
|
43
|
+
|
|
44
|
+
def initialize(si_data)
|
|
45
|
+
self.si_data = si_data
|
|
46
|
+
end
|
|
47
|
+
|
|
38
48
|
tag 'STIG_INFO'
|
|
39
49
|
has_many :si_data, SiData, tag: 'SI_DATA'
|
|
40
50
|
end
|
|
@@ -68,6 +78,11 @@ module HappyMapperTools
|
|
|
68
78
|
# Class Asset maps from the 'iSTIG' from Checklist XML file using HappyMapper
|
|
69
79
|
class IStig
|
|
70
80
|
include HappyMapper
|
|
81
|
+
|
|
82
|
+
def initialize(stig_info, vulns)
|
|
83
|
+
self.stig_info = stig_info
|
|
84
|
+
self.vuln = vulns
|
|
85
|
+
end
|
|
71
86
|
tag 'iSTIG'
|
|
72
87
|
has_one :stig_info, StigInfo, tag: 'STIG_INFO'
|
|
73
88
|
has_many :vuln, Vuln, tag: 'VULN'
|
|
@@ -76,6 +91,10 @@ module HappyMapperTools
|
|
|
76
91
|
# Class Asset maps from the 'STIGS' from Checklist XML file using HappyMapper
|
|
77
92
|
class Stigs
|
|
78
93
|
include HappyMapper
|
|
94
|
+
|
|
95
|
+
def initialize(istig)
|
|
96
|
+
self.istig = istig
|
|
97
|
+
end
|
|
79
98
|
tag 'STIGS'
|
|
80
99
|
has_one :istig, IStig, tag: 'iSTIG'
|
|
81
100
|
end
|
data/lib/inspec_tools/cli.rb
CHANGED
data/lib/inspec_tools/csv.rb
CHANGED
|
@@ -6,10 +6,6 @@ require_relative '../utilities/inspec_util'
|
|
|
6
6
|
require_relative '../utilities/cci_xml'
|
|
7
7
|
require_relative '../utilities/mapping_validator'
|
|
8
8
|
|
|
9
|
-
# rubocop:disable Metrics/AbcSize
|
|
10
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
|
11
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
|
12
|
-
|
|
13
9
|
module InspecTools
|
|
14
10
|
# Methods for converting from CSV to various formats
|
|
15
11
|
class CSVTool
|
|
@@ -46,8 +42,8 @@ module InspecTools
|
|
|
46
42
|
@profile['supports'] = []
|
|
47
43
|
@profile['attributes'] = []
|
|
48
44
|
@profile['generator'] = {
|
|
49
|
-
|
|
50
|
-
|
|
45
|
+
name: 'inspec_tools',
|
|
46
|
+
version: VERSION
|
|
51
47
|
}
|
|
52
48
|
end
|
|
53
49
|
|
|
@@ -98,7 +94,3 @@ module InspecTools
|
|
|
98
94
|
end
|
|
99
95
|
end
|
|
100
96
|
end
|
|
101
|
-
|
|
102
|
-
# rubocop:enable Metrics/AbcSize
|
|
103
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
|
104
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
|
@@ -14,21 +14,21 @@ module InspecTools
|
|
|
14
14
|
|
|
15
15
|
def default_text
|
|
16
16
|
<<~YML
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
# Setting csv_header to true will skip the csv file header
|
|
18
|
+
skip_csv_header: true
|
|
19
|
+
width : 80
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
32
|
YML
|
|
33
33
|
end
|
|
34
34
|
end
|
data/lib/inspec_tools/help.rb
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
module InspecTools
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
module InspecTools
|
|
2
|
+
module Help
|
|
3
|
+
class << self
|
|
4
|
+
def text(namespaced_command)
|
|
5
|
+
path = namespaced_command.to_s.tr(':', '/')
|
|
6
|
+
path = File.expand_path("../help/#{path}.md", __FILE__)
|
|
7
|
+
IO.read(path) if File.exist?(path)
|
|
8
|
+
end
|
|
7
9
|
end
|
|
8
10
|
end
|
|
9
11
|
end
|
data/lib/inspec_tools/inspec.rb
CHANGED
|
@@ -13,7 +13,7 @@ require_relative '../utilities/xccdf/from_inspec'
|
|
|
13
13
|
require_relative '../utilities/xccdf/to_xccdf'
|
|
14
14
|
|
|
15
15
|
module InspecTools
|
|
16
|
-
class Inspec
|
|
16
|
+
class Inspec
|
|
17
17
|
def initialize(inspec_json, metadata = {})
|
|
18
18
|
@json = JSON.parse(inspec_json.gsub(/\\+u0000/, ''))
|
|
19
19
|
@metadata = metadata
|
|
@@ -61,12 +61,31 @@ module InspecTools
|
|
|
61
61
|
|
|
62
62
|
private
|
|
63
63
|
|
|
64
|
+
def topmost_profile_name
|
|
65
|
+
find_topmost_profile_name(0)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def find_topmost_profile_name(index, parent_name = nil)
|
|
69
|
+
# Return nil when the index is out of bounds.
|
|
70
|
+
# nil returned here will set the profile name to '' in the calling functions.
|
|
71
|
+
return nil if index > @json['profiles'].length - 1
|
|
72
|
+
|
|
73
|
+
# No parent profile means this is the parent
|
|
74
|
+
if !@json['profiles'][index].key?('parent_profile') && (@json['profiles'][index]['name'] == parent_name || index.zero?)
|
|
75
|
+
# For the initial case, parent_name will be nil, and if we are already at the parent index is also zero
|
|
76
|
+
return @json['profiles'][index]['name']
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
parent_name = @json['profiles'][index]['parent_profile']
|
|
80
|
+
find_topmost_profile_name(index + 1, parent_name)
|
|
81
|
+
end
|
|
82
|
+
|
|
64
83
|
###
|
|
65
84
|
# This method converts an inspec json to an array of arrays
|
|
66
85
|
#
|
|
67
86
|
# @param inspec_json : an inspec profile formatted as a json object
|
|
68
87
|
###
|
|
69
|
-
def inspec_json_to_array(inspec_json)
|
|
88
|
+
def inspec_json_to_array(inspec_json)
|
|
70
89
|
data = []
|
|
71
90
|
headers = {}
|
|
72
91
|
inspec_json['controls'].each do |control|
|
|
@@ -111,28 +130,19 @@ module InspecTools
|
|
|
111
130
|
end
|
|
112
131
|
|
|
113
132
|
def generate_ckl
|
|
114
|
-
stigs = HappyMapperTools::StigChecklist::Stigs.new
|
|
115
|
-
istig = HappyMapperTools::StigChecklist::IStig.new
|
|
116
|
-
|
|
117
133
|
vuln_list = []
|
|
118
134
|
@data.keys.each do |control_id|
|
|
119
135
|
vuln_list.push(generate_vuln_data(@data[control_id]))
|
|
120
136
|
end
|
|
121
137
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
if !@metadata['stigid'].nil?
|
|
126
|
-
si_data.data = @metadata['stigid']
|
|
127
|
-
end
|
|
138
|
+
si_data_data = @metadata['stigid'] || topmost_profile_name || ''
|
|
139
|
+
si_data_stigid = HappyMapperTools::StigChecklist::SiData.new('stigid', si_data_data)
|
|
140
|
+
si_data_title = HappyMapperTools::StigChecklist::SiData.new('title', si_data_data)
|
|
128
141
|
|
|
129
|
-
stig_info = HappyMapperTools::StigChecklist::StigInfo.new
|
|
130
|
-
stig_info.si_data = si_data
|
|
131
|
-
istig.stig_info = stig_info
|
|
142
|
+
stig_info = HappyMapperTools::StigChecklist::StigInfo.new([si_data_stigid, si_data_title])
|
|
132
143
|
|
|
133
|
-
istig
|
|
134
|
-
|
|
135
|
-
@checklist.stig = stigs
|
|
144
|
+
istig = HappyMapperTools::StigChecklist::IStig.new(stig_info, vuln_list)
|
|
145
|
+
@checklist.stig = HappyMapperTools::StigChecklist::Stigs.new(istig)
|
|
136
146
|
|
|
137
147
|
@checklist.asset = generate_asset
|
|
138
148
|
end
|
|
@@ -158,19 +168,19 @@ module InspecTools
|
|
|
158
168
|
vuln
|
|
159
169
|
end
|
|
160
170
|
|
|
161
|
-
def generate_asset
|
|
171
|
+
def generate_asset
|
|
162
172
|
asset = HappyMapperTools::StigChecklist::Asset.new
|
|
163
|
-
asset.role =
|
|
164
|
-
asset.type =
|
|
173
|
+
asset.role = @metadata['role'].nil? ? 'Workstation' : @metadata['role']
|
|
174
|
+
asset.type = @metadata['type'].nil? ? 'Computing' : @metadata['type']
|
|
165
175
|
asset.host_name = generate_hostname
|
|
166
176
|
asset.host_ip = generate_ip
|
|
167
177
|
asset.host_mac = generate_mac
|
|
168
178
|
asset.host_fqdn = generate_fqdn
|
|
169
|
-
asset.tech_area =
|
|
170
|
-
asset.target_key =
|
|
171
|
-
asset.web_or_database =
|
|
172
|
-
asset.web_db_site =
|
|
173
|
-
asset.web_db_instance =
|
|
179
|
+
asset.tech_area = @metadata['tech_area'].nil? ? '' : @metadata['tech_area']
|
|
180
|
+
asset.target_key = @metadata['target_key'].nil? ? '' : @metadata['target_key']
|
|
181
|
+
asset.web_or_database = @metadata['web_or_database'].nil? ? '0' : @metadata['web_or_database']
|
|
182
|
+
asset.web_db_site = @metadata['web_db_site'].nil? ? '' : @metadata['web_db_site']
|
|
183
|
+
asset.web_db_instance = @metadata['web_db_instance'].nil? ? '' : @metadata['web_db_instance']
|
|
174
184
|
asset
|
|
175
185
|
end
|
|
176
186
|
|
data/lib/inspec_tools/pdf.rb
CHANGED
|
@@ -6,10 +6,6 @@ require_relative '../utilities/parser'
|
|
|
6
6
|
require_relative '../utilities/text_cleaner'
|
|
7
7
|
require_relative '../utilities/cis_to_nist'
|
|
8
8
|
|
|
9
|
-
# rubocop:disable Metrics/AbcSize
|
|
10
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
|
11
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
|
12
|
-
|
|
13
9
|
module InspecTools
|
|
14
10
|
class PDF
|
|
15
11
|
def initialize(pdf, profile_name, debug = false)
|
|
@@ -59,7 +55,7 @@ module InspecTools
|
|
|
59
55
|
@transformed_data.each do |contr|
|
|
60
56
|
nist = find_nist(contr[:cis]) unless contr[:cis] == 'No CIS Control'
|
|
61
57
|
control = {}
|
|
62
|
-
control['id'] =
|
|
58
|
+
control['id'] = "M-#{contr[:title].split[0]}"
|
|
63
59
|
control['title'] = contr[:title]
|
|
64
60
|
control['desc'] = contr[:descr]
|
|
65
61
|
control['impact'] = Utils::InspecUtil.get_impact('medium')
|
|
@@ -67,7 +63,7 @@ module InspecTools
|
|
|
67
63
|
control['tags']['severity'] = Utils::InspecUtil.get_impact_string(control['impact'])
|
|
68
64
|
control['tags']['ref'] = contr[:ref] unless contr[:ref].nil?
|
|
69
65
|
control['tags']['applicability'] = contr[:applicability] unless contr[:applicability].nil?
|
|
70
|
-
control['tags']['cis_id'] = contr[:title].split
|
|
66
|
+
control['tags']['cis_id'] = contr[:title].split[0] unless contr[:title].nil?
|
|
71
67
|
control['tags']['cis_control'] = [contr[:cis], @nist_mapping[0][:cis_ver]] unless contr[:cis].nil? # tag cis_control: [5, 6.1] ##6.1 is the version
|
|
72
68
|
control['tags']['cis_level'] = contr[:level] unless contr[:level].nil?
|
|
73
69
|
control['tags']['nist'] = nist unless nist.nil? # tag nist: [AC-3, 4] ##4 is the version
|
|
@@ -91,8 +87,8 @@ module InspecTools
|
|
|
91
87
|
@profile['supports'] = []
|
|
92
88
|
@profile['attributes'] = []
|
|
93
89
|
@profile['generator'] = {
|
|
94
|
-
|
|
95
|
-
|
|
90
|
+
name: 'inspec_tools',
|
|
91
|
+
version: VERSION
|
|
96
92
|
}
|
|
97
93
|
end
|
|
98
94
|
|
|
@@ -16,10 +16,9 @@ module InspecTools
|
|
|
16
16
|
autoload :GenerateMap, 'inspec_tools/generate_map'
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
# rubocop:disable Style/GuardClause
|
|
20
19
|
module InspecPlugins
|
|
21
20
|
module InspecToolsPlugin
|
|
22
|
-
class CliCommand < Inspec.plugin(2, :cli_command)
|
|
21
|
+
class CliCommand < Inspec.plugin(2, :cli_command)
|
|
23
22
|
POSSIBLE_LOG_LEVELS = %w{debug info warn error fatal}.freeze
|
|
24
23
|
|
|
25
24
|
class_option :log_directory, type: :string, aliases: :l, desc: 'Provide log location'
|
|
@@ -36,8 +35,9 @@ module InspecPlugins
|
|
|
36
35
|
option :separate_files, required: false, type: :boolean, default: true, aliases: '-s'
|
|
37
36
|
option :replace_tags, type: :array, required: false, aliases: '-r'
|
|
38
37
|
option :metadata, required: false, aliases: '-m'
|
|
38
|
+
option :control_id, required: false, enum: %w{ruleID vulnID}, aliases: '-c', default: 'vulnID'
|
|
39
39
|
def xccdf2inspec
|
|
40
|
-
xccdf = InspecTools::XCCDF.new(File.read(options[:xccdf]), options[:replace_tags])
|
|
40
|
+
xccdf = InspecTools::XCCDF.new(File.read(options[:xccdf]), options[:control_id] == 'vulnID', options[:replace_tags])
|
|
41
41
|
profile = xccdf.to_inspec
|
|
42
42
|
|
|
43
43
|
if !options[:metadata].nil?
|
|
@@ -240,5 +240,3 @@ if help_commands.any? { |cmd| ARGV.include? cmd }
|
|
|
240
240
|
end
|
|
241
241
|
end
|
|
242
242
|
end
|
|
243
|
-
|
|
244
|
-
# rubocop:enable Style/GuardClause
|
data/lib/inspec_tools/summary.rb
CHANGED
|
@@ -14,15 +14,8 @@ TALLYS = %i(total critical high medium low).freeze
|
|
|
14
14
|
THRESHOLD_TEMPLATE = File.expand_path('../data/threshold.yaml', File.dirname(__FILE__))
|
|
15
15
|
|
|
16
16
|
module InspecTools
|
|
17
|
-
# rubocop:disable Metrics/ClassLength
|
|
18
17
|
class Summary
|
|
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
|
|
18
|
+
attr_reader :json, :json_full, :json_counts, :threshold_file, :threshold_inline, :summary, :threshold
|
|
26
19
|
|
|
27
20
|
def initialize(**options)
|
|
28
21
|
options = options[:options]
|
|
@@ -154,5 +147,4 @@ module InspecTools
|
|
|
154
147
|
summary[:status][:error][:total])).floor
|
|
155
148
|
end
|
|
156
149
|
end
|
|
157
|
-
# rubocop:enable Metrics/ClassLength
|
|
158
150
|
end
|
data/lib/inspec_tools/xccdf.rb
CHANGED
|
@@ -6,19 +6,15 @@ require 'digest'
|
|
|
6
6
|
require 'json'
|
|
7
7
|
|
|
8
8
|
module InspecTools
|
|
9
|
-
# rubocop:disable Metrics/ClassLength
|
|
10
|
-
# rubocop:disable Metrics/AbcSize
|
|
11
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
|
12
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
|
13
|
-
# rubocop:disable Metrics/BlockLength
|
|
14
9
|
class XCCDF
|
|
15
|
-
def initialize(xccdf, replace_tags = nil)
|
|
10
|
+
def initialize(xccdf, use_vuln_id, replace_tags = nil)
|
|
16
11
|
@xccdf = xccdf
|
|
17
12
|
@xccdf = replace_tags_in_xccdf(replace_tags, @xccdf) unless replace_tags.nil?
|
|
18
13
|
cci_list_path = File.join(File.dirname(__FILE__), '../data/U_CCI_List.xml')
|
|
19
14
|
@cci_items = HappyMapperTools::CCIAttributes::CCI_List.parse(File.read(cci_list_path))
|
|
20
15
|
register_after_parse_callbacks
|
|
21
16
|
@benchmark = HappyMapperTools::StigAttributes::Benchmark.parse(@xccdf)
|
|
17
|
+
@use_vuln_id = use_vuln_id
|
|
22
18
|
end
|
|
23
19
|
|
|
24
20
|
def to_ckl
|
|
@@ -42,7 +38,7 @@ module InspecTools
|
|
|
42
38
|
# extracts non-InSpec attributes
|
|
43
39
|
###
|
|
44
40
|
# TODO there may be more attributes we want to extract, see data/attributes.yml for example
|
|
45
|
-
def to_attributes
|
|
41
|
+
def to_attributes
|
|
46
42
|
@attribute = {}
|
|
47
43
|
|
|
48
44
|
@attribute['benchmark.title'] = @benchmark.title
|
|
@@ -116,8 +112,8 @@ module InspecTools
|
|
|
116
112
|
@profile['supports'] = []
|
|
117
113
|
@profile['attributes'] = []
|
|
118
114
|
@profile['generator'] = {
|
|
119
|
-
|
|
120
|
-
|
|
115
|
+
name: 'inspec_tools',
|
|
116
|
+
version: VERSION
|
|
121
117
|
}
|
|
122
118
|
@profile['plaintext'] = @benchmark.plaintext.plaintext
|
|
123
119
|
@profile['status'] = "#{@benchmark.status} on #{@benchmark.release_date.release_date}"
|
|
@@ -129,7 +125,7 @@ module InspecTools
|
|
|
129
125
|
def insert_controls
|
|
130
126
|
@benchmark.group.each do |group|
|
|
131
127
|
control = {}
|
|
132
|
-
control['id'] = group.id
|
|
128
|
+
control['id'] = @use_vuln_id ? group.id : group.rule.id.split('r').first
|
|
133
129
|
control['title'] = group.rule.title
|
|
134
130
|
control['desc'] = group.rule.description.vuln_discussion.split('Satisfies: ')[0]
|
|
135
131
|
control['impact'] = Utils::InspecUtil.get_impact(group.rule.severity)
|
|
@@ -141,8 +137,9 @@ module InspecTools
|
|
|
141
137
|
control['tags']['rid'] = group.rule.id
|
|
142
138
|
control['tags']['stig_id'] = group.rule.version
|
|
143
139
|
control['tags']['fix_id'] = group.rule.fix.id
|
|
144
|
-
control['tags']['cci'] = group.rule.idents.select
|
|
145
|
-
|
|
140
|
+
control['tags']['cci'] = group.rule.idents.select(&:cci).map(&:ident)
|
|
141
|
+
legacy_tags = group.rule.idents.select(&:legacy)
|
|
142
|
+
control['tags']['legacy'] = legacy_tags.map(&:ident) unless legacy_tags.empty?
|
|
146
143
|
control['tags']['nist'] = @cci_items.fetch_nists(control['tags']['cci'])
|
|
147
144
|
control['tags']['false_negatives'] = group.rule.description.false_negatives if group.rule.description.false_negatives != ''
|
|
148
145
|
control['tags']['false_positives'] = group.rule.description.false_positives if group.rule.description.false_positives != ''
|
|
@@ -8,10 +8,6 @@ require_relative '../utilities/inspec_util'
|
|
|
8
8
|
require_relative '../utilities/cis_to_nist'
|
|
9
9
|
require_relative '../utilities/mapping_validator'
|
|
10
10
|
|
|
11
|
-
# rubocop:disable Metrics/AbcSize
|
|
12
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
|
13
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
|
14
|
-
|
|
15
11
|
module InspecTools
|
|
16
12
|
# Methods for converting from XLS to various formats
|
|
17
13
|
class XLSXTool
|
|
@@ -58,8 +54,8 @@ module InspecTools
|
|
|
58
54
|
@profile['supports'] = []
|
|
59
55
|
@profile['attributes'] = []
|
|
60
56
|
@profile['generator'] = {
|
|
61
|
-
|
|
62
|
-
|
|
57
|
+
name: 'inspec_tools',
|
|
58
|
+
version: ::InspecTools::VERSION
|
|
63
59
|
}
|
|
64
60
|
end
|
|
65
61
|
|
|
@@ -73,7 +69,7 @@ module InspecTools
|
|
|
73
69
|
tag_pos = @mapping['control.tags']
|
|
74
70
|
control = {}
|
|
75
71
|
control['tags'] = {}
|
|
76
|
-
control['id'] = control_prefix
|
|
72
|
+
control['id'] = "#{control_prefix}-#{row[@mapping['control.id']].formatted_value}" unless cell_empty?(@mapping['control.id']) || cell_empty?(row[@mapping['control.id']])
|
|
77
73
|
control['title'] = row[@mapping['control.title']].formatted_value unless cell_empty?(@mapping['control.title']) || cell_empty?(row[@mapping['control.title']])
|
|
78
74
|
control['desc'] = ''
|
|
79
75
|
control['desc'] = row[@mapping['control.desc']].formatted_value unless cell_empty?(row[@mapping['control.desc']])
|
|
@@ -107,7 +103,8 @@ module InspecTools
|
|
|
107
103
|
end
|
|
108
104
|
|
|
109
105
|
def apply_cis_and_nist_controls(control, cis_tags)
|
|
110
|
-
control['tags']['cis_controls']
|
|
106
|
+
control['tags']['cis_controls'] = []
|
|
107
|
+
control['tags']['nist'] = []
|
|
111
108
|
|
|
112
109
|
if cis_tags[:sub_section].nil? || cis_tags[:sub_section].blank?
|
|
113
110
|
control['tags']['cis_controls'] << cis_tags[:section]
|
|
@@ -130,7 +127,3 @@ module InspecTools
|
|
|
130
127
|
end
|
|
131
128
|
end
|
|
132
129
|
end
|
|
133
|
-
|
|
134
|
-
# rubocop:enable Metrics/AbcSize
|
|
135
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
|
136
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
data/lib/overrides/string.rb
CHANGED
|
@@ -14,7 +14,7 @@ require 'overrides/string'
|
|
|
14
14
|
require 'rubocop'
|
|
15
15
|
|
|
16
16
|
module Utils
|
|
17
|
-
class InspecUtil
|
|
17
|
+
class InspecUtil
|
|
18
18
|
WIDTH = 80
|
|
19
19
|
IMPACT_SCORES = {
|
|
20
20
|
'none' => 0.0,
|
|
@@ -24,7 +24,7 @@ module Utils
|
|
|
24
24
|
'critical' => 0.9
|
|
25
25
|
}.freeze
|
|
26
26
|
|
|
27
|
-
def self.parse_data_for_ckl(json)
|
|
27
|
+
def self.parse_data_for_ckl(json)
|
|
28
28
|
data = {}
|
|
29
29
|
|
|
30
30
|
# Parse for inspec profile results json
|
|
@@ -88,7 +88,7 @@ module Utils
|
|
|
88
88
|
hash.each_with_object({}) do |(k, v), ret|
|
|
89
89
|
key = recursive_key + k.to_s
|
|
90
90
|
if v.is_a? Hash
|
|
91
|
-
ret.merge! to_dotted_hash(v, key
|
|
91
|
+
ret.merge! to_dotted_hash(v, "#{key}.")
|
|
92
92
|
else
|
|
93
93
|
ret[key] = v
|
|
94
94
|
end
|
|
@@ -165,15 +165,16 @@ module Utils
|
|
|
165
165
|
end
|
|
166
166
|
|
|
167
167
|
private_class_method def self.string_to_impact(severity, use_cvss_terms)
|
|
168
|
-
|
|
168
|
+
case severity
|
|
169
|
+
when %r{none|na|n/a|not[_|(\s*)]?applicable}i
|
|
169
170
|
impact = 0.0 # Informative
|
|
170
|
-
|
|
171
|
+
when /low|cat(egory)?\s*(iii|3)/i
|
|
171
172
|
impact = 0.3 # Low Impact
|
|
172
|
-
|
|
173
|
+
when /med(ium)?|cat(egory)?\s*(ii|2)/i
|
|
173
174
|
impact = 0.5 # Medium Impact
|
|
174
|
-
|
|
175
|
+
when /high|cat(egory)?\s*(i|1)/i
|
|
175
176
|
impact = 0.7 # High Impact
|
|
176
|
-
|
|
177
|
+
when /crit(ical)?|severe/i
|
|
177
178
|
impact = 1.0 # Critical Controls
|
|
178
179
|
else
|
|
179
180
|
raise SeverityInputError, "'#{severity}' is not a valid severity value. It should be a Float between 0.0 and " \
|
|
@@ -218,7 +219,7 @@ module Utils
|
|
|
218
219
|
WordWrap.ww(str.to_s, width)
|
|
219
220
|
end
|
|
220
221
|
|
|
221
|
-
private_class_method def self.generate_controls(inspec_json)
|
|
222
|
+
private_class_method def self.generate_controls(inspec_json)
|
|
222
223
|
controls = []
|
|
223
224
|
inspec_json['controls'].each do |json_control|
|
|
224
225
|
control = ::Inspec::Object::Control.new
|
|
@@ -246,7 +247,7 @@ module Utils
|
|
|
246
247
|
control.add_tag(::Inspec::Object::Tag.new('stig_id', json_control['tags']['stig_id']))
|
|
247
248
|
control.add_tag(::Inspec::Object::Tag.new('fix_id', json_control['tags']['fix_id']))
|
|
248
249
|
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']))
|
|
250
|
+
control.add_tag(::Inspec::Object::Tag.new('legacy', json_control['tags']['legacy'])) unless json_control['tags']['legacy'].blank?
|
|
250
251
|
control.add_tag(::Inspec::Object::Tag.new('nist', json_control['tags']['nist']))
|
|
251
252
|
control.add_tag(::Inspec::Object::Tag.new('cis_level', json_control['tags']['cis_level'])) unless json_control['tags']['cis_level'].blank?
|
|
252
253
|
control.add_tag(::Inspec::Object::Tag.new('cis_controls', json_control['tags']['cis_controls'])) unless json_control['tags']['cis_controls'].blank?
|
|
@@ -326,7 +327,7 @@ module Utils
|
|
|
326
327
|
myfile.puts readme_contents
|
|
327
328
|
end
|
|
328
329
|
|
|
329
|
-
private_class_method def self.unpack_profile(directory, controls, separated, output_format)
|
|
330
|
+
private_class_method def self.unpack_profile(directory, controls, separated, output_format)
|
|
330
331
|
FileUtils.rm_rf(directory) if Dir.exist?(directory)
|
|
331
332
|
Dir.mkdir directory unless Dir.exist?(directory)
|
|
332
333
|
Dir.mkdir "#{directory}/controls" unless Dir.exist?("#{directory}/controls")
|
|
@@ -337,7 +338,7 @@ module Utils
|
|
|
337
338
|
file_name = control.id.to_s
|
|
338
339
|
myfile = File.new("#{directory}/controls/#{file_name}.rb", 'w')
|
|
339
340
|
myfile.puts "# encoding: UTF-8\n\n"
|
|
340
|
-
myfile.puts wrap(control.to_ruby, WIDTH)
|
|
341
|
+
myfile.puts "#{wrap(control.to_ruby, WIDTH)}\n"
|
|
341
342
|
myfile.close
|
|
342
343
|
end
|
|
343
344
|
else
|
|
@@ -353,7 +354,7 @@ module Utils
|
|
|
353
354
|
if output_format == 'ruby'
|
|
354
355
|
controls.each do |control|
|
|
355
356
|
myfile.puts "# encoding: UTF-8\n\n"
|
|
356
|
-
myfile.puts wrap(control.to_ruby.gsub('"', "\'"), WIDTH)
|
|
357
|
+
myfile.puts "#{wrap(control.to_ruby.gsub('"', "\'"), WIDTH)}\n"
|
|
357
358
|
end
|
|
358
359
|
else
|
|
359
360
|
controls.each do |control|
|
data/lib/utilities/parser.rb
CHANGED
|
@@ -4,7 +4,6 @@ require 'parslet'
|
|
|
4
4
|
require 'parslet/convenience'
|
|
5
5
|
require 'pp'
|
|
6
6
|
|
|
7
|
-
# rubocop:disable Metrics/ClassLength
|
|
8
7
|
module Util
|
|
9
8
|
class ControlParser < Parslet::Parser
|
|
10
9
|
root :controls
|
|
@@ -345,8 +344,8 @@ module Util
|
|
|
345
344
|
|
|
346
345
|
def parse(clean_text)
|
|
347
346
|
@parser.parse(clean_text)
|
|
348
|
-
rescue Parslet::ParseFailed =>
|
|
349
|
-
puts
|
|
347
|
+
rescue Parslet::ParseFailed => e
|
|
348
|
+
puts e.parse_failure_cause.ascii_tree
|
|
350
349
|
end
|
|
351
350
|
|
|
352
351
|
def convert_str(value)
|
|
@@ -359,13 +358,13 @@ module Util
|
|
|
359
358
|
references = ctrl[:ref].split("\n")
|
|
360
359
|
references.each do |ref|
|
|
361
360
|
match = ref.scan(/(?<=#)\d+\.\d+/).map(&:inspect).join(',').delete('"').tr(',', ' ')
|
|
362
|
-
ctrl[:cis] = match.split
|
|
361
|
+
ctrl[:cis] = match.split unless match.empty?
|
|
363
362
|
end
|
|
364
363
|
ctrl[:cis] = 'No CIS Control' unless ctrl[:cis]
|
|
365
364
|
elsif !ctrl[:cis] && !ctrl[:ref]
|
|
366
365
|
ctrl[:cis] = 'No CIS Control'
|
|
367
366
|
elsif ctrl[:cis] && ctrl[:ref]
|
|
368
|
-
ctrl[:cis] = ctrl[:cis].scan(/^\d+[
|
|
367
|
+
ctrl[:cis] = ctrl[:cis].scan(/^\d+[.\d+]*/)
|
|
369
368
|
end
|
|
370
369
|
end
|
|
371
370
|
end
|
|
@@ -15,36 +15,31 @@ module Util
|
|
|
15
15
|
clean_special = remove_special(clean_whitespace)
|
|
16
16
|
clean_no_space = remove_extra_space(clean_special)
|
|
17
17
|
clean_pagenum = remove_pagenum(clean_no_space)
|
|
18
|
-
|
|
19
|
-
clean_data
|
|
18
|
+
separate_controls(clean_pagenum)
|
|
20
19
|
end
|
|
21
20
|
|
|
22
21
|
# Removes everything before and after the controls
|
|
23
22
|
def isolate_controls_data(extracted_data)
|
|
24
23
|
extracted_data = extracted_data.gsub(/\| P a g e+/, "| P a g e\n")
|
|
25
|
-
extracted_data = extracted_data.split("\n").map
|
|
24
|
+
extracted_data = extracted_data.split("\n").map(&:strip).reject { |e| e.to_s.empty? }.join("\n")
|
|
26
25
|
extracted_data = extracted_data.gsub('???', '')
|
|
27
|
-
|
|
28
|
-
controls_data
|
|
26
|
+
/^1\.1\s*[^)]*?(?=\)$)(.*\n)*?(?=\s*Appendix:)/.match(extracted_data).to_s
|
|
29
27
|
end
|
|
30
28
|
|
|
31
29
|
# Removes all pagenumbers between the controls
|
|
32
30
|
def remove_pagenum(extracted_data)
|
|
33
31
|
clean_pagenum = extracted_data.gsub(/(\d{1,3}\|Page|\d{1,3} \| P a g e)/, '').to_s
|
|
34
|
-
clean_pagenum
|
|
35
|
-
clean_pagenum
|
|
32
|
+
clean_pagenum.gsub(/(\d{1,3} \| Page)/, '').to_s
|
|
36
33
|
end
|
|
37
34
|
|
|
38
35
|
# Removes section headers for each control
|
|
39
36
|
def remove_section_header(extracted_data)
|
|
40
|
-
|
|
41
|
-
clean_section_header
|
|
37
|
+
extracted_data.gsub(/(?<!•)\s\n\d{1}\s.*(?:.*\n)*?(?=\d\.\d)/, "\n\n").to_s
|
|
42
38
|
end
|
|
43
39
|
|
|
44
40
|
# removes newlines between a control
|
|
45
41
|
def remove_newline_in_controls(extracted_data)
|
|
46
|
-
|
|
47
|
-
clean_whitespace
|
|
42
|
+
extracted_data.gsub(/\s\n.*?(?!d\.)/, "\n").to_s
|
|
48
43
|
end
|
|
49
44
|
|
|
50
45
|
# adds whitespace between different controls
|
|
@@ -53,8 +48,8 @@ module Util
|
|
|
53
48
|
end
|
|
54
49
|
|
|
55
50
|
def remove_special(extracted_data)
|
|
56
|
-
extracted_data = extracted_data.gsub(
|
|
57
|
-
extracted_data.gsub(
|
|
51
|
+
extracted_data = extracted_data.gsub(//, '')
|
|
52
|
+
extracted_data.gsub(/•/, '')
|
|
58
53
|
end
|
|
59
54
|
|
|
60
55
|
def remove_extra_space(extracted_data)
|
|
@@ -4,7 +4,7 @@ module Utils
|
|
|
4
4
|
DATA_NOT_FOUND_MESSAGE = 'N/A'.freeze
|
|
5
5
|
|
|
6
6
|
# Convert raw Inspec result json into format acceptable for XCCDF transformation.
|
|
7
|
-
def parse_data_for_xccdf(json)
|
|
7
|
+
def parse_data_for_xccdf(json)
|
|
8
8
|
data = {}
|
|
9
9
|
|
|
10
10
|
controls = []
|
|
@@ -29,7 +29,7 @@ module Utils
|
|
|
29
29
|
c_data[c_id]['gid'] = control['tags']['gid'] || control['id']
|
|
30
30
|
c_data[c_id]['gtitle'] = control['tags']['gtitle'] if control['tags']['gtitle'] # Optional attribute
|
|
31
31
|
c_data[c_id]['gdescription'] = control['tags']['gdescription'] if control['tags']['gdescription'] # Optional attribute
|
|
32
|
-
c_data[c_id]['rid'] = control['tags']['rid'] ||
|
|
32
|
+
c_data[c_id]['rid'] = control['tags']['rid'] || "r_#{c_data[c_id]['gid']}"
|
|
33
33
|
c_data[c_id]['rversion'] = control['tags']['rversion'] if control['tags']['rversion'] # Optional attribute
|
|
34
34
|
c_data[c_id]['rweight'] = control['tags']['rweight'] if control['tags']['rweight'] # Optional attribute where N/A is not schema compliant
|
|
35
35
|
c_data[c_id]['stig_id'] = control['tags']['stig_id'] || DATA_NOT_FOUND_MESSAGE
|
|
@@ -2,7 +2,7 @@ require_relative 'xccdf_score'
|
|
|
2
2
|
|
|
3
3
|
module Utils
|
|
4
4
|
# Data conversions for Inspec output into XCCDF format.
|
|
5
|
-
class ToXCCDF
|
|
5
|
+
class ToXCCDF
|
|
6
6
|
# @param attribute [Hash] XCCDF supplemental attributes
|
|
7
7
|
# @param data [Hash] Converted Inspec output data
|
|
8
8
|
def initialize(attribute, data)
|
|
@@ -53,7 +53,7 @@ module Utils
|
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
# Translate join of Inspec results and input attributes to XCCDF Groups
|
|
56
|
-
def build_groups
|
|
56
|
+
def build_groups
|
|
57
57
|
group_array = []
|
|
58
58
|
@data['controls'].each do |control|
|
|
59
59
|
group = HappyMapperTools::Benchmark::Group.new
|
|
@@ -67,7 +67,7 @@ module Utils
|
|
|
67
67
|
group.rule.weight = control['rweight']
|
|
68
68
|
group.rule.version = control['rversion']
|
|
69
69
|
group.rule.title = control['title'].tr("\n", ' ') if control['title']
|
|
70
|
-
group.rule.description = "<VulnDiscussion>#{control['desc']
|
|
70
|
+
group.rule.description = "<VulnDiscussion>#{control['desc']}</VulnDiscussion><FalsePositives></FalsePositives><FalseNegatives></FalseNegatives><Documentable>false</Documentable><Mitigations></Mitigations><SeverityOverrideGuidance></SeverityOverrideGuidance><PotentialImpacts></PotentialImpacts><ThirdPartyTools></ThirdPartyTools><MitigationControl></MitigationControl><Responsibility></Responsibility><IAControls></IAControls>"
|
|
71
71
|
|
|
72
72
|
if ['reference.dc.publisher', 'reference.dc.title', 'reference.dc.subject', 'reference.dc.type', 'reference.dc.identifier'].any? { |a| @attribute.key?(a) }
|
|
73
73
|
group.rule.reference = build_rule_reference
|
|
@@ -127,7 +127,7 @@ module Utils
|
|
|
127
127
|
|
|
128
128
|
# Each rule identifier is a different element
|
|
129
129
|
idents.map do |identifier|
|
|
130
|
-
|
|
130
|
+
HappyMapperTools::Benchmark::Ident.new identifier
|
|
131
131
|
end
|
|
132
132
|
end
|
|
133
133
|
|
|
@@ -182,7 +182,7 @@ module Utils
|
|
|
182
182
|
|
|
183
183
|
# Build out the TestResult given all the control and result data.
|
|
184
184
|
def populate_results(test_result)
|
|
185
|
-
#
|
|
185
|
+
# NOTE: id is not an XCCDF 1.2 compliant identifier and will need to be updated when that support is added.
|
|
186
186
|
test_result.id = 'result_1'
|
|
187
187
|
test_result.starttime = run_start_time
|
|
188
188
|
test_result.endtime = run_end_time
|
|
@@ -237,7 +237,7 @@ module Utils
|
|
|
237
237
|
end
|
|
238
238
|
|
|
239
239
|
# Combines rule results with the same result into a single rule result.
|
|
240
|
-
def combine_results(rule_results)
|
|
240
|
+
def combine_results(rule_results)
|
|
241
241
|
return rule_results.first if rule_results.size == 1
|
|
242
242
|
|
|
243
243
|
# Can combine, result, idents (duplicate, take first instance), instance - combine into an array removing duplicates
|
|
@@ -328,7 +328,7 @@ module Utils
|
|
|
328
328
|
# @param one [String] A rule-result status
|
|
329
329
|
# @param two [String] A rule-result status
|
|
330
330
|
# @return The result of the AND operation.
|
|
331
|
-
def xccdf_and_result(one, two)
|
|
331
|
+
def xccdf_and_result(one, two)
|
|
332
332
|
# From XCCDF specification truth table
|
|
333
333
|
# P = pass
|
|
334
334
|
# F = fail
|
|
@@ -361,7 +361,7 @@ module Utils
|
|
|
361
361
|
|
|
362
362
|
message = HappyMapperTools::Benchmark::MessageType.new
|
|
363
363
|
# Including the code of the check and the resulting message if there is one.
|
|
364
|
-
message.message = "#{result['code_desc'] ? result['code_desc']
|
|
364
|
+
message.message = "#{result['code_desc'] ? "#{result['code_desc']}\n\n" : ''}#{result['message'] || result['skip_message']}"
|
|
365
365
|
message.severity = result_message_severity(xccdf_status)
|
|
366
366
|
message
|
|
367
367
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: inspec_tools
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.3.
|
|
4
|
+
version: 2.3.8
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Robert Thew
|
|
@@ -11,7 +11,7 @@ authors:
|
|
|
11
11
|
autorequire:
|
|
12
12
|
bindir: exe
|
|
13
13
|
cert_chain: []
|
|
14
|
-
date: 2021-
|
|
14
|
+
date: 2021-07-13 00:00:00.000000000 Z
|
|
15
15
|
dependencies:
|
|
16
16
|
- !ruby/object:Gem::Dependency
|
|
17
17
|
name: colorize
|
|
@@ -149,16 +149,16 @@ dependencies:
|
|
|
149
149
|
name: rubocop
|
|
150
150
|
requirement: !ruby/object:Gem::Requirement
|
|
151
151
|
requirements:
|
|
152
|
-
- - "
|
|
152
|
+
- - "~>"
|
|
153
153
|
- !ruby/object:Gem::Version
|
|
154
|
-
version: '
|
|
154
|
+
version: '1.11'
|
|
155
155
|
type: :runtime
|
|
156
156
|
prerelease: false
|
|
157
157
|
version_requirements: !ruby/object:Gem::Requirement
|
|
158
158
|
requirements:
|
|
159
|
-
- - "
|
|
159
|
+
- - "~>"
|
|
160
160
|
- !ruby/object:Gem::Version
|
|
161
|
-
version: '
|
|
161
|
+
version: '1.11'
|
|
162
162
|
- !ruby/object:Gem::Dependency
|
|
163
163
|
name: word_wrap
|
|
164
164
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -257,6 +257,20 @@ dependencies:
|
|
|
257
257
|
- - ">="
|
|
258
258
|
- !ruby/object:Gem::Version
|
|
259
259
|
version: '0'
|
|
260
|
+
- !ruby/object:Gem::Dependency
|
|
261
|
+
name: rubocop-minitest
|
|
262
|
+
requirement: !ruby/object:Gem::Requirement
|
|
263
|
+
requirements:
|
|
264
|
+
- - "~>"
|
|
265
|
+
- !ruby/object:Gem::Version
|
|
266
|
+
version: 0.12.1
|
|
267
|
+
type: :development
|
|
268
|
+
prerelease: false
|
|
269
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
270
|
+
requirements:
|
|
271
|
+
- - "~>"
|
|
272
|
+
- !ruby/object:Gem::Version
|
|
273
|
+
version: 0.12.1
|
|
260
274
|
- !ruby/object:Gem::Dependency
|
|
261
275
|
name: simplecov
|
|
262
276
|
requirement: !ruby/object:Gem::Requirement
|