inspec_tools 2.3.5 → 2.3.6
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 +7 -6
- data/lib/happy_mapper_tools/cci_attributes.rb +0 -4
- data/lib/happy_mapper_tools/stig_attributes.rb +4 -4
- 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 +10 -10
- 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: 83f3edc68686aa22483f4f17080a176122ed5d3b8c41c0a1f47a68062aa04b4d
|
4
|
+
data.tar.gz: 6c140e38354f7decc791db6829bbcff2bf845d256d9860769514d62c17cf50c6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 21875ca95036a401be3eb22ef782b1d92f12869fa6cf32183a80d8dd951e9ab95196458485c3437d4be8f902d8a6def3d5da2c411d517e40020cd4b3ad013a1b
|
7
|
+
data.tar.gz: 5879c557fcf781bec5a44a637160bedd981450b071b72e8155d7046aad8adba8cefbe93f4b92f6d5be666b1d88363328e392a5fdddc271383ca6c087ccb34f8a
|
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,10 +67,11 @@ 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})$/
|
71
72
|
# Match CCI IDs; e.g. CCI-123456
|
72
73
|
@system = 'http://cyber.mil/cci'
|
73
|
-
|
74
|
+
when /^(S?V-[0-9]{5})$/
|
74
75
|
# Match SV- IDs; e.g. SV-12345
|
75
76
|
# Match V- IDs; e.g. V-12345
|
76
77
|
@system = 'http://cyber.mil/legacy'
|
@@ -128,7 +129,7 @@ module HappyMapperTools
|
|
128
129
|
element :result, String, tag: 'result'
|
129
130
|
# element override - Not implemented. Does not apply to Inspec execution
|
130
131
|
has_many :ident, Ident, tag: 'ident'
|
131
|
-
#
|
132
|
+
# NOTE: element metadata not implemented at this time
|
132
133
|
has_many :message, MessageType, tag: 'message'
|
133
134
|
has_many :instance, String, tag: 'instance'
|
134
135
|
element :fix, Fix, tag: 'fix'
|
@@ -175,7 +176,7 @@ module HappyMapperTools
|
|
175
176
|
|
176
177
|
class TestResult
|
177
178
|
include HappyMapper
|
178
|
-
#
|
179
|
+
# NOTE: element benchmark not implemented at this time since this is same file
|
179
180
|
# Note: element title not implemented due to no mapping from Chef Inspec
|
180
181
|
element :remark, String, tag: 'remark'
|
181
182
|
has_many :organization, String, tag: 'organization'
|
@@ -184,14 +185,14 @@ module HappyMapperTools
|
|
184
185
|
has_many :target_address, String, tag: 'target-address'
|
185
186
|
element :target_facts, TargetFact, tag: 'target-facts'
|
186
187
|
element :platform, CPE2idrefType, tag: 'platform'
|
187
|
-
#
|
188
|
+
# NOTE: element profile not implemented since Benchmark profile is also not implemented
|
188
189
|
has_many :rule_result, RuleResultType, tag: 'rule-result'
|
189
190
|
has_many :score, ScoreType, tag: 'score' # One minimum
|
190
191
|
# Note: element signature not implemented due to no mapping from Chef Inspec
|
191
192
|
attribute :id, String, tag: 'id'
|
192
193
|
attribute :starttime, String, tag: 'start-time'
|
193
194
|
attribute :endtime, String, tag: 'end-time'
|
194
|
-
#
|
195
|
+
# NOTE: attribute test-system not implemented at this time due to unknown CPE value for Chef Inspec
|
195
196
|
attribute :version, String, tag: 'version'
|
196
197
|
end
|
197
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
|
@@ -204,7 +204,7 @@ module HappyMapperTools
|
|
204
204
|
'(case sensitive) option to replace the offending tags ' \
|
205
205
|
'during processing of the XCCDF ' \
|
206
206
|
'file to use the ' +
|
207
|
-
"`$#{offending_tags[0]}` "
|
207
|
+
"`$#{offending_tags[0]}` ".colorize(:light_green) +
|
208
208
|
'syntax in your InSpec profile.'
|
209
209
|
option_two = '(2) Update your XCCDF file to *not use* non-standard XCCDF ' \
|
210
210
|
'elements within ' +
|
@@ -213,7 +213,7 @@ module HappyMapperTools
|
|
213
213
|
'`>` '.colorize(:red) +
|
214
214
|
'as "placeholders", and use something that doesn\'t confuse ' \
|
215
215
|
'the XML parser, such as : ' +
|
216
|
-
"`$#{offending_tags[0]}`"
|
216
|
+
"`$#{offending_tags[0]}`".colorize(:light_green)
|
217
217
|
puts option_one
|
218
218
|
puts "\n"
|
219
219
|
puts option_two
|
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
|
@@ -66,7 +66,7 @@ module InspecTools
|
|
66
66
|
#
|
67
67
|
# @param inspec_json : an inspec profile formatted as a json object
|
68
68
|
###
|
69
|
-
def inspec_json_to_array(inspec_json)
|
69
|
+
def inspec_json_to_array(inspec_json)
|
70
70
|
data = []
|
71
71
|
headers = {}
|
72
72
|
inspec_json['controls'].each do |control|
|
@@ -158,19 +158,19 @@ module InspecTools
|
|
158
158
|
vuln
|
159
159
|
end
|
160
160
|
|
161
|
-
def generate_asset
|
161
|
+
def generate_asset
|
162
162
|
asset = HappyMapperTools::StigChecklist::Asset.new
|
163
|
-
asset.role =
|
164
|
-
asset.type =
|
163
|
+
asset.role = @metadata['role'].nil? ? 'Workstation' : @metadata['role']
|
164
|
+
asset.type = @metadata['type'].nil? ? 'Computing' : @metadata['type']
|
165
165
|
asset.host_name = generate_hostname
|
166
166
|
asset.host_ip = generate_ip
|
167
167
|
asset.host_mac = generate_mac
|
168
168
|
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 =
|
169
|
+
asset.tech_area = @metadata['tech_area'].nil? ? '' : @metadata['tech_area']
|
170
|
+
asset.target_key = @metadata['target_key'].nil? ? '' : @metadata['target_key']
|
171
|
+
asset.web_or_database = @metadata['web_or_database'].nil? ? '0' : @metadata['web_or_database']
|
172
|
+
asset.web_db_site = @metadata['web_db_site'].nil? ? '' : @metadata['web_db_site']
|
173
|
+
asset.web_db_instance = @metadata['web_db_instance'].nil? ? '' : @metadata['web_db_instance']
|
174
174
|
asset
|
175
175
|
end
|
176
176
|
|
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.6
|
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-05-
|
14
|
+
date: 2021-05-27 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
|