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.
- checksums.yaml +4 -4
- data/Gemfile.lock +24 -38
- data/abide_dev_utils.gemspec +0 -1
- data/lib/abide_dev_utils/cem/benchmark.rb +0 -2
- 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/ppt/hiera.rb +7 -24
- 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
|