abide_dev_utils 0.14.2 → 0.15.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -1,232 +1,96 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require_relative 'parser'
|
4
4
|
|
5
5
|
module AbideDevUtils
|
6
6
|
module XCCDF
|
7
7
|
# Contains methods and classes used to diff XCCDF-derived objects.
|
8
8
|
module Diff
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
array_path: true,
|
14
|
-
delimiter: '//',
|
15
|
-
use_lcs: true,
|
16
|
-
}.freeze
|
9
|
+
def self.benchmark_diff(xml1, xml2, opts = {})
|
10
|
+
bd = BenchmarkDiff.new(xml1, xml2, opts)
|
11
|
+
if opts[:raw]
|
12
|
+
return bd.diff_rules_raw if opts[:rules_only]
|
17
13
|
|
18
|
-
|
19
|
-
|
20
|
-
|
14
|
+
bd.diff_raw
|
15
|
+
else
|
16
|
+
return bd.diff_rules if opts[:rules_only]
|
21
17
|
|
22
|
-
|
23
|
-
validate_change(change)
|
24
|
-
@change = change
|
25
|
-
@key = key
|
26
|
-
@value = value
|
27
|
-
@value_to = value_to
|
28
|
-
end
|
29
|
-
|
30
|
-
def to_s
|
31
|
-
value_change_string(value, value_to)
|
32
|
-
end
|
33
|
-
|
34
|
-
def to_h
|
35
|
-
{
|
36
|
-
change: change,
|
37
|
-
key: key,
|
38
|
-
value: value,
|
39
|
-
value_to: value_to,
|
40
|
-
}
|
41
|
-
end
|
42
|
-
|
43
|
-
def can_merge?(other)
|
44
|
-
return false unless (change == '-' && other.change == '+') || (change == '+' && other.change == '-')
|
45
|
-
return false unless key == other.key || value_hash_equality(other)
|
46
|
-
|
47
|
-
true
|
48
|
-
end
|
49
|
-
|
50
|
-
def merge(other)
|
51
|
-
unless can_merge?(other)
|
52
|
-
raise ArgumentError, 'Cannot merge. Possible causes: change is identical; key or value do not match'
|
53
|
-
end
|
54
|
-
|
55
|
-
new_to_value = value == other.value ? nil : other.value
|
56
|
-
ChangeSet.new(
|
57
|
-
change: '~',
|
58
|
-
key: key,
|
59
|
-
value: value,
|
60
|
-
value_to: new_to_value
|
61
|
-
)
|
62
|
-
end
|
63
|
-
|
64
|
-
def merge!(other)
|
65
|
-
new_props = merge(other)
|
66
|
-
@change = new_props.change
|
67
|
-
@key = new_props.key
|
68
|
-
@value = new_props.value
|
69
|
-
@value_to = new_props.value_to
|
70
|
-
end
|
71
|
-
|
72
|
-
private
|
73
|
-
|
74
|
-
def value_hash_equality(other)
|
75
|
-
equality = false
|
76
|
-
value.each do |k, v|
|
77
|
-
equality = true if v == other.value[k]
|
78
|
-
end
|
79
|
-
equality
|
80
|
-
end
|
81
|
-
|
82
|
-
def validate_change(chng)
|
83
|
-
raise ArgumentError, "Change type #{chng} in invalid" unless ['+', '-', '~'].include?(chng)
|
84
|
-
end
|
85
|
-
|
86
|
-
def change_string(chng)
|
87
|
-
case chng
|
88
|
-
when '-'
|
89
|
-
'Remove'
|
90
|
-
when '+'
|
91
|
-
'Add'
|
92
|
-
else
|
93
|
-
'Change'
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
def value_change_string(value, value_to)
|
98
|
-
change_str = []
|
99
|
-
change_diff = Hashdiff.diff(value, value_to || {}, AbideDevUtils::XCCDF::Diff::DEFAULT_DIFF_OPTS)
|
100
|
-
return if change_diff.empty?
|
101
|
-
return value_change_string_single_type(change_diff, value) if all_single_change_type?(change_diff)
|
102
|
-
|
103
|
-
change_diff.each do |chng|
|
104
|
-
change_str << if chng.length == 4
|
105
|
-
"#{change_string(chng[0])} #{chng[1][0]} \"#{chng[2]}\" to \"#{chng[3]}\""
|
106
|
-
else
|
107
|
-
"#{change_string(chng[0])} #{chng[1][0]} with value #{chng[2]}"
|
108
|
-
end
|
109
|
-
end
|
110
|
-
change_str.join(', ')
|
18
|
+
bd.diff
|
111
19
|
end
|
20
|
+
end
|
112
21
|
|
113
|
-
|
114
|
-
|
22
|
+
# Class for benchmark diffs
|
23
|
+
class BenchmarkDiff
|
24
|
+
attr_reader :this, :other, :opts
|
25
|
+
|
26
|
+
# @param xml1 [String] path to the first benchmark XCCDF xml file
|
27
|
+
# @param xml2 [String] path to the second benchmark XCCDF xml file
|
28
|
+
# @param opts [Hash] options hash
|
29
|
+
def initialize(xml1, xml2, opts = {})
|
30
|
+
@this = new_benchmark(xml1)
|
31
|
+
@other = new_benchmark(xml2)
|
32
|
+
@opts = opts
|
115
33
|
end
|
116
34
|
|
117
|
-
def
|
118
|
-
|
35
|
+
def diff_raw
|
36
|
+
@diff_raw ||= @this.diff(@other)
|
119
37
|
end
|
120
|
-
end
|
121
38
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
@profile_a = profile_a
|
126
|
-
@profile_b = profile_b
|
127
|
-
@opts = opts
|
39
|
+
def diff
|
40
|
+
warn 'Full benchmark diff is not yet implemented, return rules diff for now'
|
41
|
+
diff_rules
|
128
42
|
end
|
129
43
|
|
130
|
-
def
|
131
|
-
@
|
44
|
+
def diff_rules_raw
|
45
|
+
@diff_rules_raw ||= @this.diff_only_rules(@other)
|
132
46
|
end
|
133
47
|
|
134
|
-
|
48
|
+
def diff_rules
|
49
|
+
return diff_rules_raw if opts[:raw]
|
50
|
+
return [] if diff_rules_raw.all? { |r| r.type == :equal }
|
135
51
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
52
|
+
diff_hash = {
|
53
|
+
from: @this.to_s,
|
54
|
+
to: @other.to_s,
|
55
|
+
rules: {}
|
56
|
+
}
|
57
|
+
diff_rules_raw.each do |rule|
|
58
|
+
diff_hash[:rules][rule.type] ||= []
|
59
|
+
case rule.type
|
60
|
+
when :added
|
61
|
+
diff_hash[:rules][rule.type] << { number: rule.new_value.number.to_s, title: rule.new_value.title.to_s }
|
62
|
+
when :removed
|
63
|
+
diff_hash[:rules][rule.type] << { number: rule.old_value.number.to_s, title: rule.old_value.title.to_s }
|
147
64
|
else
|
148
|
-
|
65
|
+
rd_hash = {}
|
66
|
+
rd_hash[:from] = "#{rule.old_value&.number} #{rule.old_value&.title}" if rule.old_value
|
67
|
+
rd_hash[:to] = "#{rule.new_value&.number} #{rule.new_value&.title}" if rule.new_value
|
68
|
+
changes = rule.details.transform_values { |v| v.is_a?(Array) ? v.map(&:to_s) : v.to_s }
|
69
|
+
if opts[:ignore_changed_properties]
|
70
|
+
changes.delete_if { |k, _| opts[:ignore_changed_properties].include?(k.to_s) }
|
71
|
+
next if changes.empty? # Skip entirely if all changed filtered out
|
72
|
+
end
|
73
|
+
rd_hash[:changes] = changes unless changes.empty?
|
74
|
+
diff_hash[:rules][rule.type] << rd_hash
|
149
75
|
end
|
150
76
|
end
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
[changes[0].merge(changes[1])]
|
158
|
-
else
|
159
|
-
changes
|
77
|
+
unless opts[:no_stats]
|
78
|
+
stats_hash = {}
|
79
|
+
diff_hash[:rules].each do |type, rules|
|
80
|
+
stats_hash[type] = rules.size
|
81
|
+
end
|
82
|
+
diff_hash[:stats] = stats_hash unless stats_hash.empty?
|
160
83
|
end
|
161
|
-
|
162
|
-
end
|
163
|
-
|
164
|
-
# Class used to diff two AbideDevUtils::XCCDF::Benchmark objects.
|
165
|
-
class BenchmarkDiff
|
166
|
-
def initialize(benchmark_a, benchmark_b, opts = {})
|
167
|
-
@benchmark_a = benchmark_a
|
168
|
-
@benchmark_b = benchmark_b
|
169
|
-
@opts = opts
|
170
|
-
end
|
171
|
-
|
172
|
-
def properties_to_diff
|
173
|
-
@properties_to_diff ||= %i[title version profiles]
|
174
|
-
end
|
175
|
-
|
176
|
-
def title_version
|
177
|
-
@title_version ||= diff_title_version
|
178
|
-
end
|
179
|
-
|
180
|
-
def profiles(profile: nil)
|
181
|
-
@profiles ||= diff_profiles(profile: profile)
|
84
|
+
diff_hash
|
182
85
|
end
|
183
86
|
|
184
87
|
private
|
185
88
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
AbideDevUtils::XCCDF::Diff::DEFAULT_DIFF_OPTS
|
191
|
-
)
|
192
|
-
diff.each_with_object({}) do |change, tdiff|
|
193
|
-
val_to = change.length == 4 ? change[3] : nil
|
194
|
-
change_key = change[2].is_a?(Hash) ? change[2][:title] : change[2]
|
195
|
-
tdiff[change_key] = [] unless tdiff.key?(change_key)
|
196
|
-
tdiff[change_key] << ChangeSet.new(change: change[0], key: change[1], value: change[2], value_to: val_to)
|
197
|
-
end
|
89
|
+
# Returns a Benchmark object from a XCCDF xml file path
|
90
|
+
# @param xml [String] path to a XCCDF xml file
|
91
|
+
def new_benchmark(xml)
|
92
|
+
AbideDevUtils::XCCDF::Parser.parse(xml)
|
198
93
|
end
|
199
|
-
|
200
|
-
def diff_profiles(profile: nil)
|
201
|
-
diff = {}
|
202
|
-
other_hash = @benchmark_b.to_h[:profiles]
|
203
|
-
@benchmark_a.to_h[:profiles].each do |name, data|
|
204
|
-
next if profile && profile != name
|
205
|
-
|
206
|
-
diff[name] = ProfileDiff.new(data, other_hash[name], @opts).diff
|
207
|
-
end
|
208
|
-
diff
|
209
|
-
end
|
210
|
-
end
|
211
|
-
|
212
|
-
def self.diff_benchmarks(benchmark_a, benchmark_b, opts = {})
|
213
|
-
profile = opts.fetch(:profile, nil)
|
214
|
-
profile_key = profile.nil? ? 'all_profiles' : profile
|
215
|
-
benchmark_diff = BenchmarkDiff.new(benchmark_a, benchmark_b, opts)
|
216
|
-
transform_method_sym = opts.fetch(:raw, false) ? :to_h : :to_s
|
217
|
-
diff = if profile.nil?
|
218
|
-
benchmark_diff.profiles.each do |_, v|
|
219
|
-
v.transform_values! { |x| x.map!(&transform_method_sym) }
|
220
|
-
end
|
221
|
-
else
|
222
|
-
benchmark_diff.profiles(profile: profile)[profile].transform_values! { |x| x.map!(&transform_method_sym) }
|
223
|
-
end
|
224
|
-
return diff.values.flatten if opts.fetch(:raw, false)
|
225
|
-
|
226
|
-
{
|
227
|
-
'benchmark' => benchmark_diff.title_version.transform_values { |x| x.map!(&:to_s) },
|
228
|
-
profile_key => diff.values.flatten,
|
229
|
-
}
|
230
94
|
end
|
231
95
|
end
|
232
96
|
end
|
@@ -4,99 +4,6 @@ module AbideDevUtils
|
|
4
4
|
module XCCDF
|
5
5
|
module Parser
|
6
6
|
module Helpers
|
7
|
-
# Provides helper methods for working with XCCDF element children
|
8
|
-
module ElementChildren
|
9
|
-
def search_children
|
10
|
-
@search_children ||= SearchChildren.new(children)
|
11
|
-
end
|
12
|
-
|
13
|
-
# Implements methods that allow for searching an XCCDF Element's children
|
14
|
-
class SearchChildren
|
15
|
-
attr_reader :children
|
16
|
-
|
17
|
-
def initialize(children)
|
18
|
-
@children = children
|
19
|
-
end
|
20
|
-
|
21
|
-
def recursive_select_children(children_to_search = children, &block)
|
22
|
-
search_hits = []
|
23
|
-
children_to_search.each do |child|
|
24
|
-
found = yield child
|
25
|
-
if found
|
26
|
-
search_hits << child
|
27
|
-
elsif child.respond_to?(:children)
|
28
|
-
search_hits << recursive_select_children(child.children, &block)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
search_hits.flatten.compact.uniq
|
32
|
-
end
|
33
|
-
|
34
|
-
def recursive_find_child(children_to_search = children, &block)
|
35
|
-
rescursive_select_children(children_to_search, &block).first
|
36
|
-
end
|
37
|
-
|
38
|
-
def find_children_that_respond_to(method, recurse: false)
|
39
|
-
return recursive_select_children { |child| child.respond_to?(method) } if recurse
|
40
|
-
|
41
|
-
children.select { |c| c.respond_to?(method.to_sym) }
|
42
|
-
end
|
43
|
-
|
44
|
-
def find_children_by_class(klass, recurse: false)
|
45
|
-
return recursive_select_children { |child| child.instance_of?(klass) } if recurse
|
46
|
-
|
47
|
-
children.select { |child| child.instance_of?(klass) }
|
48
|
-
end
|
49
|
-
|
50
|
-
def find_child_by_class(klass, recurse: false)
|
51
|
-
return recursive_find_child { |child| child.is_a?(klass) } if recurse
|
52
|
-
|
53
|
-
find_children_by_class(klass).first
|
54
|
-
end
|
55
|
-
|
56
|
-
def find_children_by_xpath(xpath, recurse: false)
|
57
|
-
return recursive_select_children { |child| child.xpath == xpath } if recurse
|
58
|
-
|
59
|
-
children.select { |child| child.xpath == xpath }
|
60
|
-
end
|
61
|
-
|
62
|
-
def find_child_by_xpath(xpath, recurse: false)
|
63
|
-
return recursive_find_child { |child| child.xpath == xpath } if recurse
|
64
|
-
|
65
|
-
find_children_by_xpath(xpath).first
|
66
|
-
end
|
67
|
-
|
68
|
-
def find_children_by_attribute(attribute, recurse: false)
|
69
|
-
pr = proc do |child|
|
70
|
-
next unless child.instance_of?(AbideDevUtils::XCCDF::Parser::Objects::AttributeValue)
|
71
|
-
|
72
|
-
child.attribute == attribute
|
73
|
-
end
|
74
|
-
return recursive_select_children(&pr) if recurse
|
75
|
-
|
76
|
-
children.select(&pr)
|
77
|
-
end
|
78
|
-
|
79
|
-
def find_child_by_attribute(attribute, recurse: false)
|
80
|
-
find_children_by_attribute(attribute, recurse: recurse).first
|
81
|
-
end
|
82
|
-
|
83
|
-
def find_children_by_attribute_value(attribute, value, recurse: false)
|
84
|
-
pr = proc do |child|
|
85
|
-
next unless child.instance_of?(AbideDevUtils::XCCDF::Parser::Objects::AttributeValue)
|
86
|
-
|
87
|
-
child.attribute == attribute && child.value == value
|
88
|
-
end
|
89
|
-
return recursive_select_children(&pr) if recurse
|
90
|
-
|
91
|
-
children.select(&pr)
|
92
|
-
end
|
93
|
-
|
94
|
-
def find_child_by_attribute_value(attribute, value, recurse: false)
|
95
|
-
find_children_by_attribute_value(attribute, value, recurse: recurse).first
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
7
|
# Provides helper methods for working with XML xpaths
|
101
8
|
module XPath
|
102
9
|
def find_element
|