abide_dev_utils 0.14.2 → 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.
- checksums.yaml +4 -4
- data/Gemfile.lock +19 -33
- data/abide_dev_utils.gemspec +0 -1
- data/lib/abide_dev_utils/cli/cem.rb +9 -8
- data/lib/abide_dev_utils/cli/jira.rb +48 -4
- data/lib/abide_dev_utils/cli/xccdf.rb +7 -13
- data/lib/abide_dev_utils/config.rb +3 -6
- data/lib/abide_dev_utils/files.rb +16 -6
- data/lib/abide_dev_utils/jira.rb +69 -1
- data/lib/abide_dev_utils/prompt.rb +3 -1
- data/lib/abide_dev_utils/version.rb +1 -1
- data/lib/abide_dev_utils/xccdf/diff.rb +64 -200
- data/lib/abide_dev_utils/xccdf/parser/helpers.rb +0 -93
- data/lib/abide_dev_utils/xccdf/parser/objects/diffable_object.rb +347 -0
- data/lib/abide_dev_utils/xccdf/parser/objects.rb +413 -89
- data/lib/abide_dev_utils/xccdf/parser.rb +8 -9
- data/lib/abide_dev_utils/xccdf.rb +1 -10
- metadata +4 -24
- data/lib/abide_dev_utils/xccdf/diff/benchmark/number_title.rb +0 -270
- data/lib/abide_dev_utils/xccdf/diff/benchmark/profile.rb +0 -104
- data/lib/abide_dev_utils/xccdf/diff/benchmark/property.rb +0 -127
- data/lib/abide_dev_utils/xccdf/diff/benchmark/property_existence.rb +0 -47
- data/lib/abide_dev_utils/xccdf/diff/benchmark.rb +0 -267
- data/lib/abide_dev_utils/xccdf/diff/utils.rb +0 -30
- data/lib/abide_dev_utils/xccdf/parser/objects/digest_object.rb +0 -118
@@ -0,0 +1,347 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AbideDevUtils
|
4
|
+
module XCCDF
|
5
|
+
module Parser
|
6
|
+
module Objects
|
7
|
+
# Methods for converting an object to a hash
|
8
|
+
module DiffableObject
|
9
|
+
# Generic error class for diffable objects
|
10
|
+
class DiffableObjectError < StandardError
|
11
|
+
def initialize(self_obj = nil, other_obj = nil, custom_msg: nil)
|
12
|
+
if custom_msg.nil?
|
13
|
+
super("This object (#{self_obj}) is not diffable with other object (#{other_obj})")
|
14
|
+
else
|
15
|
+
super(custom_msg)
|
16
|
+
end
|
17
|
+
@self_obj = self_obj
|
18
|
+
@other_obj = other_obj
|
19
|
+
@custom_msg = custom_msg
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Raised when the diffed objects are not instances of the same class
|
24
|
+
class NotSameClassError < DiffableObjectError
|
25
|
+
def initialize(self_obj = nil, other_obj = nil)
|
26
|
+
super(
|
27
|
+
self_obj,
|
28
|
+
other_obj,
|
29
|
+
custom_msg: "This object's class (#{self_obj.class}) does not match other object's class (#{other_obj.class})",
|
30
|
+
)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Holds the result of a diff on a per-item basis.
|
35
|
+
class DiffChangeResult
|
36
|
+
attr_accessor :type, :details
|
37
|
+
attr_reader :old_value, :new_value, :element
|
38
|
+
|
39
|
+
def initialize(type, old_value, new_value, element, details = {})
|
40
|
+
@type = type
|
41
|
+
@old_value = old_value
|
42
|
+
@new_value = new_value
|
43
|
+
@element = element
|
44
|
+
@details = details
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_h(hash_objs: false)
|
48
|
+
return new_hash(type, old_value, new_value, element, details) unless hash_objs
|
49
|
+
|
50
|
+
old_val = old_value.respond_to?(:to_s) ? old_value.to_s : old_value.inspect
|
51
|
+
new_val = new_value.respond_to?(:to_s) ? new_value.to_s : new_value.inspect
|
52
|
+
details_hash = details.transform_values do |val|
|
53
|
+
case val
|
54
|
+
when DiffChangeResult
|
55
|
+
val.to_h(hash_objs: hash_objs)
|
56
|
+
when Array
|
57
|
+
val.map { |v| v.is_a?(DiffChangeResult) ? v.to_h(hash_objs: hash_objs) : v.to_s }
|
58
|
+
else
|
59
|
+
val.to_s
|
60
|
+
end
|
61
|
+
end
|
62
|
+
new_hash(type, old_val, new_val, element, details_hash)
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_yaml
|
66
|
+
require 'yaml'
|
67
|
+
to_h(hash_objs: true).to_yaml
|
68
|
+
end
|
69
|
+
|
70
|
+
def to_a
|
71
|
+
[type, old_value, new_value, element, details]
|
72
|
+
end
|
73
|
+
|
74
|
+
def to_s
|
75
|
+
"#{element} #{type}: #{old_value} -> #{new_value}, #{details}"
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def new_hash(change_type, old_val, new_val, element, details_hash)
|
81
|
+
hsh = {}
|
82
|
+
hsh[:type] = change_type
|
83
|
+
hsh[:old_value] = old_val unless old_val.nil? || (old_val.respond_to?(:empty?) && old_val.empty?)
|
84
|
+
hsh[:new_value] = new_val unless new_val.nil? || (old_val.respond_to?(:empty?) && new_val.empty?)
|
85
|
+
hsh[:element] = element
|
86
|
+
hsh[:details] = details_hash unless details_hash.empty?
|
87
|
+
hsh
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def diff_change_result(type, old_value, new_value, element = nil, details = {})
|
92
|
+
element = if element.nil?
|
93
|
+
get_element_from = old_value || new_value
|
94
|
+
get_element_from.respond_to?(:label) ? get_element_from.label.to_sym : :plain_object
|
95
|
+
else
|
96
|
+
element
|
97
|
+
end
|
98
|
+
DiffChangeResult.new(type, old_value, new_value, element, details)
|
99
|
+
end
|
100
|
+
|
101
|
+
def check_diffable!(other)
|
102
|
+
raise DiffableObjectError.new(self, other) unless other.class.included_modules.include?(DiffableObject)
|
103
|
+
raise NotSameClassError.new(self, other) unless other.instance_of?(self.class)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Diff two parser objects
|
107
|
+
# @param other [Object] The object to compare to. Must be an instance
|
108
|
+
# of the same class as the object being diffed.
|
109
|
+
# @return [DiffChangeResult] The result of the diff
|
110
|
+
def diff(other)
|
111
|
+
return if other.nil?
|
112
|
+
|
113
|
+
check_diffable!(other)
|
114
|
+
case self
|
115
|
+
when AbideDevUtils::XCCDF::Parser::Objects::AttributeValue,
|
116
|
+
AbideDevUtils::XCCDF::Parser::Objects::ControlURI,
|
117
|
+
AbideDevUtils::XCCDF::Parser::Objects::Description,
|
118
|
+
AbideDevUtils::XCCDF::Parser::Objects::Fixtext,
|
119
|
+
AbideDevUtils::XCCDF::Parser::Objects::LongText,
|
120
|
+
AbideDevUtils::XCCDF::Parser::Objects::Platform,
|
121
|
+
AbideDevUtils::XCCDF::Parser::Objects::Rationale,
|
122
|
+
AbideDevUtils::XCCDF::Parser::Objects::ShortText,
|
123
|
+
AbideDevUtils::XCCDF::Parser::Objects::System,
|
124
|
+
AbideDevUtils::XCCDF::Parser::Objects::Title,
|
125
|
+
AbideDevUtils::XCCDF::Parser::Objects::Version
|
126
|
+
diff_str_obj(self, other)
|
127
|
+
when AbideDevUtils::XCCDF::Parser::Objects::Rule
|
128
|
+
diff_rule(self, other)
|
129
|
+
when AbideDevUtils::XCCDF::Parser::Objects::Check
|
130
|
+
diff_check(self, other)
|
131
|
+
when AbideDevUtils::XCCDF::Parser::Objects::ComplexCheck
|
132
|
+
diff_complex_check(self, other)
|
133
|
+
when AbideDevUtils::XCCDF::Parser::Objects::XccdfSelect
|
134
|
+
diff_xccdf_select(self, other)
|
135
|
+
when AbideDevUtils::XCCDF::Parser::Objects::Value
|
136
|
+
diff_value(self, other)
|
137
|
+
when AbideDevUtils::XCCDF::Parser::Objects::Group
|
138
|
+
diff_group(self, other)
|
139
|
+
when AbideDevUtils::XCCDF::Parser::Objects::Profile
|
140
|
+
diff_profile(self, other)
|
141
|
+
when AbideDevUtils::XCCDF::Parser::Objects::Benchmark
|
142
|
+
diff_benchmark(self, other)
|
143
|
+
else
|
144
|
+
diff_ambiguous(self, other, called_from_diff: true)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def diff_ambiguous(self_obj, other_obj, called_from_diff: false)
|
149
|
+
return if other_obj.nil?
|
150
|
+
|
151
|
+
if self_obj.is_a?(Array) && other_obj.is_a?(Array)
|
152
|
+
diff_array_obj(self_obj, other_obj)
|
153
|
+
elsif self_obj.respond_to?(:diff) && other_obj.respond_to?(:diff) && !called_from_diff
|
154
|
+
self_obj.diff(other_obj)
|
155
|
+
elsif self_obj.respond_to?(:to_s) && other_obj.respond_to?(:to_s)
|
156
|
+
diff_str_obj(self_obj, other_obj)
|
157
|
+
else
|
158
|
+
diff_plain_obj(self_obj, other_obj)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def diff_plain_obj(self_obj, other_obj)
|
163
|
+
result = self_obj == other_obj ? :equal : :not_equal
|
164
|
+
d_hash = {
|
165
|
+
self_ivars: self_obj.iv_to_h,
|
166
|
+
other_ivars: other_obj.iv_to_h,
|
167
|
+
}
|
168
|
+
diff_change_result(result, self_obj, other_obj, d_hash)
|
169
|
+
end
|
170
|
+
|
171
|
+
def diff_str_obj(self_obj, other_obj)
|
172
|
+
result = self_obj.to_s == other_obj.to_s ? :equal : :not_equal
|
173
|
+
diff_change_result(result, self_obj, other_obj)
|
174
|
+
end
|
175
|
+
|
176
|
+
def diff_array_obj(self_ary, other_ary)
|
177
|
+
sorted_self = self_ary.sort
|
178
|
+
sorted_other = other_ary.sort
|
179
|
+
added_ary = (sorted_other - sorted_self).map { |i| diff_change_result(:added, nil, i) }
|
180
|
+
removed_ary = (sorted_self - sorted_other).map { |i| diff_change_result(:removed, i, nil) }
|
181
|
+
changed_ary = correlate_added_removed(added_ary, removed_ary)
|
182
|
+
diffable_self = sorted_self - (changed_ary.map(&:old_value) + changed_ary.map(&:new_value)).compact
|
183
|
+
diffable_other = sorted_other - (changed_ary.map(&:old_value) + changed_ary.map(&:new_value)).compact
|
184
|
+
diff_ary = diffable_self.zip(diffable_other).filter_map do |(self_obj, other_obj)|
|
185
|
+
change = diff_ambiguous(self_obj, other_obj)
|
186
|
+
if change.type == :equal
|
187
|
+
nil
|
188
|
+
else
|
189
|
+
change
|
190
|
+
end
|
191
|
+
end
|
192
|
+
diff_ary + changed_ary
|
193
|
+
end
|
194
|
+
|
195
|
+
def correlate_added_removed(added_ary, removed_ary)
|
196
|
+
return [] if added_ary.empty? && removed_ary.empty?
|
197
|
+
|
198
|
+
actual_added = added_ary.dup
|
199
|
+
actual_removed = removed_ary.dup
|
200
|
+
correlated = added_ary.each_with_object([]) do |added, ary|
|
201
|
+
similarity = nil
|
202
|
+
removed = removed_ary.find do |r|
|
203
|
+
similarity = added.new_value.find_similarity(r.old_value)
|
204
|
+
similarity.any? { |s| s[3] }
|
205
|
+
end
|
206
|
+
next if removed.nil?
|
207
|
+
|
208
|
+
details_hash = {}
|
209
|
+
similarity.each do |similar|
|
210
|
+
details_hash[similar[0]] = similar[1..2] unless similar[3]
|
211
|
+
end
|
212
|
+
ary << diff_change_result(:not_equal, removed.old_value, added.new_value, nil, details_hash)
|
213
|
+
actual_added.delete(added)
|
214
|
+
actual_removed.delete(removed)
|
215
|
+
end
|
216
|
+
(correlated + actual_added + actual_removed).uniq
|
217
|
+
end
|
218
|
+
|
219
|
+
def diff_rule(self_rule, other_rule, diff_properties: %i[number title])
|
220
|
+
d_hash = diff_properties.each_with_object({}) do |prop, hsh|
|
221
|
+
hsh[prop] = diff_ambiguous(self_rule.send(prop), other_rule.send(prop))
|
222
|
+
end
|
223
|
+
result = result_from_details_hash(d_hash)
|
224
|
+
d_hash = process_details_hash!(d_hash)
|
225
|
+
diff_change_result(result, self_rule, other_rule, :rule, d_hash)
|
226
|
+
end
|
227
|
+
|
228
|
+
def diff_check(self_check, other_check)
|
229
|
+
d_hash = {
|
230
|
+
system: self_check.system.diff(other_check.system),
|
231
|
+
check_export: diff_ambiguous(self_check.check_export, other_check.check_export),
|
232
|
+
check_content_ref: diff_ambiguous(self_check.check_content_ref, other_check.check_content_ref),
|
233
|
+
}
|
234
|
+
result = result_from_details_hash(d_hash)
|
235
|
+
d_hash = process_details_hash!(d_hash)
|
236
|
+
diff_change_result(result, self_check, other_check, :check, d_hash)
|
237
|
+
end
|
238
|
+
|
239
|
+
def diff_complex_check(self_complex_check, other_complex_check)
|
240
|
+
d_hash = {
|
241
|
+
operator: self_complex_check.operator.diff(other_complex_check.operator),
|
242
|
+
check: diff_array_obj(self_complex_check.check, other_complex_check.check),
|
243
|
+
}
|
244
|
+
result = result_from_details_hash(d_hash)
|
245
|
+
d_hash = process_details_hash!(d_hash)
|
246
|
+
diff_change_result(result, self_complex_check, other_complex_check, :complex_check, d_hash)
|
247
|
+
end
|
248
|
+
|
249
|
+
def diff_xccdf_select(self_select, other_select)
|
250
|
+
d_hash = {
|
251
|
+
idref: diff_str_obj(self_select.to_s, other_select.to_s),
|
252
|
+
selected: diff_str_obj(self_select.selected, other_select.selected),
|
253
|
+
}
|
254
|
+
result = result_from_details_hash(d_hash)
|
255
|
+
d_hash = process_details_hash!(d_hash)
|
256
|
+
diff_change_result(result, self_select, other_select, :xccdf_select, d_hash)
|
257
|
+
end
|
258
|
+
|
259
|
+
def diff_value(self_value, other_value)
|
260
|
+
d_hash = {
|
261
|
+
title: self_value.title.diff(other_value.title),
|
262
|
+
description: self_value.description.diff(other_value.description),
|
263
|
+
text: diff_str_obj(self_value.text.to_s, other_value.text.to_s),
|
264
|
+
operator: self_value.operator.diff(other_value.operator),
|
265
|
+
type: self_value.type.diff(other_value.type),
|
266
|
+
}
|
267
|
+
result = result_from_details_hash(d_hash)
|
268
|
+
d_hash = process_details_hash!(d_hash)
|
269
|
+
diff_change_result(result, self_value, other_value, :value, d_hash)
|
270
|
+
end
|
271
|
+
|
272
|
+
def diff_group(self_group, other_group)
|
273
|
+
d_hash = {
|
274
|
+
title: self_group.title.diff(other_group.title),
|
275
|
+
description: self_group.description.diff(other_group.description),
|
276
|
+
}
|
277
|
+
if self_group.respond_to?(:group) && other_group.respond_to?(:group)
|
278
|
+
d_hash[:group] ||= []
|
279
|
+
g_diff = diff_ambiguous(self_group.group, other_group.group)
|
280
|
+
if g_diff.is_a?(Array)
|
281
|
+
d_hash[:group] += g_diff
|
282
|
+
else
|
283
|
+
d_hash[:group] << g_diff
|
284
|
+
end
|
285
|
+
end
|
286
|
+
if self_group.respond_to?(:rule) && other_group.respond_to?(:rule)
|
287
|
+
d_hash[:rule] ||= []
|
288
|
+
r_diff = diff_ambiguous(self_group.rule, other_group.rule)
|
289
|
+
if r_diff.is_a?(Array)
|
290
|
+
d_hash[:rule] += r_diff
|
291
|
+
else
|
292
|
+
d_hash[:rule] << r_diff
|
293
|
+
end
|
294
|
+
end
|
295
|
+
result = result_from_details_hash(d_hash)
|
296
|
+
d_hash = process_details_hash!(d_hash)
|
297
|
+
diff_change_result(result, self_group, other_group, :group, d_hash)
|
298
|
+
end
|
299
|
+
|
300
|
+
def diff_profile(self_profile, other_profile)
|
301
|
+
d_hash = {
|
302
|
+
title: self_profile.title.diff(other_profile.title),
|
303
|
+
description: self_profile.description.diff(other_profile.description),
|
304
|
+
level: diff_str_obj(self_profile.level, other_profile.level),
|
305
|
+
xccdf_select: diff_ambiguous(self_profile.xccdf_select, other_profile.xccdf_select),
|
306
|
+
}
|
307
|
+
result = result_from_details_hash(d_hash)
|
308
|
+
d_hash = process_details_hash!(d_hash)
|
309
|
+
diff_change_result(result, self_profile, other_profile, :profile, d_hash)
|
310
|
+
end
|
311
|
+
|
312
|
+
def diff_benchmark(self_benchmark, other_benchmark)
|
313
|
+
d_hash = {}
|
314
|
+
d_hash[:title] = self_benchmark.title.diff(other_benchmark.title)
|
315
|
+
d_hash[:description] = self_benchmark.description.diff(other_benchmark.description)
|
316
|
+
d_hash[:platform] = self_benchmark.platform.diff(other_benchmark.platform)
|
317
|
+
d_hash[:profile] = diff_ambiguous(self_benchmark.profile, other_benchmark.profile)
|
318
|
+
d_hash[:group] = diff_ambiguous(self_benchmark.group, other_benchmark.group)
|
319
|
+
d_hash[:value] = diff_ambiguous(self_benchmark.value, other_benchmark.value)
|
320
|
+
result = result_from_details_hash(d_hash)
|
321
|
+
d_hash = process_details_hash!(d_hash)
|
322
|
+
diff_change_result(result, self_benchmark, other_benchmark, :benchmark, d_hash)
|
323
|
+
end
|
324
|
+
|
325
|
+
def process_details_hash!(d_hash, _element = :plain_object)
|
326
|
+
d_hash.reject! do |_, v|
|
327
|
+
v.nil? || (v.empty? if v.respond_to?(:empty?)) || (v.type == :equal if v.respond_to?(:type))
|
328
|
+
end
|
329
|
+
d_hash
|
330
|
+
end
|
331
|
+
|
332
|
+
def result_from_details_hash(d_hash)
|
333
|
+
changed_types = %i[not_equal added removed]
|
334
|
+
results = d_hash.values.find do |v|
|
335
|
+
if v.is_a?(Array)
|
336
|
+
v.map(&:type).any? { |i| changed_types.include?(i) }
|
337
|
+
elsif v.respond_to?(:type)
|
338
|
+
changed_types.include?(v&.type)
|
339
|
+
end
|
340
|
+
end
|
341
|
+
results.nil? ? :equal : :not_equal
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|