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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 85fb0e453df008a48284f03fe59bd1577b733498ce030039e215bf1863e1767c
4
- data.tar.gz: 9a89ac46e8506ea059044006a5c50d8132847fa2a8f9a6b726174ed51ab6421c
3
+ metadata.gz: d6f05434761a55369ffb231c1573ba627aa5c9ebef29d198ccfa16c422d05c48
4
+ data.tar.gz: 534968456e7eefca691fa6a3cdc46a170dc3ee12060261225fc35adadbc27d18
5
5
  SHA512:
6
- metadata.gz: 9f221ac5c9c15db1414005ca4017dda7a9f848d8b7586d4b8a3ede9ecaf5fc8e35187ab2f172e127cad92a1d483befe31fa17278279b54b33abcc1d25f7a21a6
7
- data.tar.gz: b9f3e5553a03faed668600c94b44759ee4554165e20722b7c6d5108add327ba23a6986e707034647b3eed400e6217b032eb058e537a74f26d11a004cf309bcfd
6
+ metadata.gz: b9907ee602b3367d4692b5c2102f0c769614f33d22980d5b8c0a14ea7dd15ac19947800eed399e6d68215ea2c21ab670b233209bf62e79399795fc5cc0ca68c2
7
+ data.tar.gz: edbeac0d044795300b42ac1ddd70623abc9c9e7d322ecf1e5d6356c8b4f875a31e31e02c96397b0bc9ee1a6a815fa342401916d9f87696f0ad68f0bd9d887b75
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- abide_dev_utils (0.11.2)
4
+ abide_dev_utils (0.12.1)
5
5
  amatch (~> 0.4)
6
6
  cmdparse (~> 3.0)
7
7
  facterdb (>= 1.18)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AbideDevUtils
4
- VERSION = "0.11.2"
4
+ VERSION = "0.12.1"
5
5
  end
@@ -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: 'xccdf:Benchmark',
55
- title: 'xccdf:Benchmark/xccdf:title',
56
- version: 'xccdf:Benchmark/xccdf:version'
56
+ all: 'Benchmark',
57
+ title: 'Benchmark/title',
58
+ version: 'Benchmark/version'
57
59
  },
58
60
  cis: {
59
61
  profiles: {
60
- all: 'xccdf:Benchmark/xccdf:Profile',
61
- relative_title: './xccdf:title',
62
- relative_select: './xccdf: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
- STIG_PROFILE_PARTS = /(STIG)/.freeze
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
- return ['STIG', ''] if profile == 'STIG'
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].gsub!(/[Ll]evel_/, 'L')
129
- parts[1..2]
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, parent_level: nil)
133
- mdata = control_profile_text(control).match(CIS_CONTROL_PARTS)
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[2] = parent_level unless parent_level.nil?
137
- mdata[1..3]
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
- MAP_INDICES = %w[title hiera_title hiera_title_num number].freeze
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
- @title = xpath('xccdf:Benchmark/xccdf:title').text
182
- @version = xpath('xccdf:Benchmark/xccdf:version').text
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('xccdf:Benchmark/xccdf:Profile'))
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('//xccdf:select'))
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
- os, ver = facter_platform
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
- MAP_INDICES.each_with_object({}) do |idx, h|
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('xccdf:Benchmark/xccdf:platform')[0]['idref'].split(':')
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 resolve_control_reference(control)
287
- xpath("//xccdf:Rule[@id='#{control.reference}']")
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
- resolve_control_reference(control).xpath('./xccdf:title').text
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 = MAP_INDICES.each_with_object([]) do |idx_sym, ary|
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('xccdf:Benchmark/xccdf:Profile').each do |profile|
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 ObjectContainer
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
- @object_list = send(object_creation_method.to_sym, list, *args, **kwargs)
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 Controls < ObjectContainer
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 == 'control' ? @xml['idref'] : @xml['id']
622
+ @reference ||= @element_type.include?('control') ? @xml['idref'] : @xml['id']
505
623
  end
506
624
 
507
625
  def hiera_title(**opts)
508
- send("normalize_#{@element_type}_name".to_sym, @xml, **opts)
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('./xccdf:title').text
534
- @controls = Controls.new(xpath('./xccdf:select'))
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 Control < XccdfElement
540
- def initialize(control, parent_level: nil)
541
- super(control)
542
- @number, @level, @title = control_parts(control_profile_text(control), parent_level: parent_level)
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.11.2
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-15 00:00:00.000000000 Z
11
+ date: 2022-08-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri