abide_dev_utils 0.11.2 → 0.12.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/lib/abide_dev_utils/version.rb +1 -1
- data/lib/abide_dev_utils/xccdf.rb +193 -66
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d6f05434761a55369ffb231c1573ba627aa5c9ebef29d198ccfa16c422d05c48
|
4
|
+
data.tar.gz: 534968456e7eefca691fa6a3cdc46a170dc3ee12060261225fc35adadbc27d18
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b9907ee602b3367d4692b5c2102f0c769614f33d22980d5b8c0a14ea7dd15ac19947800eed399e6d68215ea2c21ab670b233209bf62e79399795fc5cc0ca68c2
|
7
|
+
data.tar.gz: edbeac0d044795300b42ac1ddd70623abc9c9e7d322ecf1e5d6356c8b4f875a31e31e02c96397b0bc9ee1a6a815fa342401916d9f87696f0ad68f0bd9d887b75
|
data/Gemfile.lock
CHANGED
@@ -16,6 +16,8 @@ module AbideDevUtils
|
|
16
16
|
case type.downcase
|
17
17
|
when 'cis'
|
18
18
|
Benchmark.new(xccdf_file).gen_map(**opts)
|
19
|
+
when 'stig'
|
20
|
+
Benchmark.new(xccdf_file).gen_map(**opts)
|
19
21
|
else
|
20
22
|
raise AbideDevUtils::Errors::UnsupportedXCCDFError, "XCCDF type #{type} is unsupported!"
|
21
23
|
end
|
@@ -51,26 +53,31 @@ module AbideDevUtils
|
|
51
53
|
module Common
|
52
54
|
XPATHS = {
|
53
55
|
benchmark: {
|
54
|
-
all: '
|
55
|
-
title: '
|
56
|
-
version: '
|
56
|
+
all: 'Benchmark',
|
57
|
+
title: 'Benchmark/title',
|
58
|
+
version: 'Benchmark/version'
|
57
59
|
},
|
58
60
|
cis: {
|
59
61
|
profiles: {
|
60
|
-
all: '
|
61
|
-
relative_title: './
|
62
|
-
relative_select: './
|
62
|
+
all: 'Benchmark/Profile',
|
63
|
+
relative_title: './title',
|
64
|
+
relative_select: './select'
|
63
65
|
}
|
64
66
|
}
|
65
67
|
}.freeze
|
66
68
|
CONTROL_PREFIX = /^[\d.]+_/.freeze
|
67
69
|
UNDERSCORED = /(\s|\(|\)|-|\.)/.freeze
|
70
|
+
CIS_TITLE_MARKER = 'CIS'
|
68
71
|
CIS_NEXT_GEN_WINDOWS = /[Nn]ext_[Gg]eneration_[Ww]indows_[Ss]ecurity/.freeze
|
69
72
|
CIS_CONTROL_NUMBER = /([0-9.]+[0-9]+)/.freeze
|
70
73
|
CIS_LEVEL_CODE = /(?:_|^)([Ll]evel_[0-9]|[Ll]1|[Ll]2|[NnBb][GgLl]|#{CIS_NEXT_GEN_WINDOWS})/.freeze
|
71
74
|
CIS_CONTROL_PARTS = /#{CIS_CONTROL_NUMBER}#{CIS_LEVEL_CODE}?_+([A-Za-z].*)/.freeze
|
72
75
|
CIS_PROFILE_PARTS = /#{CIS_LEVEL_CODE}[_-]+([A-Za-z].*)/.freeze
|
73
|
-
|
76
|
+
STIG_TITLE_MARKER = 'Security Technical Implementation Guide'
|
77
|
+
STIG_CONTROL_PARTS = /(V-[0-9]+)/.freeze
|
78
|
+
STIG_PROFILE_PARTS = /(MAC-\d+)_([A-Za-z].+)/.freeze
|
79
|
+
PROFILE_PARTS = /#{CIS_PROFILE_PARTS}|#{STIG_PROFILE_PARTS}/.freeze
|
80
|
+
CONTROL_PARTS = /#{CIS_CONTROL_PARTS}|#{STIG_CONTROL_PARTS}/.freeze
|
74
81
|
|
75
82
|
def xpath(path)
|
76
83
|
@xml.xpath(path)
|
@@ -120,21 +127,40 @@ module AbideDevUtils
|
|
120
127
|
end
|
121
128
|
|
122
129
|
def profile_parts(profile)
|
123
|
-
|
124
|
-
|
125
|
-
parts = control_profile_text(profile).match(CIS_PROFILE_PARTS)
|
130
|
+
parts = control_profile_text(profile).match(PROFILE_PARTS)
|
126
131
|
raise AbideDevUtils::Errors::ProfilePartsError, profile if parts.nil?
|
127
132
|
|
128
|
-
parts[1]
|
129
|
-
|
133
|
+
if parts[1]
|
134
|
+
# CIS profile
|
135
|
+
parts[1].gsub!(/[Ll]evel_/, 'L')
|
136
|
+
parts[1..2]
|
137
|
+
elsif parts[3]
|
138
|
+
# STIG profile
|
139
|
+
parts[3..4]
|
140
|
+
else
|
141
|
+
raise AbideDevUtils::Errors::ProfilePartsError, profile
|
142
|
+
end
|
130
143
|
end
|
131
144
|
|
132
|
-
def control_parts(control
|
133
|
-
mdata = control_profile_text(control).match(
|
145
|
+
def control_parts(control)
|
146
|
+
mdata = control_profile_text(control).match(CONTROL_PARTS)
|
134
147
|
raise AbideDevUtils::Errors::ControlPartsError, control if mdata.nil?
|
135
148
|
|
136
|
-
mdata[
|
137
|
-
|
149
|
+
if mdata[1]
|
150
|
+
# CIS control
|
151
|
+
mdata[1..3]
|
152
|
+
elsif mdata[4]
|
153
|
+
# STIG control
|
154
|
+
vuln_id = mdata[4]
|
155
|
+
group = @benchmark.xpath("Group[@id='#{vuln_id}']")
|
156
|
+
if group.xpath('Rule').length != 1
|
157
|
+
raise AbideDevUtils::Errors::ControlPartsError, control
|
158
|
+
end
|
159
|
+
rule_id = group.xpath('Rule/@id').first.value
|
160
|
+
return [vuln_id, rule_id]
|
161
|
+
else
|
162
|
+
raise AbideDevUtils::Errors::ControlPartsError, control
|
163
|
+
end
|
138
164
|
end
|
139
165
|
|
140
166
|
def control_profile_text(item)
|
@@ -151,14 +177,6 @@ module AbideDevUtils
|
|
151
177
|
end
|
152
178
|
end
|
153
179
|
|
154
|
-
def sorted_control_classes(raw_select_list, sort_key: :number)
|
155
|
-
raw_select_list.map { |x| Control.new(x) }.sort_by(&sort_key)
|
156
|
-
end
|
157
|
-
|
158
|
-
def sorted_profile_classes(raw_profile_list, sort_key: :title)
|
159
|
-
raw_profile_list.map { |x| Profile.new(x) }.sort_by(&sort_key)
|
160
|
-
end
|
161
|
-
|
162
180
|
def ==(other)
|
163
181
|
diff_properties.map { |x| send(x) } == other.diff_properties.map { |x| other.send(x) }
|
164
182
|
end
|
@@ -172,14 +190,17 @@ module AbideDevUtils
|
|
172
190
|
class Benchmark
|
173
191
|
include AbideDevUtils::XCCDF::Common
|
174
192
|
|
175
|
-
|
193
|
+
CIS_MAP_INDICES = %w[title hiera_title hiera_title_num number].freeze
|
194
|
+
STIG_MAP_INDICES = %w[vulnid ruleid].freeze
|
176
195
|
|
177
|
-
attr_reader :xml, :title, :version, :diff_properties
|
196
|
+
attr_reader :xml, :title, :version, :diff_properties, :benchmark
|
178
197
|
|
179
198
|
def initialize(path)
|
180
199
|
@xml = parse(path)
|
181
|
-
@
|
182
|
-
@
|
200
|
+
@xml.remove_namespaces!
|
201
|
+
@benchmark = xpath('Benchmark')
|
202
|
+
@title = xpath('Benchmark/title').text
|
203
|
+
@version = xpath('Benchmark/version').text
|
183
204
|
@diff_properties = %i[title version profiles]
|
184
205
|
end
|
185
206
|
|
@@ -188,7 +209,7 @@ module AbideDevUtils
|
|
188
209
|
end
|
189
210
|
|
190
211
|
def profiles
|
191
|
-
@profiles ||= Profiles.new(xpath('
|
212
|
+
@profiles ||= Profiles.new(xpath('Benchmark/Profile'), @benchmark)
|
192
213
|
end
|
193
214
|
|
194
215
|
def profile_levels
|
@@ -200,7 +221,7 @@ module AbideDevUtils
|
|
200
221
|
end
|
201
222
|
|
202
223
|
def controls
|
203
|
-
@controls ||= Controls.new(xpath('//
|
224
|
+
@controls ||= Controls.new(xpath('//select'))
|
204
225
|
end
|
205
226
|
|
206
227
|
def controls_by_profile_level(level_code)
|
@@ -212,15 +233,22 @@ module AbideDevUtils
|
|
212
233
|
end
|
213
234
|
|
214
235
|
def gen_map(dir: nil, type: 'cis', parent_key_prefix: '', version_output_dir: false, **_)
|
215
|
-
|
236
|
+
case type
|
237
|
+
when 'cis'
|
238
|
+
os, ver = facter_platform
|
239
|
+
indicies = CIS_MAP_INDICES
|
240
|
+
when 'stig'
|
241
|
+
os, ver = facter_benchmark
|
242
|
+
indicies = STIG_MAP_INDICES
|
243
|
+
end
|
216
244
|
output_path = [type, os, ver]
|
217
245
|
output_path.unshift(File.expand_path(dir)) if dir
|
218
246
|
output_path << version if version_output_dir
|
219
247
|
mapping_dir = File.expand_path(File.join(output_path))
|
220
248
|
parent_key_prefix = '' if parent_key_prefix.nil?
|
221
|
-
|
249
|
+
indicies.each_with_object({}) do |idx, h|
|
222
250
|
map_file_path = "#{mapping_dir}/#{idx}.yaml"
|
223
|
-
h[map_file_path] = map_indexed(index: idx, framework: type, key_prefix: parent_key_prefix)
|
251
|
+
h[map_file_path] = map_indexed(indicies: indicies, index: idx, framework: type, key_prefix: parent_key_prefix)
|
224
252
|
end
|
225
253
|
end
|
226
254
|
|
@@ -240,10 +268,10 @@ module AbideDevUtils
|
|
240
268
|
}
|
241
269
|
end
|
242
270
|
|
243
|
-
def map_indexed(index: 'title', framework: 'cis', key_prefix: '')
|
271
|
+
def map_indexed(indicies: [], index: 'title', framework: 'cis', key_prefix: '')
|
244
272
|
c_map = profiles.each_with_object({}) do |profile, obj|
|
245
273
|
obj[profile.level.downcase] = {} unless obj[profile.level.downcase].is_a?(Hash)
|
246
|
-
obj[profile.level.downcase][profile.title.downcase] = map_controls_hash(profile, index).sort_by { |k, _| k }.to_h
|
274
|
+
obj[profile.level.downcase][profile.title.downcase] = map_controls_hash(profile, indicies, index).sort_by { |k, _| k }.to_h
|
247
275
|
end
|
248
276
|
|
249
277
|
c_map['benchmark'] = { 'title' => title, 'version' => version }
|
@@ -252,8 +280,13 @@ module AbideDevUtils
|
|
252
280
|
{ mappings.join('::') => c_map }.to_yaml
|
253
281
|
end
|
254
282
|
|
283
|
+
def facter_benchmark
|
284
|
+
id = xpath('Benchmark/@id').text
|
285
|
+
id.split('_')[0..-2]
|
286
|
+
end
|
287
|
+
|
255
288
|
def facter_platform
|
256
|
-
cpe = xpath('
|
289
|
+
cpe = xpath('Benchmark/platform')[0]['idref'].split(':')
|
257
290
|
if cpe.length > 4
|
258
291
|
product_name = cpe[4].split('_')
|
259
292
|
product_version = cpe[5].split('.') unless cpe[5].nil?
|
@@ -283,8 +316,8 @@ module AbideDevUtils
|
|
283
316
|
hash.to_yaml
|
284
317
|
end
|
285
318
|
|
286
|
-
def
|
287
|
-
xpath("//
|
319
|
+
def resolve_cis_control_reference(control)
|
320
|
+
xpath("//Rule[@id='#{control.reference}']")
|
288
321
|
end
|
289
322
|
|
290
323
|
private
|
@@ -294,15 +327,15 @@ module AbideDevUtils
|
|
294
327
|
when 'hiera_title_num'
|
295
328
|
control.hiera_title(number_format: true)
|
296
329
|
when 'title'
|
297
|
-
|
330
|
+
resolve_cis_control_reference(control).xpath('./title').text
|
298
331
|
else
|
299
332
|
control.send(index.to_sym)
|
300
333
|
end
|
301
334
|
end
|
302
335
|
|
303
|
-
def map_controls_hash(profile, index)
|
336
|
+
def map_controls_hash(profile, indicies, index)
|
304
337
|
profile.controls.each_with_object({}) do |ctrl, hsh|
|
305
|
-
control_array =
|
338
|
+
control_array = indicies.each_with_object([]) do |idx_sym, ary|
|
306
339
|
next if idx_sym == index
|
307
340
|
|
308
341
|
item = format_map_control_index(idx_sym, ctrl)
|
@@ -319,13 +352,9 @@ module AbideDevUtils
|
|
319
352
|
end
|
320
353
|
end
|
321
354
|
|
322
|
-
def sorted_profile_classes(raw_profile_list, sort_key: :level)
|
323
|
-
raw_profile_list.map { |x| Profile.new(x) }.sort_by(&sort_key)
|
324
|
-
end
|
325
|
-
|
326
355
|
def find_profiles
|
327
356
|
profs = {}
|
328
|
-
xpath('
|
357
|
+
xpath('Benchmark/Profile').each do |profile|
|
329
358
|
level_code, name = profile_parts(profile['id'])
|
330
359
|
profs[name] = {} unless profs.key?(name)
|
331
360
|
profs[name][level_code] = profile
|
@@ -352,11 +381,66 @@ module AbideDevUtils
|
|
352
381
|
end
|
353
382
|
end
|
354
383
|
|
355
|
-
class
|
384
|
+
class XccdfObject
|
385
|
+
include AbideDevUtils::XCCDF::Common
|
386
|
+
|
387
|
+
def initialize(benchmark)
|
388
|
+
@benchmark = benchmark
|
389
|
+
@benchmark_type = benchmark_type
|
390
|
+
end
|
391
|
+
|
392
|
+
def controls_class
|
393
|
+
case @benchmark_type
|
394
|
+
when :cis
|
395
|
+
CisControls
|
396
|
+
when :stig
|
397
|
+
StigControls
|
398
|
+
else
|
399
|
+
raise AbideDevUtils::Errors::UnsupportedXCCDFError
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
def control_sort_key
|
404
|
+
case @benchmark_type
|
405
|
+
when :cis
|
406
|
+
:number
|
407
|
+
when :stig
|
408
|
+
:vulnid
|
409
|
+
else
|
410
|
+
raise AbideDevUtils::Errors::UnsupportedXCCDFError
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
def control_class
|
415
|
+
case @benchmark_type
|
416
|
+
when :cis
|
417
|
+
CisControl
|
418
|
+
when :stig
|
419
|
+
StigControl
|
420
|
+
else
|
421
|
+
raise AbideDevUtils::Errors::UnsupportedXCCDFError
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
private
|
426
|
+
|
427
|
+
def benchmark_type
|
428
|
+
title = @benchmark.at_xpath('title').text
|
429
|
+
if title.include?(STIG_TITLE_MARKER)
|
430
|
+
return :stig
|
431
|
+
elsif title.include?(CIS_TITLE_MARKER)
|
432
|
+
return :cis
|
433
|
+
end
|
434
|
+
raise AbideDevUtils::Errors::UnsupportedXCCDFError, "XCCDF type is unsupported!"
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
class ObjectContainer < XccdfObject
|
356
439
|
include AbideDevUtils::XCCDF::Common
|
357
440
|
|
358
|
-
def initialize(list, object_creation_method, *args, **kwargs)
|
359
|
-
|
441
|
+
def initialize(list, object_creation_method, benchmark, *args, **kwargs)
|
442
|
+
super(benchmark)
|
443
|
+
@object_list = send(object_creation_method.to_sym, list, benchmark, *args, **kwargs)
|
360
444
|
@searchable = []
|
361
445
|
end
|
362
446
|
|
@@ -399,6 +483,14 @@ module AbideDevUtils
|
|
399
483
|
|
400
484
|
private
|
401
485
|
|
486
|
+
def sorted_control_classes(raw_select_list, benchmark)
|
487
|
+
raw_select_list.map { |x| control_class.new(x, benchmark) }.sort_by(&control_sort_key)
|
488
|
+
end
|
489
|
+
|
490
|
+
def sorted_profile_classes(raw_profile_list, benchmark)
|
491
|
+
raw_profile_list.map { |x| Profile.new(x, benchmark) }.sort_by(&:title)
|
492
|
+
end
|
493
|
+
|
402
494
|
def resolve_hash_key(obj)
|
403
495
|
return obj.send(:raw_title) unless defined?(@hash_key)
|
404
496
|
|
@@ -419,8 +511,8 @@ module AbideDevUtils
|
|
419
511
|
end
|
420
512
|
|
421
513
|
class Profiles < ObjectContainer
|
422
|
-
def initialize(list)
|
423
|
-
super(list, :sorted_profile_classes)
|
514
|
+
def initialize(list, benchmark)
|
515
|
+
super(list, :sorted_profile_classes, benchmark)
|
424
516
|
searchable! :level, :title
|
425
517
|
index! :title
|
426
518
|
hash_key! :level, :title
|
@@ -443,9 +535,34 @@ module AbideDevUtils
|
|
443
535
|
end
|
444
536
|
end
|
445
537
|
|
446
|
-
class
|
447
|
-
def initialize(list)
|
448
|
-
super(list, :sorted_control_classes)
|
538
|
+
class StigControls < ObjectContainer
|
539
|
+
def initialize(list, benchmark)
|
540
|
+
super(list, :sorted_control_classes, benchmark)
|
541
|
+
searchable! :vulnid, :ruleid
|
542
|
+
index! :vulnid
|
543
|
+
hash_key! :vulnid
|
544
|
+
end
|
545
|
+
|
546
|
+
def vulnids
|
547
|
+
@vulnids ||= @object_list.map(&:vulnid).sort
|
548
|
+
end
|
549
|
+
|
550
|
+
def ruleids
|
551
|
+
@ruleids ||= @object_list.map(&:ruleid).sort
|
552
|
+
end
|
553
|
+
|
554
|
+
def include_vulnid?(item)
|
555
|
+
@object_list.map(&:vulnid).include?(item)
|
556
|
+
end
|
557
|
+
|
558
|
+
def include_ruleid?(item)
|
559
|
+
@object_list.map(&:ruleid).include?(item)
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
563
|
+
class CisControls < ObjectContainer
|
564
|
+
def initialize(list, benchmark)
|
565
|
+
super(list, :sorted_control_classes, benchmark)
|
449
566
|
searchable! :level, :title, :number
|
450
567
|
index! :number
|
451
568
|
hash_key! :number
|
@@ -476,10 +593,11 @@ module AbideDevUtils
|
|
476
593
|
end
|
477
594
|
end
|
478
595
|
|
479
|
-
class XccdfElement
|
596
|
+
class XccdfElement < XccdfObject
|
480
597
|
include AbideDevUtils::XCCDF::Common
|
481
598
|
|
482
|
-
def initialize(element)
|
599
|
+
def initialize(element, benchmark)
|
600
|
+
super(benchmark)
|
483
601
|
@xml = element
|
484
602
|
@element_type = self.class.name.split('::').last.downcase
|
485
603
|
@raw_title = control_profile_text(element)
|
@@ -501,11 +619,12 @@ module AbideDevUtils
|
|
501
619
|
end
|
502
620
|
|
503
621
|
def reference
|
504
|
-
@reference ||= @element_type
|
622
|
+
@reference ||= @element_type.include?('control') ? @xml['idref'] : @xml['id']
|
505
623
|
end
|
506
624
|
|
507
625
|
def hiera_title(**opts)
|
508
|
-
|
626
|
+
e_type = @element_type.include?('control') ? 'control' : 'profile'
|
627
|
+
send("normalize_#{e_type}_name".to_sym, @xml, **opts)
|
509
628
|
end
|
510
629
|
|
511
630
|
private
|
@@ -527,19 +646,27 @@ module AbideDevUtils
|
|
527
646
|
end
|
528
647
|
|
529
648
|
class Profile < XccdfElement
|
530
|
-
def initialize(profile)
|
531
|
-
super(profile)
|
649
|
+
def initialize(profile, benchmark)
|
650
|
+
super(profile, benchmark)
|
532
651
|
@level, @title = profile_parts(control_profile_text(profile))
|
533
|
-
@plain_text_title = @xml.xpath('./
|
534
|
-
@controls =
|
652
|
+
@plain_text_title = @xml.xpath('./title').text
|
653
|
+
@controls = controls_class.new(xpath('./select'), benchmark)
|
535
654
|
properties :title, :level, :plain_text_title, controls: :to_h
|
536
655
|
end
|
537
656
|
end
|
538
657
|
|
539
|
-
class
|
540
|
-
def initialize(control,
|
541
|
-
super(control)
|
542
|
-
@
|
658
|
+
class StigControl < XccdfElement
|
659
|
+
def initialize(control, benchmark)
|
660
|
+
super(control, benchmark)
|
661
|
+
@vulnid, @ruleid = control_parts(control_profile_text(control))
|
662
|
+
properties :vulnid, :ruleid
|
663
|
+
end
|
664
|
+
end
|
665
|
+
|
666
|
+
class CisControl < XccdfElement
|
667
|
+
def initialize(control, benchmark)
|
668
|
+
super(control, benchmark)
|
669
|
+
@number, @level, @title = control_parts(control_profile_text(control))
|
543
670
|
properties :number, :level, :title
|
544
671
|
end
|
545
672
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: abide_dev_utils
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.12.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- abide-team
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-08-
|
11
|
+
date: 2022-08-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|