inspec_tools 2.1.0 → 2.3.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +13 -7
- data/lib/happy_mapper_tools/benchmark.rb +8 -0
- data/lib/happy_mapper_tools/stig_attributes.rb +10 -1
- data/lib/inspec_tools/csv.rb +16 -6
- data/lib/inspec_tools/plugin_cli.rb +8 -28
- data/lib/inspec_tools/summary.rb +108 -76
- data/lib/inspec_tools/xccdf.rb +12 -3
- data/lib/utilities/inspec_util.rb +5 -4
- data/lib/utilities/xccdf/from_inspec.rb +1 -0
- data/lib/utilities/xccdf/to_xccdf.rb +3 -4
- metadata +4 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6188a2ff394f6f733d23d032d4919e1f9e5e08267bbce30a2a4725631d5a5a13
|
4
|
+
data.tar.gz: 4b3b5fc70d53d4784ef84f881493ca361d998a254c96323e4e1e65c48e7e5630
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e537d4749c34eaac49e4c80355250949f02d0db6020596d4c73e1f2995e384742cefc5d72a12fb7c2a159ceca406dbc723aa5d8e36ea8272967e9bd37f49da08
|
7
|
+
data.tar.gz: 5a26c4b893a8646ae1e4ae62cd380b0603f0a45b1a88a7234af5a9a8d52a2cc71561d8d0ee784a8bedbbcda7abc26d88b58f999761649bcd29c0adfc464fec78
|
data/README.md
CHANGED
@@ -100,13 +100,15 @@ If the specified threshold is not met, an error code (1) is returned along with
|
|
100
100
|
|
101
101
|
The compliance score are rounded down to the nearest whole number. For example a score of 77.3 would be displayed as 77.
|
102
102
|
|
103
|
+
Thresholds provided inline (i.e. `-i`) override thresholds provided by files (i.e. `-f`).
|
104
|
+
|
103
105
|
```
|
104
106
|
USAGE: inspec_tools compliance [OPTIONS] -j <inspec-json> -i <threshold-inline>
|
105
107
|
inspec_tools compliance [OPTIONS] -j <inspec-json> -f <threshold-file>
|
106
108
|
FLAGS:
|
107
109
|
-j --inspec-json <inspec-json> : path to InSpec results Json
|
108
110
|
-i --template-inline <threshold-inline> : inline compliance threshold definition
|
109
|
-
-f --
|
111
|
+
-f --threshold-file <threshold-file> : yaml file with compliance threshold definition
|
110
112
|
Examples:
|
111
113
|
|
112
114
|
inspec_tools compliance -j examples/sample_json/rhel-simp.json -i '{compliance.min: 80, failed.critical.max: 0, failed.high.max: 0}'
|
@@ -135,11 +137,11 @@ failed.high.max: 1
|
|
135
137
|
|
136
138
|
#### In-Line Examples
|
137
139
|
```
|
138
|
-
{compliance: {min: 90}, failed: {critical: {max: 0}, high: {max: 0}}}
|
140
|
+
"{compliance: {min: 90}, failed: {critical: {max: 0}, high: {max: 0}}}"
|
139
141
|
```
|
140
142
|
|
141
143
|
```
|
142
|
-
{compliance.min: 81, failed.critical.max: 0, failed.high.max: 0}
|
144
|
+
"{compliance.min: 81, failed.critical.max: 0, failed.high.max: 0}"
|
143
145
|
```
|
144
146
|
|
145
147
|
## summary
|
@@ -185,12 +187,16 @@ Using additional flags will override the normal output and only display the outp
|
|
185
187
|
|
186
188
|
USAGE: inspec_tools summary [OPTIONS] -j <inspec-json>
|
187
189
|
|
190
|
+
Thresholds provided inline (i.e. `-i`) override thresholds provided by files (i.e. `-t`).
|
191
|
+
|
192
|
+
|
188
193
|
```
|
189
194
|
FLAGS:
|
190
|
-
-j --inspec-json <inspec-json>
|
191
|
-
-
|
192
|
-
-
|
193
|
-
-
|
195
|
+
-j --inspec-json <inspec-json> : path to InSpec results JSON
|
196
|
+
-f --json-full, --no-json-full : print the summary STDOUT as JSON
|
197
|
+
-k --json-counts, --no-json-counts : print the result status to STDOUT as JSON
|
198
|
+
-t, --threshold-file=THRESHOLD_FILE] : path to threshold YAML file
|
199
|
+
-i, --threshold-inline=THRESHOLD_INLINE] : string of text representing threshold YAML inline
|
194
200
|
|
195
201
|
Examples:
|
196
202
|
|
@@ -65,6 +65,14 @@ 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
|
+
if ident_str =~ /^(CCI-[0-9]{6})$/
|
71
|
+
@system = 'http://cyber.mil/cci'
|
72
|
+
else
|
73
|
+
@system = 'http://cyber.mil/legacy'
|
74
|
+
end
|
75
|
+
end
|
68
76
|
end
|
69
77
|
|
70
78
|
# Class Fixtext maps from the 'fixtext' from Benchmark XML file using HappyMapper
|
@@ -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
|
83
|
+
attr_accessor :cci
|
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'
|
data/lib/inspec_tools/csv.rb
CHANGED
@@ -21,12 +21,12 @@ module InspecTools
|
|
21
21
|
@csv.shift if @mapping['skip_csv_header']
|
22
22
|
end
|
23
23
|
|
24
|
-
def to_inspec
|
24
|
+
def to_inspec(control_name_prefix: nil)
|
25
25
|
@controls = []
|
26
26
|
@profile = {}
|
27
27
|
@cci_xml = Utils::CciXml.get_cci_list('U_CCI_List.xml')
|
28
28
|
insert_metadata
|
29
|
-
parse_controls
|
29
|
+
parse_controls(control_name_prefix)
|
30
30
|
@profile['controls'] = @controls
|
31
31
|
@profile['sha256'] = Digest::SHA256.hexdigest(@profile.to_s)
|
32
32
|
@profile
|
@@ -66,12 +66,12 @@ module InspecTools
|
|
66
66
|
cell.split("\n").first
|
67
67
|
end
|
68
68
|
|
69
|
-
def parse_controls
|
69
|
+
def parse_controls(prefix)
|
70
70
|
@csv.each do |row|
|
71
71
|
control = {}
|
72
|
-
control['id'] = row[@mapping['control.id']]
|
73
|
-
control['title'] = row[@mapping['control.title']]
|
74
|
-
control['desc'] = row[@mapping['control.desc']]
|
72
|
+
control['id'] = generate_control_id(prefix, row[@mapping['control.id']]) unless @mapping['control.id'].nil? || row[@mapping['control.id']].nil?
|
73
|
+
control['title'] = row[@mapping['control.title']] unless @mapping['control.title'].nil? || row[@mapping['control.title']].nil?
|
74
|
+
control['desc'] = row[@mapping['control.desc']] unless @mapping['control.desc'].nil? || row[@mapping['control.desc']].nil?
|
75
75
|
control['tags'] = {}
|
76
76
|
cci_number = get_cci_number(row[@mapping['control.tags']['cci']])
|
77
77
|
nist = get_nist_reference(cci_number) unless cci_number.nil?
|
@@ -90,5 +90,15 @@ module InspecTools
|
|
90
90
|
@controls << control
|
91
91
|
end
|
92
92
|
end
|
93
|
+
|
94
|
+
def generate_control_id(prefix, id)
|
95
|
+
return id if prefix.nil?
|
96
|
+
|
97
|
+
"#{prefix}-#{id}"
|
98
|
+
end
|
93
99
|
end
|
94
100
|
end
|
101
|
+
|
102
|
+
# rubocop:enable Metrics/AbcSize
|
103
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
104
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'yaml'
|
2
1
|
require 'json'
|
3
2
|
require 'roo'
|
4
3
|
require_relative '../utilities/inspec_util'
|
@@ -13,7 +12,6 @@ module InspecTools
|
|
13
12
|
autoload :CKL, 'inspec_tools/ckl'
|
14
13
|
autoload :Inspec, 'inspec_tools/inspec'
|
15
14
|
autoload :Summary, 'inspec_tools/summary'
|
16
|
-
autoload :Threshold, 'inspec_tools/threshold'
|
17
15
|
autoload :XLSXTool, 'inspec_tools/xlsx_tool'
|
18
16
|
autoload :GenerateMap, 'inspec_tools/generate_map'
|
19
17
|
end
|
@@ -80,10 +78,11 @@ module InspecPlugins
|
|
80
78
|
option :output, required: false, aliases: '-o', default: 'profile'
|
81
79
|
option :format, required: false, aliases: '-f', enum: %w{ruby hash}, default: 'ruby'
|
82
80
|
option :separate_files, required: false, type: :boolean, default: true, aliases: '-s'
|
81
|
+
option :control_name_prefix, required: false, type: :string, aliases: '-p'
|
83
82
|
def csv2inspec
|
84
83
|
csv = CSV.read(options[:csv], encoding: 'ISO8859-1')
|
85
84
|
mapping = YAML.load_file(options[:mapping])
|
86
|
-
profile = InspecTools::CSVTool.new(csv, mapping, options[:csv].split('/')[-1].split('.')[0], options[:verbose]).to_inspec
|
85
|
+
profile = InspecTools::CSVTool.new(csv, mapping, options[:csv].split('/')[-1].split('.')[0], options[:verbose]).to_inspec(control_name_prefix: options[:control_name_prefix])
|
87
86
|
Utils::InspecUtil.unpack_inspec_json(options[:output], profile, options[:separate_files], options[:format])
|
88
87
|
end
|
89
88
|
|
@@ -189,26 +188,14 @@ module InspecPlugins
|
|
189
188
|
desc 'summary', 'summary parses an inspec results json to create a summary json'
|
190
189
|
long_desc InspecTools::Help.text(:summary)
|
191
190
|
option :inspec_json, required: true, aliases: '-j'
|
192
|
-
option :verbose, type: :boolean, aliases: '-V'
|
193
191
|
option :json_full, type: :boolean, required: false, aliases: '-f'
|
194
192
|
option :json_counts, type: :boolean, required: false, aliases: '-k'
|
193
|
+
option :threshold_file, required: false, aliases: '-t'
|
194
|
+
option :threshold_inline, required: false, aliases: '-i'
|
195
195
|
|
196
196
|
def summary
|
197
|
-
summary = InspecTools::Summary.new(
|
198
|
-
|
199
|
-
unless options.include?('json_full') || options.include?('json_counts')
|
200
|
-
puts "\nOverall compliance: #{summary[:compliance]}%\n\n"
|
201
|
-
summary[:status].keys.each do |category|
|
202
|
-
puts category
|
203
|
-
summary[:status][category].keys.each do |impact|
|
204
|
-
puts "\t#{impact} : #{summary[:status][category][impact]}"
|
205
|
-
end
|
206
|
-
end
|
207
|
-
end
|
208
|
-
|
209
|
-
json_summary = summary.to_json
|
210
|
-
puts json_summary if options[:json_full]
|
211
|
-
puts summary[:status].to_json if options[:json_counts]
|
197
|
+
summary = InspecTools::Summary.new(options: options)
|
198
|
+
summary.output_summary
|
212
199
|
end
|
213
200
|
|
214
201
|
desc 'compliance', 'compliance parses an inspec results json to check if the compliance level meets a specified threshold'
|
@@ -216,17 +203,10 @@ module InspecPlugins
|
|
216
203
|
option :inspec_json, required: true, aliases: '-j'
|
217
204
|
option :threshold_file, required: false, aliases: '-f'
|
218
205
|
option :threshold_inline, required: false, aliases: '-i'
|
219
|
-
option :verbose, type: :boolean, aliases: '-V'
|
220
206
|
|
221
207
|
def compliance
|
222
|
-
|
223
|
-
|
224
|
-
exit(1)
|
225
|
-
end
|
226
|
-
threshold = YAML.load_file(options[:threshold_file]) unless options[:threshold_file].nil?
|
227
|
-
threshold = YAML.safe_load(options[:threshold_inline]) unless options[:threshold_inline].nil?
|
228
|
-
compliance = InspecTools::Summary.new(File.read(options[:inspec_json])).threshold(threshold)
|
229
|
-
compliance ? exit(0) : exit(1)
|
208
|
+
compliance = InspecTools::Summary.new(options: options)
|
209
|
+
compliance.results_meet_threshold? ? exit(0) : exit(1)
|
230
210
|
end
|
231
211
|
end
|
232
212
|
end
|
data/lib/inspec_tools/summary.rb
CHANGED
@@ -2,8 +2,6 @@ require 'json'
|
|
2
2
|
require 'yaml'
|
3
3
|
require_relative '../utilities/inspec_util'
|
4
4
|
|
5
|
-
# rubocop:disable Metrics/AbcSize
|
6
|
-
|
7
5
|
# Impact Definitions
|
8
6
|
CRITICAL = 0.9
|
9
7
|
HIGH = 0.7
|
@@ -16,48 +14,118 @@ TALLYS = %i(total critical high medium low).freeze
|
|
16
14
|
THRESHOLD_TEMPLATE = File.expand_path('../data/threshold.yaml', File.dirname(__FILE__))
|
17
15
|
|
18
16
|
module InspecTools
|
17
|
+
# rubocop:disable Metrics/ClassLength
|
19
18
|
class Summary
|
20
|
-
|
21
|
-
|
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
|
26
|
+
|
27
|
+
def initialize(**options)
|
28
|
+
options = options[:options]
|
29
|
+
@json = JSON.parse(File.read(options[:inspec_json]))
|
30
|
+
@json_full = false || options[:json_full]
|
31
|
+
@json_counts = false || options[:json_counts]
|
32
|
+
@threshold = parse_threshold(options[:threshold_inline], options[:threshold_file])
|
33
|
+
@threshold_provided = options[:threshold_inline] || options[:threshold_file]
|
34
|
+
@summary = compute_summary
|
22
35
|
end
|
23
36
|
|
24
|
-
def
|
25
|
-
@
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
37
|
+
def output_summary
|
38
|
+
unless @json_full || @json_counts
|
39
|
+
puts "\nThreshold compliance: #{@threshold['compliance.min']}%"
|
40
|
+
puts "\nOverall compliance: #{@summary[:compliance]}%\n\n"
|
41
|
+
@summary[:status].keys.each do |category|
|
42
|
+
puts category
|
43
|
+
@summary[:status][category].keys.each do |impact|
|
44
|
+
puts "\t#{impact} : #{@summary[:status][category][impact]}"
|
45
|
+
end
|
46
|
+
end
|
31
47
|
end
|
32
|
-
|
33
|
-
@summary
|
48
|
+
|
49
|
+
puts @summary.to_json if @json_full
|
50
|
+
puts @summary[:status].to_json if @json_counts
|
34
51
|
end
|
35
52
|
|
36
|
-
def
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
53
|
+
def results_meet_threshold?
|
54
|
+
raise 'Please provide threshold as a yaml file or inline yaml' unless @threshold_provided
|
55
|
+
|
56
|
+
compliance = true
|
57
|
+
failure = []
|
58
|
+
failure << check_max_compliance(@threshold['compliance.max'], @summary[:compliance], '', 'compliance')
|
59
|
+
failure << check_min_compliance(@threshold['compliance.min'], @summary[:compliance], '', 'compliance')
|
60
|
+
|
61
|
+
BUCKETS.each do |bucket|
|
62
|
+
TALLYS.each do |tally|
|
63
|
+
failure << check_min_compliance(@threshold["#{bucket}.#{tally}.min"], @summary[:status][bucket][tally], bucket, tally)
|
64
|
+
failure << check_max_compliance(@threshold["#{bucket}.#{tally}.max"], @summary[:status][bucket][tally], bucket, tally)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
failure.reject!(&:nil?)
|
69
|
+
compliance = false if failure.length.positive?
|
70
|
+
output(compliance, failure)
|
71
|
+
compliance
|
41
72
|
end
|
42
73
|
|
43
74
|
private
|
44
75
|
|
76
|
+
def check_min_compliance(min, data, bucket, tally)
|
77
|
+
expected_to_string(bucket, tally, 'min', min, data) if min != -1 and data < min
|
78
|
+
end
|
79
|
+
|
80
|
+
def check_max_compliance(max, data, bucket, tally)
|
81
|
+
expected_to_string(bucket, tally, 'max', max, data) if max != -1 and data > max
|
82
|
+
end
|
83
|
+
|
84
|
+
def output(passed_threshold, what_failed)
|
85
|
+
if passed_threshold
|
86
|
+
puts "Overall compliance threshold of #{@threshold['compliance.min']}\% met. Current compliance at #{@summary[:compliance]}\%"
|
87
|
+
else
|
88
|
+
puts 'Compliance threshold was not met: '
|
89
|
+
puts what_failed.join("\n")
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def expected_to_string(bucket, tally, maxmin, value, got)
|
94
|
+
return "Expected #{bucket}.#{tally}.#{maxmin}:#{value} got:#{got}" unless bucket.empty? || bucket.nil?
|
95
|
+
|
96
|
+
"Expected #{tally}.#{maxmin}:#{value}\% got:#{got}\%"
|
97
|
+
end
|
98
|
+
|
99
|
+
def parse_threshold(threshold_inline, threshold_file)
|
100
|
+
threshold = Utils::InspecUtil.to_dotted_hash(YAML.load_file(THRESHOLD_TEMPLATE))
|
101
|
+
threshold.merge!(Utils::InspecUtil.to_dotted_hash(YAML.load_file(threshold_file))) if threshold_file
|
102
|
+
threshold.merge!(Utils::InspecUtil.to_dotted_hash(YAML.safe_load(threshold_inline))) if threshold_inline
|
103
|
+
threshold
|
104
|
+
end
|
105
|
+
|
45
106
|
def compute_summary
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
107
|
+
data = Utils::InspecUtil.parse_data_for_ckl(@json)
|
108
|
+
|
109
|
+
data.keys.each do |control_id|
|
110
|
+
current_control = data[control_id]
|
111
|
+
current_control[:compliance_status] = Utils::InspecUtil.control_status(current_control, true)
|
112
|
+
current_control[:finding_details] = Utils::InspecUtil.control_finding_details(current_control, current_control[:compliance_status])
|
113
|
+
end
|
114
|
+
|
115
|
+
summary = {}
|
116
|
+
summary[:buckets] = {}
|
117
|
+
summary[:buckets][:failed] = select_by_status(data, 'Open')
|
118
|
+
summary[:buckets][:passed] = select_by_status(data, 'NotAFinding')
|
119
|
+
summary[:buckets][:no_impact] = select_by_status(data, 'Not_Applicable')
|
120
|
+
summary[:buckets][:skipped] = select_by_status(data, 'Not_Reviewed')
|
121
|
+
summary[:buckets][:error] = select_by_status(data, 'Profile_Error')
|
122
|
+
|
123
|
+
summary[:status] = {}
|
124
|
+
%i(failed passed no_impact skipped error).each do |key|
|
125
|
+
summary[:status][key] = tally_by_impact(summary[:buckets][key])
|
126
|
+
end
|
127
|
+
summary[:compliance] = compute_compliance(summary)
|
128
|
+
summary
|
61
129
|
end
|
62
130
|
|
63
131
|
def select_by_impact(controls, impact)
|
@@ -78,49 +146,13 @@ module InspecTools
|
|
78
146
|
tally
|
79
147
|
end
|
80
148
|
|
81
|
-
def compute_compliance
|
82
|
-
(
|
83
|
-
(
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
end
|
88
|
-
|
89
|
-
def threshold_compliance
|
90
|
-
compliance = true
|
91
|
-
failure = []
|
92
|
-
max = @threshold['compliance.max']
|
93
|
-
min = @threshold['compliance.min']
|
94
|
-
if max != -1 and @summary[:compliance] > max
|
95
|
-
compliance = false
|
96
|
-
failure << "Expected compliance.max:#{max} got:#{@summary[:compliance]}"
|
97
|
-
end
|
98
|
-
if min != -1 and @summary[:compliance] < min
|
99
|
-
compliance = false
|
100
|
-
failure << "Expected compliance.min:#{min} got:#{@summary[:compliance]}"
|
101
|
-
end
|
102
|
-
status = @summary[:status]
|
103
|
-
BUCKETS.each do |bucket|
|
104
|
-
TALLYS.each do |tally|
|
105
|
-
max = @threshold["#{bucket}.#{tally}.max"]
|
106
|
-
min = @threshold["#{bucket}.#{tally}.min"]
|
107
|
-
if max != -1 and status[bucket][tally] > max
|
108
|
-
compliance = false
|
109
|
-
failure << "Expected #{bucket}.#{tally}.max:#{max} got:#{status[bucket][tally]}"
|
110
|
-
end
|
111
|
-
if min != -1 and status[bucket][tally] < min
|
112
|
-
compliance = false
|
113
|
-
failure << "Expected #{bucket}.#{tally}.min:#{min} got:#{status[bucket][tally]}"
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
puts failure.join("\n") unless compliance
|
118
|
-
puts 'Compliance threshold met' if compliance
|
119
|
-
compliance
|
120
|
-
end
|
121
|
-
|
122
|
-
def parse_threshold(new_threshold)
|
123
|
-
@threshold.merge!(new_threshold)
|
149
|
+
def compute_compliance(summary)
|
150
|
+
(summary[:status][:passed][:total]*100.0/
|
151
|
+
(summary[:status][:passed][:total]+
|
152
|
+
summary[:status][:failed][:total]+
|
153
|
+
summary[:status][:skipped][:total]+
|
154
|
+
summary[:status][:error][:total])).floor
|
124
155
|
end
|
125
156
|
end
|
157
|
+
# rubocop:enable Metrics/ClassLength
|
126
158
|
end
|
data/lib/inspec_tools/xccdf.rb
CHANGED
@@ -17,7 +17,7 @@ module InspecTools
|
|
17
17
|
@xccdf = replace_tags_in_xccdf(replace_tags, @xccdf) unless replace_tags.nil?
|
18
18
|
cci_list_path = File.join(File.dirname(__FILE__), '../data/U_CCI_List.xml')
|
19
19
|
@cci_items = HappyMapperTools::CCIAttributes::CCI_List.parse(File.read(cci_list_path))
|
20
|
-
|
20
|
+
register_after_parse_callbacks
|
21
21
|
@benchmark = HappyMapperTools::StigAttributes::Benchmark.parse(@xccdf)
|
22
22
|
end
|
23
23
|
|
@@ -89,6 +89,14 @@ module InspecTools
|
|
89
89
|
|
90
90
|
private
|
91
91
|
|
92
|
+
def register_after_parse_callbacks
|
93
|
+
# Determine if the parsed Ident is refrencing a legacy ID number.
|
94
|
+
HappyMapperTools::StigAttributes::Ident.after_parse do |object|
|
95
|
+
object.cci = object.system.eql?('http://cyber.mil/cci')
|
96
|
+
object.legacy = !object.cci
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
92
100
|
def replace_tags_in_xccdf(replace_tags, xccdf_xml)
|
93
101
|
replace_tags.each do |tag|
|
94
102
|
xccdf_xml = xccdf_xml.gsub(/(<|<)#{tag}(>|>)/, "$#{tag}")
|
@@ -133,8 +141,9 @@ module InspecTools
|
|
133
141
|
control['tags']['rid'] = group.rule.id
|
134
142
|
control['tags']['stig_id'] = group.rule.version
|
135
143
|
control['tags']['fix_id'] = group.rule.fix.id
|
136
|
-
control['tags']['cci'] = group.rule.idents
|
137
|
-
control['tags']['
|
144
|
+
control['tags']['cci'] = group.rule.idents.select { |i| i.cci }.map { |i| i.ident }
|
145
|
+
control['tags']['legacy'] = group.rule.idents.select { |i| i.legacy}.map { |i| i.ident }
|
146
|
+
control['tags']['nist'] = @cci_items.fetch_nists(control['tags']['cci'])
|
138
147
|
control['tags']['false_negatives'] = group.rule.description.false_negatives if group.rule.description.false_negatives != ''
|
139
148
|
control['tags']['false_positives'] = group.rule.description.false_positives if group.rule.description.false_positives != ''
|
140
149
|
control['tags']['documentable'] = group.rule.description.documentable if group.rule.description.documentable != ''
|
@@ -48,8 +48,8 @@ module Utils
|
|
48
48
|
end
|
49
49
|
|
50
50
|
if control['descriptions'].respond_to?(:find)
|
51
|
-
data[c_id][:check_content] = control['descriptions'].find { |c| c['label'] == '
|
52
|
-
data[c_id][:fix_text] = control['descriptions'].find { |c| c['label'] == '
|
51
|
+
data[c_id][:check_content] = control['descriptions'].find { |c| c['label'] == 'check' }&.dig('data')
|
52
|
+
data[c_id][:fix_text] = control['descriptions'].find { |c| c['label'] == 'fix' }&.dig('data')
|
53
53
|
end
|
54
54
|
|
55
55
|
data[c_id][:impact] = control['impact'].to_s unless control['impact'].nil?
|
@@ -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'
|
@@ -246,6 +246,7 @@ module Utils
|
|
246
246
|
control.add_tag(::Inspec::Object::Tag.new('stig_id', json_control['tags']['stig_id']))
|
247
247
|
control.add_tag(::Inspec::Object::Tag.new('fix_id', json_control['tags']['fix_id']))
|
248
248
|
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']))
|
249
250
|
control.add_tag(::Inspec::Object::Tag.new('nist', json_control['tags']['nist']))
|
250
251
|
control.add_tag(::Inspec::Object::Tag.new('cis_level', json_control['tags']['cis_level'])) unless json_control['tags']['cis_level'].blank?
|
251
252
|
control.add_tag(::Inspec::Object::Tag.new('cis_controls', json_control['tags']['cis_controls'])) unless json_control['tags']['cis_controls'].blank?
|
@@ -34,6 +34,7 @@ module Utils
|
|
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
|
@@ -74,6 +74,7 @@ module Utils
|
|
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
|
-
ident = HappyMapperTools::Benchmark::Ident.new
|
130
|
-
ident.system = 'https://public.cyber.mil/stigs/cci/'
|
131
|
-
ident.ident = identifier
|
132
|
-
ident
|
130
|
+
ident = HappyMapperTools::Benchmark::Ident.new identifier
|
133
131
|
end
|
134
132
|
end
|
135
133
|
|
@@ -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'
|
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.
|
4
|
+
version: 2.3.4
|
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:
|
14
|
+
date: 2021-04-16 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: colorize
|
@@ -201,20 +201,6 @@ dependencies:
|
|
201
201
|
- - ">="
|
202
202
|
- !ruby/object:Gem::Version
|
203
203
|
version: '0'
|
204
|
-
- !ruby/object:Gem::Dependency
|
205
|
-
name: codeclimate-test-reporter
|
206
|
-
requirement: !ruby/object:Gem::Requirement
|
207
|
-
requirements:
|
208
|
-
- - ">="
|
209
|
-
- !ruby/object:Gem::Version
|
210
|
-
version: '0'
|
211
|
-
type: :development
|
212
|
-
prerelease: false
|
213
|
-
version_requirements: !ruby/object:Gem::Requirement
|
214
|
-
requirements:
|
215
|
-
- - ">="
|
216
|
-
- !ruby/object:Gem::Version
|
217
|
-
version: '0'
|
218
204
|
- !ruby/object:Gem::Dependency
|
219
205
|
name: minitest
|
220
206
|
requirement: !ruby/object:Gem::Requirement
|
@@ -363,7 +349,7 @@ require_paths:
|
|
363
349
|
- lib
|
364
350
|
required_ruby_version: !ruby/object:Gem::Requirement
|
365
351
|
requirements:
|
366
|
-
- - "
|
352
|
+
- - ">="
|
367
353
|
- !ruby/object:Gem::Version
|
368
354
|
version: '2.5'
|
369
355
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
@@ -372,7 +358,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
372
358
|
- !ruby/object:Gem::Version
|
373
359
|
version: '0'
|
374
360
|
requirements: []
|
375
|
-
rubygems_version: 3.
|
361
|
+
rubygems_version: 3.2.15
|
376
362
|
signing_key:
|
377
363
|
specification_version: 4
|
378
364
|
summary: Converter utils for Inspec
|