inspec_tools 2.3.2 → 2.3.7
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 +19 -4
- data/lib/happy_mapper_tools/cci_attributes.rb +0 -4
- data/lib/happy_mapper_tools/stig_attributes.rb +12 -3
- 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 +19 -13
- data/lib/inspec_tools/xlsx_tool.rb +5 -12
- data/lib/overrides/string.rb +1 -1
- data/lib/utilities/inspec_util.rb +16 -14
- data/lib/utilities/parser.rb +4 -5
- data/lib/utilities/text_cleaner.rb +8 -13
- data/lib/utilities/xccdf/from_inspec.rb +3 -2
- data/lib/utilities/xccdf/to_xccdf.rb +10 -11
- metadata +21 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a9633cda4bf3fedc9702dd91ba4ed6b40b68ec8e054eb87ad6ca32d32dfc11e5
|
|
4
|
+
data.tar.gz: 22f5ab86d76ccbc7f7fc0a684371b791c26f9771e369be54c359121d8c28f4d3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0e50a5db32e542cd5a66b8bab09a8613c9b8b6ea62513d073a3eb71717397d5c54cf3ccea2c48dd3c0dbc21dbb5d0f9c550744bb8ed6f27f37335a88b0184ea8
|
|
7
|
+
data.tar.gz: af786d30584f7bf79108b44d9cb653e7673c2716ea7ce72f24fa1c494b4dfc7801f032aeca8829c23f5a1ed9ca8be542b5bf7f4d72fa315400a0b0d1349a3c64
|
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
|
@@ -65,6 +65,21 @@ module HappyMapperTools
|
|
|
65
65
|
tag 'ident'
|
|
66
66
|
attribute :system, String, tag: 'system'
|
|
67
67
|
content :ident, String
|
|
68
|
+
def initialize(ident_str)
|
|
69
|
+
@ident = ident_str
|
|
70
|
+
case ident_str
|
|
71
|
+
when /^(CCI-[0-9]{6})$/
|
|
72
|
+
# Match CCI IDs; e.g. CCI-123456
|
|
73
|
+
@system = 'http://cyber.mil/cci'
|
|
74
|
+
when /^(S?V-[0-9]{5})$/
|
|
75
|
+
# Match SV- IDs; e.g. SV-12345
|
|
76
|
+
# Match V- IDs; e.g. V-12345
|
|
77
|
+
@system = 'http://cyber.mil/legacy'
|
|
78
|
+
else
|
|
79
|
+
# for all other ident_str, use the old identifier
|
|
80
|
+
@system = 'https://public.cyber.mil/stigs/cci/'
|
|
81
|
+
end
|
|
82
|
+
end
|
|
68
83
|
end
|
|
69
84
|
|
|
70
85
|
# Class Fixtext maps from the 'fixtext' from Benchmark XML file using HappyMapper
|
|
@@ -114,7 +129,7 @@ module HappyMapperTools
|
|
|
114
129
|
element :result, String, tag: 'result'
|
|
115
130
|
# element override - Not implemented. Does not apply to Inspec execution
|
|
116
131
|
has_many :ident, Ident, tag: 'ident'
|
|
117
|
-
#
|
|
132
|
+
# NOTE: element metadata not implemented at this time
|
|
118
133
|
has_many :message, MessageType, tag: 'message'
|
|
119
134
|
has_many :instance, String, tag: 'instance'
|
|
120
135
|
element :fix, Fix, tag: 'fix'
|
|
@@ -161,7 +176,7 @@ module HappyMapperTools
|
|
|
161
176
|
|
|
162
177
|
class TestResult
|
|
163
178
|
include HappyMapper
|
|
164
|
-
#
|
|
179
|
+
# NOTE: element benchmark not implemented at this time since this is same file
|
|
165
180
|
# Note: element title not implemented due to no mapping from Chef Inspec
|
|
166
181
|
element :remark, String, tag: 'remark'
|
|
167
182
|
has_many :organization, String, tag: 'organization'
|
|
@@ -170,14 +185,14 @@ module HappyMapperTools
|
|
|
170
185
|
has_many :target_address, String, tag: 'target-address'
|
|
171
186
|
element :target_facts, TargetFact, tag: 'target-facts'
|
|
172
187
|
element :platform, CPE2idrefType, tag: 'platform'
|
|
173
|
-
#
|
|
188
|
+
# NOTE: element profile not implemented since Benchmark profile is also not implemented
|
|
174
189
|
has_many :rule_result, RuleResultType, tag: 'rule-result'
|
|
175
190
|
has_many :score, ScoreType, tag: 'score' # One minimum
|
|
176
191
|
# Note: element signature not implemented due to no mapping from Chef Inspec
|
|
177
192
|
attribute :id, String, tag: 'id'
|
|
178
193
|
attribute :starttime, String, tag: 'start-time'
|
|
179
194
|
attribute :endtime, String, tag: 'end-time'
|
|
180
|
-
#
|
|
195
|
+
# NOTE: attribute test-system not implemented at this time due to unknown CPE value for Chef Inspec
|
|
181
196
|
attribute :version, String, tag: 'version'
|
|
182
197
|
end
|
|
183
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
|
|
@@ -77,6 +77,15 @@ module HappyMapperTools
|
|
|
77
77
|
element :dc_identifier, String, tag: 'identifier', namespace: 'dc'
|
|
78
78
|
end
|
|
79
79
|
|
|
80
|
+
class Ident
|
|
81
|
+
include HappyMapper
|
|
82
|
+
attr_accessor :legacy, :cci
|
|
83
|
+
|
|
84
|
+
tag 'ident'
|
|
85
|
+
attribute :system, String, tag: 'system'
|
|
86
|
+
content :ident, String
|
|
87
|
+
end
|
|
88
|
+
|
|
80
89
|
class Rule
|
|
81
90
|
include HappyMapper
|
|
82
91
|
tag 'Rule'
|
|
@@ -87,7 +96,7 @@ module HappyMapperTools
|
|
|
87
96
|
element :title, String, tag: 'title'
|
|
88
97
|
has_one :description, Description, tag: 'description'
|
|
89
98
|
element :reference, ReferenceInfo, tag: 'reference'
|
|
90
|
-
has_many :idents,
|
|
99
|
+
has_many :idents, Ident, tag: 'ident'
|
|
91
100
|
element :fixtext, String, tag: 'fixtext'
|
|
92
101
|
has_one :fix, Fix, tag: 'fix'
|
|
93
102
|
has_one :check, Check, tag: 'check'
|
|
@@ -195,7 +204,7 @@ module HappyMapperTools
|
|
|
195
204
|
'(case sensitive) option to replace the offending tags ' \
|
|
196
205
|
'during processing of the XCCDF ' \
|
|
197
206
|
'file to use the ' +
|
|
198
|
-
"`$#{offending_tags[0]}` "
|
|
207
|
+
"`$#{offending_tags[0]}` ".colorize(:light_green) +
|
|
199
208
|
'syntax in your InSpec profile.'
|
|
200
209
|
option_two = '(2) Update your XCCDF file to *not use* non-standard XCCDF ' \
|
|
201
210
|
'elements within ' +
|
|
@@ -204,7 +213,7 @@ module HappyMapperTools
|
|
|
204
213
|
'`>` '.colorize(:red) +
|
|
205
214
|
'as "placeholders", and use something that doesn\'t confuse ' \
|
|
206
215
|
'the XML parser, such as : ' +
|
|
207
|
-
"`$#{offending_tags[0]}`"
|
|
216
|
+
"`$#{offending_tags[0]}`".colorize(:light_green)
|
|
208
217
|
puts option_one
|
|
209
218
|
puts "\n"
|
|
210
219
|
puts option_two
|
|
@@ -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
|
|
@@ -89,6 +85,14 @@ module InspecTools
|
|
|
89
85
|
|
|
90
86
|
private
|
|
91
87
|
|
|
88
|
+
def register_after_parse_callbacks
|
|
89
|
+
# Determine if the parsed Ident is refrencing a legacy ID number.
|
|
90
|
+
HappyMapperTools::StigAttributes::Ident.after_parse do |object|
|
|
91
|
+
object.cci = object.system.eql?('http://cyber.mil/cci')
|
|
92
|
+
object.legacy = !object.cci
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
92
96
|
def replace_tags_in_xccdf(replace_tags, xccdf_xml)
|
|
93
97
|
replace_tags.each do |tag|
|
|
94
98
|
xccdf_xml = xccdf_xml.gsub(/(<|<)#{tag}(>|>)/, "$#{tag}")
|
|
@@ -108,8 +112,8 @@ module InspecTools
|
|
|
108
112
|
@profile['supports'] = []
|
|
109
113
|
@profile['attributes'] = []
|
|
110
114
|
@profile['generator'] = {
|
|
111
|
-
|
|
112
|
-
|
|
115
|
+
name: 'inspec_tools',
|
|
116
|
+
version: VERSION
|
|
113
117
|
}
|
|
114
118
|
@profile['plaintext'] = @benchmark.plaintext.plaintext
|
|
115
119
|
@profile['status'] = "#{@benchmark.status} on #{@benchmark.release_date.release_date}"
|
|
@@ -121,7 +125,7 @@ module InspecTools
|
|
|
121
125
|
def insert_controls
|
|
122
126
|
@benchmark.group.each do |group|
|
|
123
127
|
control = {}
|
|
124
|
-
control['id'] = group.id
|
|
128
|
+
control['id'] = @use_vuln_id ? group.id : group.rule.id.split('r').first
|
|
125
129
|
control['title'] = group.rule.title
|
|
126
130
|
control['desc'] = group.rule.description.vuln_discussion.split('Satisfies: ')[0]
|
|
127
131
|
control['impact'] = Utils::InspecUtil.get_impact(group.rule.severity)
|
|
@@ -133,8 +137,10 @@ module InspecTools
|
|
|
133
137
|
control['tags']['rid'] = group.rule.id
|
|
134
138
|
control['tags']['stig_id'] = group.rule.version
|
|
135
139
|
control['tags']['fix_id'] = group.rule.fix.id
|
|
136
|
-
control['tags']['cci'] = group.rule.idents
|
|
137
|
-
|
|
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?
|
|
143
|
+
control['tags']['nist'] = @cci_items.fetch_nists(control['tags']['cci'])
|
|
138
144
|
control['tags']['false_negatives'] = group.rule.description.false_negatives if group.rule.description.false_negatives != ''
|
|
139
145
|
control['tags']['false_positives'] = group.rule.description.false_positives if group.rule.description.false_positives != ''
|
|
140
146
|
control['tags']['documentable'] = group.rule.description.documentable if group.rule.description.documentable != ''
|
|
@@ -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
|
|
@@ -99,12 +99,12 @@ module Utils
|
|
|
99
99
|
status_list = control[:status].uniq
|
|
100
100
|
if control[:impact].to_f.zero?
|
|
101
101
|
'Not_Applicable'
|
|
102
|
+
elsif (status_list.include?('error') || status_list.empty?) && for_summary
|
|
103
|
+
'Profile_Error'
|
|
102
104
|
elsif status_list.include?('failed')
|
|
103
105
|
'Open'
|
|
104
106
|
elsif status_list.include?('passed')
|
|
105
107
|
'NotAFinding'
|
|
106
|
-
elsif status_list.include?('error') && for_summary
|
|
107
|
-
'Profile_Error'
|
|
108
108
|
else
|
|
109
109
|
# profile skipped or profile error
|
|
110
110
|
'Not_Reviewed'
|
|
@@ -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,6 +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']))
|
|
250
|
+
control.add_tag(::Inspec::Object::Tag.new('legacy', json_control['tags']['legacy'])) unless json_control['tags']['legacy'].blank?
|
|
249
251
|
control.add_tag(::Inspec::Object::Tag.new('nist', json_control['tags']['nist']))
|
|
250
252
|
control.add_tag(::Inspec::Object::Tag.new('cis_level', json_control['tags']['cis_level'])) unless json_control['tags']['cis_level'].blank?
|
|
251
253
|
control.add_tag(::Inspec::Object::Tag.new('cis_controls', json_control['tags']['cis_controls'])) unless json_control['tags']['cis_controls'].blank?
|
|
@@ -325,7 +327,7 @@ module Utils
|
|
|
325
327
|
myfile.puts readme_contents
|
|
326
328
|
end
|
|
327
329
|
|
|
328
|
-
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)
|
|
329
331
|
FileUtils.rm_rf(directory) if Dir.exist?(directory)
|
|
330
332
|
Dir.mkdir directory unless Dir.exist?(directory)
|
|
331
333
|
Dir.mkdir "#{directory}/controls" unless Dir.exist?("#{directory}/controls")
|
|
@@ -336,7 +338,7 @@ module Utils
|
|
|
336
338
|
file_name = control.id.to_s
|
|
337
339
|
myfile = File.new("#{directory}/controls/#{file_name}.rb", 'w')
|
|
338
340
|
myfile.puts "# encoding: UTF-8\n\n"
|
|
339
|
-
myfile.puts wrap(control.to_ruby, WIDTH)
|
|
341
|
+
myfile.puts "#{wrap(control.to_ruby, WIDTH)}\n"
|
|
340
342
|
myfile.close
|
|
341
343
|
end
|
|
342
344
|
else
|
|
@@ -352,7 +354,7 @@ module Utils
|
|
|
352
354
|
if output_format == 'ruby'
|
|
353
355
|
controls.each do |control|
|
|
354
356
|
myfile.puts "# encoding: UTF-8\n\n"
|
|
355
|
-
myfile.puts wrap(control.to_ruby.gsub('"', "\'"), WIDTH)
|
|
357
|
+
myfile.puts "#{wrap(control.to_ruby.gsub('"', "\'"), WIDTH)}\n"
|
|
356
358
|
end
|
|
357
359
|
else
|
|
358
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,11 +29,12 @@ 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
|
|
36
36
|
c_data[c_id]['cci'] = control['tags']['cci'] if control['tags']['cci'] # Optional attribute
|
|
37
|
+
c_data[c_id]['legacy'] = control['tags']['legacy'] if control['tags']['legacy'] # Optional attribute
|
|
37
38
|
c_data[c_id]['nist'] = control['tags']['nist'] || ['unmapped']
|
|
38
39
|
c_data[c_id]['check'] = control['tags']['check'] || DATA_NOT_FOUND_MESSAGE
|
|
39
40
|
c_data[c_id]['checkref'] = control['tags']['checkref'] || 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,13 +67,14 @@ 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
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
group.rule.ident = build_rule_idents(control['cci']) if control['cci']
|
|
77
|
+
group.rule.ident += build_rule_idents(control['legacy']) if control['legacy']
|
|
77
78
|
|
|
78
79
|
group.rule.fixtext = HappyMapperTools::Benchmark::Fixtext.new
|
|
79
80
|
group.rule.fixtext.fixref = control['fix_id']
|
|
@@ -126,10 +127,7 @@ module Utils
|
|
|
126
127
|
|
|
127
128
|
# Each rule identifier is a different element
|
|
128
129
|
idents.map do |identifier|
|
|
129
|
-
|
|
130
|
-
ident.system = 'https://public.cyber.mil/stigs/cci/'
|
|
131
|
-
ident.ident = identifier
|
|
132
|
-
ident
|
|
130
|
+
HappyMapperTools::Benchmark::Ident.new identifier
|
|
133
131
|
end
|
|
134
132
|
end
|
|
135
133
|
|
|
@@ -184,7 +182,7 @@ module Utils
|
|
|
184
182
|
|
|
185
183
|
# Build out the TestResult given all the control and result data.
|
|
186
184
|
def populate_results(test_result)
|
|
187
|
-
#
|
|
185
|
+
# NOTE: id is not an XCCDF 1.2 compliant identifier and will need to be updated when that support is added.
|
|
188
186
|
test_result.id = 'result_1'
|
|
189
187
|
test_result.starttime = run_start_time
|
|
190
188
|
test_result.endtime = run_end_time
|
|
@@ -227,6 +225,7 @@ module Utils
|
|
|
227
225
|
rule_result.instance = result['code_desc']
|
|
228
226
|
|
|
229
227
|
rule_result.ident = build_rule_idents(control['cci']) if control['cci']
|
|
228
|
+
rule_result.ident += build_rule_idents(control['legacy']) if control['legacy']
|
|
230
229
|
|
|
231
230
|
# Fix information is only necessary when there are failed tests
|
|
232
231
|
rule_result.fix = build_rule_fix(control['fix_id']) if control['fix_id'] && result_status == 'fail'
|
|
@@ -238,7 +237,7 @@ module Utils
|
|
|
238
237
|
end
|
|
239
238
|
|
|
240
239
|
# Combines rule results with the same result into a single rule result.
|
|
241
|
-
def combine_results(rule_results)
|
|
240
|
+
def combine_results(rule_results)
|
|
242
241
|
return rule_results.first if rule_results.size == 1
|
|
243
242
|
|
|
244
243
|
# Can combine, result, idents (duplicate, take first instance), instance - combine into an array removing duplicates
|
|
@@ -329,7 +328,7 @@ module Utils
|
|
|
329
328
|
# @param one [String] A rule-result status
|
|
330
329
|
# @param two [String] A rule-result status
|
|
331
330
|
# @return The result of the AND operation.
|
|
332
|
-
def xccdf_and_result(one, two)
|
|
331
|
+
def xccdf_and_result(one, two)
|
|
333
332
|
# From XCCDF specification truth table
|
|
334
333
|
# P = pass
|
|
335
334
|
# F = fail
|
|
@@ -362,7 +361,7 @@ module Utils
|
|
|
362
361
|
|
|
363
362
|
message = HappyMapperTools::Benchmark::MessageType.new
|
|
364
363
|
# Including the code of the check and the resulting message if there is one.
|
|
365
|
-
message.message = "#{result['code_desc'] ? result['code_desc']
|
|
364
|
+
message.message = "#{result['code_desc'] ? "#{result['code_desc']}\n\n" : ''}#{result['message'] || result['skip_message']}"
|
|
366
365
|
message.severity = result_message_severity(xccdf_status)
|
|
367
366
|
message
|
|
368
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.7
|
|
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-06-07 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
|
|
@@ -358,7 +372,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
358
372
|
- !ruby/object:Gem::Version
|
|
359
373
|
version: '0'
|
|
360
374
|
requirements: []
|
|
361
|
-
rubygems_version: 3.
|
|
375
|
+
rubygems_version: 3.2.15
|
|
362
376
|
signing_key:
|
|
363
377
|
specification_version: 4
|
|
364
378
|
summary: Converter utils for Inspec
|