abide_dev_utils 0.14.1 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'digest'
4
- require_relative './objects/digest_object'
3
+ require_relative './objects/diffable_object'
5
4
  require_relative './objects/numbered_object'
6
5
  require_relative './helpers'
7
6
 
@@ -12,19 +11,28 @@ module AbideDevUtils
12
11
  module Objects
13
12
  # Base class for XCCDF element objects
14
13
  class ElementBase
15
- include AbideDevUtils::XCCDF::Parser::Objects::DigestObject
16
- include AbideDevUtils::XCCDF::Parser::Helpers::ElementChildren
14
+ include Comparable
15
+ include AbideDevUtils::XCCDF::Parser::Objects::DiffableObject
17
16
  include AbideDevUtils::XCCDF::Parser::Helpers::XPath
18
17
  extend AbideDevUtils::XCCDF::Parser::Helpers::XPath
19
- attr_reader :children, :child_labels, :link_labels
20
18
 
21
- def initialize(*_args, **_kwargs)
19
+ UNICODE_SYMBOLS = {
20
+ vertical: "\u2502",
21
+ horizontal: "\u2500",
22
+ tee: "\u251C",
23
+ corner: "\u2514"
24
+ }.freeze
25
+
26
+ attr_reader :children, :child_labels, :links, :link_labels, :parent
27
+
28
+ def initialize(*_args, parent_node: nil, **_kwargs)
29
+ @parent = parent_node
22
30
  @children = []
23
31
  @links = []
24
32
  @link_labels = []
25
33
  @child_labels = []
26
34
  @label_method_values = {}
27
- exclude_from_digest(%i[@digest @children @child_labels @label @exclude_from_digest @label_method_values])
35
+ @similarity_methods = []
28
36
  end
29
37
 
30
38
  # For subclasses that are associated with a specific
@@ -57,41 +65,14 @@ module AbideDevUtils
57
65
  @label_method_values
58
66
  end
59
67
 
60
- # Allows access to child objects by label
61
- def method_missing(method_name, *args, &block)
62
- m_name_string = method_name.to_s.downcase
63
- return @label_method_values[m_name_string] if @label_method_values.key?(m_name_string)
64
-
65
- label_str = m_name_string.start_with?('linked_') ? m_name_string.split('_')[1..].join('_') : m_name_string
66
- if m_name_string.start_with?('linked_') && @link_labels.include?(label_str)
67
- found = @links.select { |link| link.label == label_str }
68
- @label_method_values["linked_#{label_str}"] = if found.length == 1
69
- found.first
70
- else
71
- found
72
- end
73
- @label_method_values["linked_#{label_str}"]
74
- elsif @child_labels.include?(label_str)
75
- found = @children.select { |child| child.label == label_str }
76
- @label_method_values[label_str] = if found.length == 1
77
- found.first
78
- else
79
- found
80
- end
81
- @label_method_values[label_str]
82
- elsif search_children.respond_to?(method_name)
83
- search_children.send(method_name, *args, &block)
84
- else
85
- super
86
- end
68
+ def inspect
69
+ "<#{self.class}:#{object_id}:\"#{self}\">"
87
70
  end
88
71
 
89
- def respond_to_missing?(method_name, include_private = false)
90
- m_name_string = method_name.to_s.downcase
91
- label_str = m_name_string.start_with?('linked_') ? m_name_string.split('_')[1..].join('_') : m_name_string
92
- (m_name_string.start_with?('linked_') && @link_labels.include?(label_str)) ||
93
- @child_labels.include?(label_str) ||
94
- super
72
+ def <=>(other)
73
+ return nil unless other.is_a?(self.class)
74
+
75
+ label <=> other.label
95
76
  end
96
77
 
97
78
  def label
@@ -108,30 +89,118 @@ module AbideDevUtils
108
89
  @label
109
90
  end
110
91
 
92
+ def find_similarity(other)
93
+ return [] unless other.is_a?(self.class)
94
+
95
+ @similarity_methods.each_with_object([]) do |method, ary|
96
+ val = send(method)
97
+ other_val = other.send(method)
98
+ ary << [method, val, other_val, val.eql?(other_val)]
99
+ end
100
+ end
101
+
111
102
  def add_link(object)
103
+ define_child_method(object.label, linked: true)
112
104
  @links << object
113
105
  @link_labels << object.label unless @link_labels.include?(object.label)
114
106
  end
115
107
 
116
108
  def add_links(objects)
117
- objects.each { |object| add_object_as_child(object) }
109
+ objects.each { |object| add_link(object) }
110
+ end
111
+
112
+ def root?
113
+ parent.nil?
114
+ end
115
+
116
+ def root
117
+ return self if root?
118
+
119
+ parent.root
120
+ end
121
+
122
+ def leaf?
123
+ children.empty?
124
+ end
125
+
126
+ def siblings
127
+ return [] if root?
128
+
129
+ parent.children.reject { |child| child == self }
130
+ end
131
+
132
+ def ancestors
133
+ return [] if root?
134
+
135
+ [parent] + parent.ancestors
136
+ end
137
+
138
+ def descendants
139
+ return [] if leaf?
140
+
141
+ children + children.map(&:descendants).flatten
142
+ end
143
+
144
+ def depth
145
+ return 0 if root?
146
+
147
+ 1 + parent.depth
148
+ end
149
+
150
+ def print_tree
151
+ puts tree_string_parts.join("\n")
152
+ end
153
+
154
+ protected
155
+
156
+ def tree_string_parts(indent = 0, parts = [])
157
+ parts << if indent.zero?
158
+ "#{UNICODE_SYMBOLS[:vertical]} #{inspect}".encode('utf-8')
159
+ elsif !children.empty?
160
+ "#{UNICODE_SYMBOLS[:tee]}#{UNICODE_SYMBOLS[:horizontal] * indent} #{inspect}".encode('utf-8')
161
+ else
162
+ "#{UNICODE_SYMBOLS[:corner]}#{UNICODE_SYMBOLS[:horizontal] * indent} #{inspect}".encode('utf-8')
163
+ end
164
+ children.each { |c| c.tree_string_parts(indent + 2, parts) } unless children.empty?
165
+ parts
118
166
  end
119
167
 
120
168
  private
121
169
 
170
+ def similarity_methods(*methods)
171
+ @similarity_methods = methods
172
+ end
173
+
122
174
  def with_safe_methods(default: nil)
123
175
  yield
124
176
  rescue NoMethodError
125
177
  default
126
178
  end
127
179
 
180
+ def define_child_method(child_label, linked: false)
181
+ method_name = linked ? "linked_#{child_label}" : child_label
182
+ self.class.define_method method_name do
183
+ found = if method_name.start_with?('linked_')
184
+ @links.select { |l| l.label == child_label }
185
+ else
186
+ children.select { |c| c.label == child_label }
187
+ end
188
+ if found.length == 1
189
+ found.first
190
+ else
191
+ found
192
+ end
193
+ end
194
+ end
195
+
128
196
  def add_child(klass, element, *args, **kwargs)
129
197
  return if element.nil?
130
198
 
131
199
  real_element = klass.xpath.nil? ? element : find_element.at_xpath(element, klass.xpath)
132
200
  return if real_element.nil?
133
201
 
134
- obj = new_object(klass, real_element, *args, **kwargs)
202
+ obj = new_object(klass, real_element, *args, parent_node: self, **kwargs)
203
+ define_child_method(obj.label)
135
204
  @children << obj
136
205
  @child_labels << obj.label unless @child_labels.include?(obj.label)
137
206
  rescue StandardError => e
@@ -149,7 +218,8 @@ module AbideDevUtils
149
218
  return if real_elements.nil?
150
219
 
151
220
  real_elements.each do |e|
152
- obj = new_object(klass, e, *args, **kwargs)
221
+ obj = new_object(klass, e, *args, parent_node: self, **kwargs)
222
+ define_child_method(obj.label)
153
223
  @children << obj
154
224
  @child_labels << obj.label unless @child_labels.include?(obj.label)
155
225
  end
@@ -170,12 +240,20 @@ module AbideDevUtils
170
240
  class ShortText < ElementBase
171
241
  attr_reader :text
172
242
 
173
- def initialize(element)
243
+ def initialize(element, parent_node: nil)
174
244
  super
175
245
  text = element.respond_to?(:text) ? element.text : element
176
246
  @text = text.to_s
177
247
  end
178
248
 
249
+ def eql?(other)
250
+ text == other.text
251
+ end
252
+
253
+ def hash
254
+ text.hash
255
+ end
256
+
179
257
  def to_s
180
258
  @text
181
259
  end
@@ -185,11 +263,20 @@ module AbideDevUtils
185
263
  class LongText < ElementBase
186
264
  attr_reader :text
187
265
 
188
- def initialize(element)
266
+ def initialize(element, parent_node: nil)
189
267
  super
190
268
  text = element.respond_to?(:text) ? element.text : element
191
269
  @text = text.to_s
192
270
  @string_text = text.to_s.tr("\n", ' ').gsub(/\s+/, ' ')
271
+ similarity_methods :to_s
272
+ end
273
+
274
+ def eql?(other)
275
+ @string_text == other.to_s
276
+ end
277
+
278
+ def hash
279
+ @string_text.hash
193
280
  end
194
281
 
195
282
  def to_s
@@ -201,10 +288,19 @@ module AbideDevUtils
201
288
  class AttributeValue < ElementBase
202
289
  attr_reader :attribute, :value
203
290
 
204
- def initialize(element, attribute)
291
+ def initialize(element, attribute, parent_node: nil)
205
292
  super
206
293
  @attribute = attribute
207
294
  @value = element[attribute]
295
+ similarity_methods :attribute, :value
296
+ end
297
+
298
+ def eql?(other)
299
+ @attribute == other.attribute && @value == other.value
300
+ end
301
+
302
+ def hash
303
+ to_s.hash
208
304
  end
209
305
 
210
306
  def to_s
@@ -214,7 +310,7 @@ module AbideDevUtils
214
310
 
215
311
  # Class for an XCCDF element title
216
312
  class Title < ElementBase
217
- def initialize(element)
313
+ def initialize(element, parent_node: nil)
218
314
  super
219
315
  add_child(ShortText, element)
220
316
  end
@@ -223,14 +319,22 @@ module AbideDevUtils
223
319
  'title'
224
320
  end
225
321
 
322
+ def eql?(other)
323
+ to_s == other.to_s
324
+ end
325
+
326
+ def hash
327
+ to_s.hash
328
+ end
329
+
226
330
  def to_s
227
- search_children.find_child_by_class(ShortText).to_s
331
+ text.to_s
228
332
  end
229
333
  end
230
334
 
231
335
  # Class for an XCCDF element description
232
336
  class Description < ElementBase
233
- def initialize(element)
337
+ def initialize(element, parent_node: nil)
234
338
  super
235
339
  add_child(LongText, element)
236
340
  end
@@ -239,8 +343,16 @@ module AbideDevUtils
239
343
  'description'
240
344
  end
241
345
 
346
+ def eql?(other)
347
+ to_s == other.to_s
348
+ end
349
+
350
+ def hash
351
+ to_s.hash
352
+ end
353
+
242
354
  def to_s
243
- search_children.find_child_by_class(LongText).to_s
355
+ text.to_s
244
356
  end
245
357
  end
246
358
 
@@ -248,10 +360,24 @@ module AbideDevUtils
248
360
  class ElementWithId < ElementBase
249
361
  attr_reader :id
250
362
 
251
- def initialize(element)
363
+ def initialize(element, parent_node: nil)
252
364
  super
253
365
  add_child(AttributeValue, element, 'id')
254
- @id = search_children.find_child_by_attribute('id').value.to_s
366
+ @id = descendants.find { |d| d.label == 'id' }.value
367
+ end
368
+
369
+ def <=>(other)
370
+ return nil unless other.instance_of?(self.class)
371
+
372
+ @id <=> other.id.value
373
+ end
374
+
375
+ def eql?(other)
376
+ @id == other.id.value
377
+ end
378
+
379
+ def hash
380
+ @id.hash
255
381
  end
256
382
 
257
383
  def to_s
@@ -263,10 +389,24 @@ module AbideDevUtils
263
389
  class ElementWithIdref < ElementBase
264
390
  attr_reader :idref
265
391
 
266
- def initialize(element)
392
+ def initialize(element, parent_node: nil)
267
393
  super
268
394
  add_child(AttributeValue, element, 'idref')
269
- @idref = search_children.find_child_by_attribute('idref').value.to_s
395
+ @idref = descendants.find { |d| d.label == 'idref' }.value
396
+ end
397
+
398
+ def <=>(other)
399
+ return nil unless other.instance_of?(self.class)
400
+
401
+ @idref <=> other.idref.value
402
+ end
403
+
404
+ def eql?(other)
405
+ @idref == other.idref.value
406
+ end
407
+
408
+ def hash
409
+ @idref.hash
270
410
  end
271
411
 
272
412
  def to_s
@@ -276,9 +416,14 @@ module AbideDevUtils
276
416
 
277
417
  # Class for an XCCDF select element
278
418
  class XccdfSelect < ElementWithIdref
279
- def initialize(element)
419
+ attr_reader :number, :title
420
+
421
+ def initialize(element, parent_node: nil)
280
422
  super
281
423
  add_child(AttributeValue, element, 'selected')
424
+ @number = to_s[/([0-9]+\.)+[0-9]+|([0-9]+)/]
425
+ @title = to_s[/[A-Z].*$/]
426
+ similarity_methods :number, :title
282
427
  end
283
428
 
284
429
  def self.xpath
@@ -288,11 +433,12 @@ module AbideDevUtils
288
433
 
289
434
  # Class for XCCDF profile
290
435
  class Profile < ElementWithId
291
- def initialize(element)
436
+ def initialize(element, parent_node: nil)
292
437
  super
293
438
  add_child(Title, element)
294
439
  add_child(Description, element)
295
440
  add_children(XccdfSelect, element)
441
+ similarity_methods :id, :title, :level, :description, :xccdf_select
296
442
  end
297
443
 
298
444
  def level
@@ -313,13 +459,14 @@ module AbideDevUtils
313
459
  include AbideDevUtils::XCCDF::Parser::Objects::NumberedObject
314
460
  attr_reader :number
315
461
 
316
- def initialize(element)
462
+ def initialize(element, parent_node: nil)
317
463
  super
318
464
  @number = to_s[/group_([0-9]+\.)+[0-9]+|group_([0-9]+)/]&.gsub(/group_/, '')
319
465
  add_child(Title, element)
320
466
  add_child(Description, element)
321
467
  add_children(Group, element)
322
468
  add_children(Rule, element)
469
+ similarity_methods :title, :number
323
470
  end
324
471
 
325
472
  def self.xpath
@@ -329,7 +476,7 @@ module AbideDevUtils
329
476
 
330
477
  # Class for XCCDF check-export
331
478
  class CheckExport < ElementBase
332
- def initialize(element)
479
+ def initialize(element, parent_node: nil)
333
480
  super
334
481
  add_child(AttributeValue, element, 'export-name')
335
482
  add_child(AttributeValue, element, 'value-id')
@@ -339,14 +486,22 @@ module AbideDevUtils
339
486
  'check-export'
340
487
  end
341
488
 
489
+ def eql?(other)
490
+ to_s == other.to_s
491
+ end
492
+
493
+ def hash
494
+ to_s.hash
495
+ end
496
+
342
497
  def to_s
343
- [search_children.find_child_by_attribute('export-name').to_s, search_children.find_child_by_attribute('value-id').to_s].join('|')
498
+ [export_name.to_s, value_id.to_s].join('|')
344
499
  end
345
500
  end
346
501
 
347
502
  # Class for XCCDF check-content-ref
348
503
  class CheckContentRef < ElementBase
349
- def initialize(element)
504
+ def initialize(element, parent_node: nil)
350
505
  super
351
506
  add_child(AttributeValue, element, 'href')
352
507
  add_child(AttributeValue, element, 'name')
@@ -356,20 +511,36 @@ module AbideDevUtils
356
511
  'check-content-ref'
357
512
  end
358
513
 
514
+ def eql?(other)
515
+ to_s == other.to_s
516
+ end
517
+
518
+ def hash
519
+ to_s.hash
520
+ end
521
+
359
522
  def to_s
360
- [search_children.find_child_by_attribute('href').to_s, search_children.find_child_by_attribute('name').to_s].join('|')
523
+ [href.to_s, name.to_s].join('|')
361
524
  end
362
525
  end
363
526
 
364
527
  # Class for XCCDF check
365
528
  class Check < ElementBase
366
- def initialize(element)
529
+ def initialize(element, parent_node: nil)
367
530
  super
368
531
  add_child(AttributeValue, element, 'system')
369
532
  add_children(CheckExport, element)
370
533
  add_children(CheckContentRef, element)
371
534
  end
372
535
 
536
+ def eql?(other)
537
+ @children.map(&:to_s).join == other.children.map(&:to_s).join
538
+ end
539
+
540
+ def hash
541
+ @children.map(&:to_s).join.hash
542
+ end
543
+
373
544
  def self.xpath
374
545
  'check'
375
546
  end
@@ -377,12 +548,22 @@ module AbideDevUtils
377
548
 
378
549
  # Class for XCCDF Ident ControlURI element
379
550
  class ControlURI < ElementBase
380
- def initialize(element)
551
+ attr_reader :namespace, :value
552
+
553
+ def initialize(element, parent_node: nil)
381
554
  super
382
555
  @namespace = element.attributes['controlURI'].namespace.prefix
383
556
  @value = element.attributes['controlURI'].value
384
557
  end
385
558
 
559
+ def eql?(other)
560
+ to_s == other.to_s
561
+ end
562
+
563
+ def hash
564
+ to_s.hash
565
+ end
566
+
386
567
  def to_s
387
568
  [label, @namespace, @value].join(':')
388
569
  end
@@ -390,12 +571,22 @@ module AbideDevUtils
390
571
 
391
572
  # Class for XCCDF Ident System element
392
573
  class System < ElementBase
393
- def initialize(element)
574
+ attr_reader :system, :text
575
+
576
+ def initialize(element, parent_node: nil)
394
577
  super
395
578
  @system = element.attributes['system'].value
396
579
  @text = element.text
397
580
  end
398
581
 
582
+ def eql?(other)
583
+ to_s == other.to_s
584
+ end
585
+
586
+ def hash
587
+ to_s.hash
588
+ end
589
+
399
590
  def to_s
400
591
  [label, @system, @text].join(':')
401
592
  end
@@ -403,12 +594,20 @@ module AbideDevUtils
403
594
 
404
595
  # Class for XCCDF rule ident
405
596
  class Ident < ElementBase
406
- def initialize(element)
597
+ def initialize(element, parent_node: nil)
407
598
  super
408
599
  with_safe_methods { add_child(ControlURI, element) }
409
600
  with_safe_methods { add_child(System, element) }
410
601
  end
411
602
 
603
+ def eql?(other)
604
+ to_s == other.to_s
605
+ end
606
+
607
+ def hash
608
+ to_s.hash
609
+ end
610
+
412
611
  def self.xpath
413
612
  'ident'
414
613
  end
@@ -422,12 +621,20 @@ module AbideDevUtils
422
621
  class ComplexCheck < ElementBase
423
622
  attr_reader :operator, :check
424
623
 
425
- def initialize(element, parent: nil)
624
+ def initialize(element, parent: nil, parent_node: nil)
426
625
  super
427
626
  add_child(AttributeValue, element, 'operator')
428
627
  add_children(Check, element)
429
628
  end
430
629
 
630
+ def eql?(other)
631
+ @children.map(&:to_s).join == other.children.map(&:to_s).join
632
+ end
633
+
634
+ def hash
635
+ @children.map(&:to_s).join.hash
636
+ end
637
+
431
638
  def self.xpath
432
639
  'complex-check'
433
640
  end
@@ -435,7 +642,7 @@ module AbideDevUtils
435
642
 
436
643
  # Class for XCCDF rule metadata cis_controls framework safeguard
437
644
  class MetadataCisControlsFrameworkSafeguard < ElementBase
438
- def initialize(element)
645
+ def initialize(element, parent_node: nil)
439
646
  super
440
647
  add_child(ShortText, element['title'])
441
648
  add_child(ShortText, element['urn'])
@@ -444,6 +651,14 @@ module AbideDevUtils
444
651
  add_child(ShortText, find_element.at_xpath(element, 'security_function').text)
445
652
  end
446
653
 
654
+ def eql?(other)
655
+ @children.map(&:to_s).join == other.children.map(&:to_s).join
656
+ end
657
+
658
+ def hash
659
+ @children.map(&:hash).join.hash
660
+ end
661
+
447
662
  def self.xpath
448
663
  'safeguard'
449
664
  end
@@ -466,12 +681,20 @@ module AbideDevUtils
466
681
 
467
682
  # Class for XCCDF rule metadata cis_controls framework
468
683
  class MetadataCisControlsFramework < ElementBase
469
- def initialize(element)
684
+ def initialize(element, parent_node: nil)
470
685
  super
471
686
  add_child(AttributeValue, element, 'urn')
472
687
  add_children(MetadataCisControlsFrameworkSafeguard, element)
473
688
  end
474
689
 
690
+ def eql?(other)
691
+ @children.map(&:to_s).join == other.children.map(&:to_s).join
692
+ end
693
+
694
+ def hash
695
+ @children.map(&:hash).join.hash
696
+ end
697
+
475
698
  def self.xpath
476
699
  'framework'
477
700
  end
@@ -483,12 +706,20 @@ module AbideDevUtils
483
706
 
484
707
  # Class for XCCDF metadata cis_controls element
485
708
  class MetadataCisControls < ElementBase
486
- def initialize(element, parent: nil)
709
+ def initialize(element, parent: nil, parent_node: nil)
487
710
  super
488
711
  add_child(AttributeValue, element, 'controls')
489
712
  add_children(MetadataCisControlsFramework, element)
490
713
  end
491
714
 
715
+ def eql?(other)
716
+ @children.map(&:to_s).join == other.children.map(&:to_s).join
717
+ end
718
+
719
+ def hash
720
+ @children.map(&:hash).join.hash
721
+ end
722
+
492
723
  def self.xpath
493
724
  'cis_controls'
494
725
  end
@@ -503,11 +734,19 @@ module AbideDevUtils
503
734
 
504
735
  # Class for XCCDF rule metadata element
505
736
  class Metadata < ElementBase
506
- def initialize(element, parent: nil)
737
+ def initialize(element, parent: nil, parent_node: nil)
507
738
  super
508
739
  add_children(MetadataCisControls, element)
509
740
  end
510
741
 
742
+ def eql?(other)
743
+ @children.map(&:to_s).join == other.children.map(&:to_s).join
744
+ end
745
+
746
+ def hash
747
+ @children.map(&:hash).join.hash
748
+ end
749
+
511
750
  def self.xpath
512
751
  'metadata'
513
752
  end
@@ -515,13 +754,17 @@ module AbideDevUtils
515
754
 
516
755
  # Class for XCCDF Rule child element Rationale
517
756
  class Rationale < ElementBase
518
- def initialize(element)
757
+ def initialize(element, parent_node: nil)
519
758
  super
520
759
  add_child(LongText, element)
521
760
  end
522
761
 
523
- def digest
524
- @digest ||= find_child_by_class(LongText).digest
762
+ def eql?(other)
763
+ to_s == other.to_s
764
+ end
765
+
766
+ def hash
767
+ to_s.hash
525
768
  end
526
769
 
527
770
  def self.xpath
@@ -535,13 +778,17 @@ module AbideDevUtils
535
778
 
536
779
  # Class for XCCDF Rule child element Fixtext
537
780
  class Fixtext < ElementBase
538
- def initialize(element)
781
+ def initialize(element, parent_node: nil)
539
782
  super
540
783
  add_child(LongText, element)
541
784
  end
542
785
 
543
- def digest
544
- @digest ||= search_children.find_child_by_class(LongText).digest
786
+ def eql?(other)
787
+ to_s == other.to_s
788
+ end
789
+
790
+ def hash
791
+ to_s.hash
545
792
  end
546
793
 
547
794
  def self.xpath
@@ -549,7 +796,7 @@ module AbideDevUtils
549
796
  end
550
797
 
551
798
  def to_s
552
- search_children.find_child_by_class(LongText).to_s
799
+ text.to_s
553
800
  end
554
801
  end
555
802
 
@@ -558,7 +805,7 @@ module AbideDevUtils
558
805
  include AbideDevUtils::XCCDF::Parser::Objects::NumberedObject
559
806
  attr_reader :number
560
807
 
561
- def initialize(element)
808
+ def initialize(element, parent_node: nil)
562
809
  super
563
810
  @number = to_s[/([0-9]+\.)+[0-9]+/]
564
811
  add_child(AttributeValue, element, 'role')
@@ -572,6 +819,7 @@ module AbideDevUtils
572
819
  add_children(Check, element)
573
820
  add_child(ComplexCheck, element)
574
821
  add_child(Metadata, element)
822
+ similarity_methods :number, :title
575
823
  end
576
824
 
577
825
  def self.xpath
@@ -581,7 +829,7 @@ module AbideDevUtils
581
829
 
582
830
  # Class for XCCDF Value
583
831
  class Value < ElementWithId
584
- def initialize(element)
832
+ def initialize(element, parent_node: nil)
585
833
  super
586
834
  add_child(AttributeValue, element, 'operator')
587
835
  add_child(AttributeValue, element, 'type')
@@ -590,64 +838,112 @@ module AbideDevUtils
590
838
  add_child(ShortText, find_element.at_xpath(element, 'value'))
591
839
  end
592
840
 
841
+ def <=>(other)
842
+ return nil unless other.instance_of?(self.class)
843
+
844
+ title.to_s <=> other.title.to_s
845
+ end
846
+
847
+ def eql?(other)
848
+ operator.value == other.operator.value &&
849
+ type.value == other.type.value &&
850
+ title.to_s == other.title.to_s &&
851
+ description.to_s == other.description.to_s &&
852
+ text == other.text
853
+ end
854
+
855
+ def hash
856
+ [
857
+ operator.value,
858
+ type.value,
859
+ title.to_s,
860
+ description.to_s,
861
+ text,
862
+ ].join.hash
863
+ end
864
+
593
865
  def self.xpath
594
866
  'Value'
595
867
  end
596
868
 
597
869
  def to_s
598
- search_children.find_child_by_class(Title).to_s
870
+ "#{title}: #{type.value} #{operator.value} #{text}"
599
871
  end
600
872
  end
601
873
 
602
874
  # Class for XCCDF benchmark status
603
875
  class Status < ElementBase
604
- def initialize(element)
876
+ def initialize(element, parent_node: nil)
605
877
  super
606
878
  add_child(ShortText, element)
607
879
  add_child(AttributeValue, element, 'date')
608
880
  end
609
881
 
882
+ def eql?(other)
883
+ to_s == other.to_s
884
+ end
885
+
886
+ def hash
887
+ to_s.hash
888
+ end
889
+
610
890
  def self.xpath
611
891
  'status'
612
892
  end
613
893
 
614
894
  def to_s
615
895
  [
616
- "Status:#{search_children.find_child_by_class(ShortText)}",
617
- "Date:#{search_children.find_child_by_class(AttributeValue)}",
896
+ "Status:#{text}",
897
+ "Date:#{date}",
618
898
  ].join('|')
619
899
  end
620
900
  end
621
901
 
622
902
  # Class for XCCDF benchmark version
623
903
  class Version < ElementBase
624
- def initialize(element)
904
+ def initialize(element, parent_node: nil)
625
905
  super
626
906
  add_child(ShortText, element)
627
907
  end
628
908
 
909
+ def eql?(other)
910
+ to_s == other.to_s
911
+ end
912
+
913
+ def hash
914
+ to_s.hash
915
+ end
916
+
629
917
  def self.xpath
630
918
  'version'
631
919
  end
632
920
 
633
921
  def to_s
634
- search_children.find_child_by_class(ShortText).to_s
922
+ text.to_s
635
923
  end
636
924
  end
637
925
 
638
926
  # Class for XCCDF benchmark platform
639
927
  class Platform < ElementBase
640
- def initialize(element)
928
+ def initialize(element, parent_node: nil)
641
929
  super
642
930
  add_child(AttributeValue, element, 'idref')
643
931
  end
644
932
 
933
+ def eql?(other)
934
+ to_s == other.to_s
935
+ end
936
+
937
+ def hash
938
+ to_s.hash
939
+ end
940
+
645
941
  def self.xpath
646
942
  'platform'
647
943
  end
648
944
 
649
945
  def to_s
650
- search_children.find_child_by_class(AttributeValue).to_s
946
+ idref.to_s
651
947
  end
652
948
  end
653
949
 
@@ -655,7 +951,7 @@ module AbideDevUtils
655
951
  class Benchmark < ElementBase
656
952
  include AbideDevUtils::XCCDF::Parser::Objects::NumberedObject
657
953
 
658
- def initialize(element)
954
+ def initialize(element, parent_node: nil)
659
955
  super
660
956
  elem = find_element.at_xpath(element, 'Benchmark')
661
957
  raise 'No Benchmark element found' if elem.nil?
@@ -670,12 +966,40 @@ module AbideDevUtils
670
966
  add_children(Value, elem)
671
967
  end
672
968
 
969
+ def number
970
+ @number ||= version.to_s[/([0-9]+\.)+[0-9]+/]
971
+ end
972
+
973
+ def to_h
974
+ {
975
+ title: title.to_s,
976
+ version: version.to_s,
977
+ status: status.to_s,
978
+ platform: platform.to_s,
979
+ profile: profile.map(&:to_h),
980
+ group: group.map(&:to_h),
981
+ value: value.map(&:to_h),
982
+ }
983
+ end
984
+
985
+ def diff_only_rules(other, profile: nil, level: nil)
986
+ self_rules = descendants.select { |x| x.is_a?(Rule) }
987
+ other_rules = other.descendants.select { |x| x.is_a?(Rule) }
988
+ unless profile.nil?
989
+ self_rules = self_rules.select { |x| x.linked_profile.any? { |p| p.title.to_s.match?(profile) } }
990
+ end
991
+ unless level.nil?
992
+ self_rules = self_rules.select { |x| x.linked_profile.any? { |p| p.level.to_s.match?(level) } }
993
+ end
994
+ diff_array_obj(self_rules, other_rules)
995
+ end
996
+
673
997
  def self.xpath
674
998
  'Benchmark'
675
999
  end
676
1000
 
677
1001
  def to_s
678
- [search_children.find_child_by_class(Title).to_s, search_children.find_child_by_class(Version).to_s].join(' ')
1002
+ [title.to_s, version.to_s].join(' ')
679
1003
  end
680
1004
  end
681
1005
  end