abide_dev_utils 0.14.1 → 0.15.0

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