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 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