abide_dev_utils 0.11.2 → 0.12.1
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/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
|