abide_dev_utils 0.9.7 → 0.10.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 -8
- data/abide_dev_utils.gemspec +1 -0
- data/lib/abide_dev_utils/cem.rb +72 -0
- data/lib/abide_dev_utils/cli/cem.rb +73 -0
- data/lib/abide_dev_utils/cli/xccdf.rb +12 -1
- data/lib/abide_dev_utils/cli.rb +2 -0
- data/lib/abide_dev_utils/files.rb +34 -0
- data/lib/abide_dev_utils/version.rb +1 -1
- data/lib/abide_dev_utils/xccdf/diff/benchmark/number_title.rb +270 -0
- data/lib/abide_dev_utils/xccdf/diff/benchmark/profile.rb +104 -0
- data/lib/abide_dev_utils/xccdf/diff/benchmark/property.rb +127 -0
- data/lib/abide_dev_utils/xccdf/diff/benchmark/property_existence.rb +47 -0
- data/lib/abide_dev_utils/xccdf/diff/benchmark.rb +267 -0
- data/lib/abide_dev_utils/xccdf/diff/utils.rb +30 -0
- data/lib/abide_dev_utils/xccdf/diff.rb +233 -0
- data/lib/abide_dev_utils/xccdf/parser/objects/digest_object.rb +118 -0
- data/lib/abide_dev_utils/xccdf/parser/objects/numbered_object.rb +104 -0
- data/lib/abide_dev_utils/xccdf/parser/objects.rb +741 -0
- data/lib/abide_dev_utils/xccdf/parser.rb +52 -0
- data/lib/abide_dev_utils/xccdf.rb +9 -122
- data/new_diff.rb +48 -0
- metadata +34 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4fc3820895d385ca28d6e097c1822cfaf878d41e2b282bfca55bec2e884a2dc2
|
4
|
+
data.tar.gz: de27ab8fb2021de5cd437695566e4ab0ad0531bc94659948557b3b09a05d59f5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7df796781a8f4e4b87cf324c83273a9d2964f44f8dc8fd9f68c65610295f7b9f5161b70c23d66ab19ff75ca610860d27245256dabf6e69633850cbd2d1f3dd96
|
7
|
+
data.tar.gz: 3cf0142c1a0f3fae3bab07f65add4a320f90ca900adc5be63b77f72e40edba4936b6ea835796d0fd6b2c3cff034b6a2dcb4f5bd1ff43a476b40bcc053c5356cb
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
abide_dev_utils (0.
|
4
|
+
abide_dev_utils (0.10.0)
|
5
|
+
amatch (~> 0.4)
|
5
6
|
cmdparse (~> 3.0)
|
6
7
|
google-cloud-storage (~> 1.34)
|
7
8
|
hashdiff (~> 1.0)
|
@@ -21,6 +22,9 @@ GEM
|
|
21
22
|
tzinfo (~> 2.0)
|
22
23
|
addressable (2.8.0)
|
23
24
|
public_suffix (>= 2.0.2, < 5.0)
|
25
|
+
amatch (0.4.0)
|
26
|
+
mize
|
27
|
+
tins (~> 1.0)
|
24
28
|
ast (2.4.2)
|
25
29
|
async (1.30.1)
|
26
30
|
console (~> 1.10)
|
@@ -53,7 +57,7 @@ GEM
|
|
53
57
|
diff-lcs (1.5.0)
|
54
58
|
digest-crc (0.6.4)
|
55
59
|
rake (>= 12.0.0, < 14.0.0)
|
56
|
-
facter (4.2.
|
60
|
+
facter (4.2.9)
|
57
61
|
hocon (~> 1.3)
|
58
62
|
thor (>= 1.0.1, < 2.0)
|
59
63
|
faraday (1.10.0)
|
@@ -104,13 +108,13 @@ GEM
|
|
104
108
|
webrick
|
105
109
|
google-apis-iamcredentials_v1 (0.10.0)
|
106
110
|
google-apis-core (>= 0.4, < 2.a)
|
107
|
-
google-apis-storage_v1 (0.
|
111
|
+
google-apis-storage_v1 (0.13.0)
|
108
112
|
google-apis-core (>= 0.4, < 2.a)
|
109
113
|
google-cloud-core (1.6.0)
|
110
114
|
google-cloud-env (~> 1.0)
|
111
115
|
google-cloud-errors (~> 1.0)
|
112
|
-
google-cloud-env (1.
|
113
|
-
faraday (>= 0.17.3, <
|
116
|
+
google-cloud-env (1.6.0)
|
117
|
+
faraday (>= 0.17.3, < 3.0)
|
114
118
|
google-cloud-errors (1.2.0)
|
115
119
|
google-cloud-storage (1.36.1)
|
116
120
|
addressable (~> 2.8)
|
@@ -128,7 +132,7 @@ GEM
|
|
128
132
|
os (>= 0.9, < 2.0)
|
129
133
|
signet (>= 0.16, < 2.a)
|
130
134
|
hashdiff (1.0.1)
|
131
|
-
hiera (3.
|
135
|
+
hiera (3.9.0)
|
132
136
|
hocon (1.3.1)
|
133
137
|
httpclient (2.8.3)
|
134
138
|
i18n (1.10.0)
|
@@ -143,11 +147,15 @@ GEM
|
|
143
147
|
memoist (0.16.2)
|
144
148
|
method_source (1.0.0)
|
145
149
|
mini_mime (1.1.2)
|
150
|
+
mini_portile2 (2.8.0)
|
146
151
|
minitest (5.15.0)
|
152
|
+
mize (0.4.0)
|
153
|
+
protocol (~> 2.0)
|
147
154
|
multi_json (1.15.0)
|
148
155
|
multipart-post (2.1.1)
|
149
156
|
nio4r (2.5.8)
|
150
|
-
nokogiri (1.13.
|
157
|
+
nokogiri (1.13.6)
|
158
|
+
mini_portile2 (~> 2.8.0)
|
151
159
|
racc (~> 1.4)
|
152
160
|
oauth (0.5.8)
|
153
161
|
octokit (4.22.0)
|
@@ -157,6 +165,8 @@ GEM
|
|
157
165
|
parallel (1.21.0)
|
158
166
|
parser (3.1.1.0)
|
159
167
|
ast (~> 2.4.1)
|
168
|
+
protocol (2.0.0)
|
169
|
+
ruby_parser (~> 3.0)
|
160
170
|
protocol-hpack (1.4.2)
|
161
171
|
protocol-http (0.22.5)
|
162
172
|
protocol-http1 (0.14.2)
|
@@ -168,7 +178,7 @@ GEM
|
|
168
178
|
coderay (~> 1.1)
|
169
179
|
method_source (~> 1.0)
|
170
180
|
public_suffix (4.0.6)
|
171
|
-
puppet (7.
|
181
|
+
puppet (7.16.0)
|
172
182
|
concurrent-ruby (~> 1.0)
|
173
183
|
deep_merge (~> 1.0)
|
174
184
|
facter (> 2.0.1, < 5)
|
@@ -224,6 +234,8 @@ GEM
|
|
224
234
|
rubocop (~> 1.19)
|
225
235
|
ruby-progressbar (1.11.0)
|
226
236
|
ruby2_keywords (0.0.5)
|
237
|
+
ruby_parser (3.19.1)
|
238
|
+
sexp_processor (~> 4.16)
|
227
239
|
rubyzip (2.3.2)
|
228
240
|
sawyer (0.8.2)
|
229
241
|
addressable (>= 2.3.5)
|
@@ -234,13 +246,17 @@ GEM
|
|
234
246
|
rexml (~> 3.2, >= 3.2.5)
|
235
247
|
rubyzip (>= 1.2.2)
|
236
248
|
semantic_puppet (1.0.4)
|
249
|
+
sexp_processor (4.16.1)
|
237
250
|
signet (0.16.1)
|
238
251
|
addressable (~> 2.8)
|
239
252
|
faraday (>= 0.17.5, < 3.0)
|
240
253
|
jwt (>= 1.5, < 3.0)
|
241
254
|
multi_json (~> 1.10)
|
255
|
+
sync (0.5.0)
|
242
256
|
thor (1.2.1)
|
243
257
|
timers (4.3.3)
|
258
|
+
tins (1.31.0)
|
259
|
+
sync
|
244
260
|
trailblazer-option (0.1.2)
|
245
261
|
tzinfo (2.0.4)
|
246
262
|
concurrent-ruby (~> 1.0)
|
data/abide_dev_utils.gemspec
CHANGED
@@ -40,6 +40,7 @@ Gem::Specification.new do |spec|
|
|
40
40
|
spec.add_dependency 'selenium-webdriver', '~> 4.0.0.beta4'
|
41
41
|
spec.add_dependency 'google-cloud-storage', '~> 1.34'
|
42
42
|
spec.add_dependency 'hashdiff', '~> 1.0'
|
43
|
+
spec.add_dependency 'amatch', '~> 0.4'
|
43
44
|
|
44
45
|
# Dev dependencies
|
45
46
|
spec.add_development_dependency 'bundler'
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'abide_dev_utils/xccdf'
|
4
|
+
|
5
|
+
module AbideDevUtils
|
6
|
+
# Methods for working with Compliance Enforcement Modules (CEM)
|
7
|
+
module CEM
|
8
|
+
def self.xccdf
|
9
|
+
return @xccdf if defined?(@xccdf)
|
10
|
+
|
11
|
+
xccdf = Object.new
|
12
|
+
xccdf.extend AbideDevUtils::XCCDF::Common
|
13
|
+
@xccdf = xccdf
|
14
|
+
@xccdf
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.rule_id_format(rule_id)
|
18
|
+
case rule_id
|
19
|
+
when /^c[0-9_]+$/
|
20
|
+
:hiera_title_num
|
21
|
+
when /^[a-z][a-z0-9_]+$/
|
22
|
+
:hiera_title
|
23
|
+
when /^[0-9.]+$/
|
24
|
+
:number
|
25
|
+
else
|
26
|
+
:title
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.rule_identifiers(rule_id)
|
31
|
+
{
|
32
|
+
number: xccdf.control_parts(rule_id).first,
|
33
|
+
hiera_title: xccdf.name_normalize_control(rule_id),
|
34
|
+
hiera_title_num: xccdf.number_normalize_control(rule_id),
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.update_legacy_config_from_diff(config_hiera, diff)
|
39
|
+
new_config_hiera = config_hiera.dup
|
40
|
+
new_control_configs = {}
|
41
|
+
change_report = []
|
42
|
+
changes = diff.select { |d| d[:type][0] == :number }
|
43
|
+
config_hiera['config']['control_configs'].each do |key, val_hash|
|
44
|
+
key_id_format = rule_id_format(key)
|
45
|
+
changed = false
|
46
|
+
changes.each do |change|
|
47
|
+
if key_id_format == :title
|
48
|
+
next unless change[:title] == key
|
49
|
+
else
|
50
|
+
next unless rule_identifiers(change[:self].id)[key_id_format] == key
|
51
|
+
end
|
52
|
+
|
53
|
+
changed = true
|
54
|
+
new_key = if key_id_format == :title
|
55
|
+
change[:other_title]
|
56
|
+
else
|
57
|
+
rule_identifiers(change[:other].id)[key_id_format]
|
58
|
+
end
|
59
|
+
new_control_configs[new_key] = val_hash
|
60
|
+
change_report << {
|
61
|
+
type: :identifier_update,
|
62
|
+
from: key,
|
63
|
+
to: new_key,
|
64
|
+
}
|
65
|
+
end
|
66
|
+
new_control_configs[key] = val_hash unless changed
|
67
|
+
end
|
68
|
+
new_config_hiera['config']['control_configs'] = new_control_configs
|
69
|
+
[new_config_hiera, change_report]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'abide_dev_utils/cem'
|
4
|
+
require 'abide_dev_utils/files'
|
5
|
+
require 'abide_dev_utils/output'
|
6
|
+
require 'abide_dev_utils/validate'
|
7
|
+
require 'abide_dev_utils/xccdf/diff/benchmark'
|
8
|
+
require 'abide_dev_utils/cli/abstract'
|
9
|
+
|
10
|
+
module Abide
|
11
|
+
module CLI
|
12
|
+
class CemCommand < AbideCommand
|
13
|
+
CMD_NAME = 'cem'
|
14
|
+
CMD_SHORT = 'Commands related to Puppet CEM'
|
15
|
+
CMD_LONG = 'Namespace for commands related to Puppet CEM'
|
16
|
+
def initialize
|
17
|
+
super(CMD_NAME, CMD_SHORT, CMD_LONG, takes_commands: true)
|
18
|
+
add_command(CemUpdateConfig.new)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class CemUpdateConfig < AbideCommand
|
23
|
+
CMD_NAME = 'update-config'
|
24
|
+
CMD_SHORT = 'Updates the Puppet CEM config'
|
25
|
+
CMD_LONG = 'Updates the Puppet CEM config'
|
26
|
+
def initialize
|
27
|
+
super(CMD_NAME, CMD_SHORT, CMD_LONG, takes_commands: true)
|
28
|
+
add_command(CemUpdateConfigFromDiff.new)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class CemUpdateConfigFromDiff < AbideCommand
|
33
|
+
CMD_NAME = 'from-diff'
|
34
|
+
CMD_SHORT = 'Update by diffing two XCCDF files'
|
35
|
+
CMD_LONG = 'Update by diffing two XCCDF files'
|
36
|
+
CMD_CONFIG_FILE = 'Path to the Puppet CEM config file'
|
37
|
+
CMD_CURRENT_XCCDF = 'Path to the current XCCDF file'
|
38
|
+
CMD_NEW_XCCDF = 'Path to the new XCCDF file'
|
39
|
+
def initialize
|
40
|
+
super(CMD_NAME, CMD_SHORT, CMD_LONG, takes_commands: false)
|
41
|
+
argument_desc(CONFIG_FILE: CMD_CONFIG_FILE, CURRENT_XCCDF: CMD_CURRENT_XCCDF, NEW_XCCDF: CMD_NEW_XCCDF)
|
42
|
+
options.on('-o [FILE]', '--out-file [FILE]', 'Path to save the updated config file') do |o|
|
43
|
+
@data[:out_file] = o
|
44
|
+
end
|
45
|
+
options.on('-v', '--verbose', 'Verbose output') do
|
46
|
+
@data[:verbose] = true
|
47
|
+
end
|
48
|
+
options.on('-q', '--quiet', 'Quiet output') do
|
49
|
+
@data[:quiet] = true
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def help_arguments
|
54
|
+
<<~ARGHELP
|
55
|
+
Arguments:
|
56
|
+
CONFIG_FILE: #{CMD_CONFIG_FILE}
|
57
|
+
CURRENT_XCCDF: #{CMD_CURRENT_XCCDF}
|
58
|
+
NEW_XCCDF: #{CMD_NEW_XCCDF}
|
59
|
+
ARGHELP
|
60
|
+
end
|
61
|
+
|
62
|
+
def execute(config_file, cur_xccdf, new_xccdf)
|
63
|
+
AbideDevUtils::Validate.file(config_file, extension: 'yaml')
|
64
|
+
AbideDevUtils::Validate.file(cur_xccdf, extension: 'xml')
|
65
|
+
config_hiera = AbideDevUtils::Files::Reader.read(config_file, safe: true)
|
66
|
+
diff = AbideDevUtils::XCCDF::Diff::BenchmarkDiff.new(cur_xccdf, new_xccdf).diff[:diff][:number_title]
|
67
|
+
new_config_hiera, change_report = AbideDevUtils::CEM.update_legacy_config_from_diff(config_hiera, diff)
|
68
|
+
AbideDevUtils::Output.yaml(new_config_hiera, console: @data[:verbose], file: @data[:out_file])
|
69
|
+
AbideDevUtils::Output.simple(change_report) unless @data[:quiet]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -104,13 +104,24 @@ module Abide
|
|
104
104
|
options.on('-p [PROFILE]', '--profile', 'Only diff and specific profile in the benchmarks') do |x|
|
105
105
|
@data[:profile] = x
|
106
106
|
end
|
107
|
+
options.on('-l [LEVEL]', '--level', 'Only diff the specific level in the benchmarks') do |x|
|
108
|
+
@data[:level] = x
|
109
|
+
end
|
110
|
+
options.on('-r', '--raw', 'Output the diff in raw hash format') { @data[:raw] = true }
|
107
111
|
options.on('-q', '--quiet', 'Show no output in the terminal') { @data[:quiet] = false }
|
108
112
|
options.on('--no-diff-profiles', 'Do not diff the profiles in the XCCDF files') { @data[:diff_profiles] = false }
|
109
113
|
options.on('--no-diff-controls', 'Do not diff the controls in the XCCDF files') { @data[:diff_controls] = false }
|
114
|
+
options.on('--old-style', 'Use old-style diffs') { @data[:old_style] = true }
|
110
115
|
end
|
111
116
|
|
112
117
|
def execute(file1, file2)
|
113
|
-
diffreport =
|
118
|
+
diffreport = if @data[:old_style]
|
119
|
+
AbideDevUtils::XCCDF.diff(file1, file2, @data)
|
120
|
+
else
|
121
|
+
dr = AbideDevUtils::XCCDF.new_style_diff(file1, file2, @data)
|
122
|
+
dr[:diff][:number_title].map! { |d| d[:text] }
|
123
|
+
dr
|
124
|
+
end
|
114
125
|
AbideDevUtils::Output.yaml(diffreport, console: @data.fetch(:quiet, true), file: @data.fetch(:outfile, nil))
|
115
126
|
end
|
116
127
|
end
|
data/lib/abide_dev_utils/cli.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'cmdparse'
|
4
4
|
require 'abide_dev_utils/version'
|
5
|
+
require 'abide_dev_utils/cli/cem'
|
5
6
|
require 'abide_dev_utils/constants'
|
6
7
|
require 'abide_dev_utils/cli/comply'
|
7
8
|
require 'abide_dev_utils/cli/puppet'
|
@@ -22,6 +23,7 @@ module Abide
|
|
22
23
|
parser.main_options.banner = ROOT_CMD_BANNER
|
23
24
|
parser.add_command(CmdParse::HelpCommand.new, default: true)
|
24
25
|
parser.add_command(CmdParse::VersionCommand.new(add_switches: true))
|
26
|
+
parser.add_command(CemCommand.new)
|
25
27
|
parser.add_command(ComplyCommand.new)
|
26
28
|
parser.add_command(PuppetCommand.new)
|
27
29
|
parser.add_command(XccdfCommand.new)
|
@@ -1,7 +1,41 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'abide_dev_utils/validate'
|
4
|
+
|
3
5
|
module AbideDevUtils
|
4
6
|
module Files
|
7
|
+
class Reader
|
8
|
+
def self.read(path, raw: false, safe: true, opts: {})
|
9
|
+
AbideDevUtils::Validate.file(path)
|
10
|
+
return File.read(path) if raw
|
11
|
+
|
12
|
+
extension = File.extname(path)
|
13
|
+
case extension
|
14
|
+
when /\.yaml|\.yml/
|
15
|
+
require 'yaml'
|
16
|
+
if safe
|
17
|
+
YAML.safe_load(File.read(path))
|
18
|
+
else
|
19
|
+
YAML.load_file(path)
|
20
|
+
end
|
21
|
+
when '.json'
|
22
|
+
require 'json'
|
23
|
+
return JSON.parse(File.read(path), opts) if safe
|
24
|
+
|
25
|
+
JSON.parse!(File.read(path), opts)
|
26
|
+
when '.xml'
|
27
|
+
require 'nokogiri'
|
28
|
+
File.open(path, 'r') do |file|
|
29
|
+
Nokogiri::XML.parse(file) do |config|
|
30
|
+
config.strict.noblanks.norecover
|
31
|
+
end
|
32
|
+
end
|
33
|
+
else
|
34
|
+
File.read(path)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
5
39
|
class Writer
|
6
40
|
MSG_EXT_APPEND = 'Appending %s extension to file'
|
7
41
|
|
@@ -0,0 +1,270 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'abide_dev_utils/xccdf/diff/benchmark/property_existence'
|
4
|
+
|
5
|
+
module AbideDevUtils
|
6
|
+
module XCCDF
|
7
|
+
module Diff
|
8
|
+
# Diffs two XCCDF benchmarks using the title / number of the items as the primary
|
9
|
+
# diff properties.
|
10
|
+
class NumberTitleDiff
|
11
|
+
SKIP_DIFF_TYPES = %i[equal both].freeze
|
12
|
+
|
13
|
+
def initialize(numbered_children, other_numbered_children)
|
14
|
+
new_number_title_objs(numbered_children, other_numbered_children)
|
15
|
+
end
|
16
|
+
|
17
|
+
def diff
|
18
|
+
@diff ||= find_diffs(@number_title_objs, @other_number_title_objs)
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
parts = []
|
23
|
+
@diff.each do |_, diffs|
|
24
|
+
diffs.each do |dh|
|
25
|
+
parts << dh[:diff_text]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
parts.join("\n")
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
attr_writer :diff
|
34
|
+
|
35
|
+
def added_number_title_objs
|
36
|
+
added_titles = @self_prop_checker.added_titles
|
37
|
+
@other_number_title_objs.select do |nto|
|
38
|
+
added_titles.include?(nto.title)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def removed_number_title_objs
|
43
|
+
removed_titles = @self_prop_checker.removed_titles
|
44
|
+
@number_title_objs.select do |nto|
|
45
|
+
removed_titles.include?(nto.title)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def find_diffs(objs, other_objs)
|
50
|
+
diffs = []
|
51
|
+
added_number_title_objs.each do |nto|
|
52
|
+
change_type = %i[both added]
|
53
|
+
stand_in = NumberTitleContainerStandIn.new(change_type)
|
54
|
+
diffs << process_diffs([diff_hash(change_type, stand_in, nto)])
|
55
|
+
end
|
56
|
+
removed_number_title_objs.each do |nto|
|
57
|
+
change_type = %i[both removed]
|
58
|
+
stand_in = NumberTitleContainerStandIn.new(change_type)
|
59
|
+
diffs << process_diffs([diff_hash(change_type, nto, stand_in)])
|
60
|
+
end
|
61
|
+
objs.each do |obj|
|
62
|
+
obj_diffs = other_objs.each_with_object([]) do |other_obj, o_ary|
|
63
|
+
obj_diff = obj.diff(other_obj)
|
64
|
+
next if SKIP_DIFF_TYPES.include?(obj_diff[0])
|
65
|
+
|
66
|
+
o_ary << diff_hash(obj_diff, obj, other_obj)
|
67
|
+
end
|
68
|
+
|
69
|
+
processed_obj_diffs = process_diffs(obj_diffs)
|
70
|
+
diffs << processed_obj_diffs unless obj_diffs.empty?
|
71
|
+
end
|
72
|
+
diffs
|
73
|
+
end
|
74
|
+
|
75
|
+
def process_diffs(diffs)
|
76
|
+
return {} if diffs.empty?
|
77
|
+
|
78
|
+
raise "Unexpected diffs: #{diffs}" if diffs.length > 2
|
79
|
+
|
80
|
+
return diffs[0] if diffs.length == 1
|
81
|
+
|
82
|
+
if diffs[0][:type][0] == PropChecker.inverse_existence_state[diffs[1][:type][0]]
|
83
|
+
diffs[0]
|
84
|
+
else
|
85
|
+
diffs[1]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def diff_hash(diff_type, obj, other_obj)
|
90
|
+
{
|
91
|
+
self: obj.child,
|
92
|
+
other: other_obj.child,
|
93
|
+
type: diff_type,
|
94
|
+
text: diff_type_text(diff_type, obj, other_obj),
|
95
|
+
number: obj.number,
|
96
|
+
other_number: other_obj.number,
|
97
|
+
title: obj.title,
|
98
|
+
other_title: other_obj.title,
|
99
|
+
}
|
100
|
+
end
|
101
|
+
|
102
|
+
def diff_type_text(diff_type, obj, other_obj)
|
103
|
+
DiffTypeText.text(diff_type, obj, other_obj)
|
104
|
+
end
|
105
|
+
|
106
|
+
def new_number_title_objs(children, other_children)
|
107
|
+
number_title_objs = children.map { |c| NumberTitleContainer.new(c) }.sort
|
108
|
+
other_number_title_objs = other_children.map { |c| NumberTitleContainer.new(c) }.sort
|
109
|
+
@self_prop_checker = PropChecker.new(number_title_objs, other_number_title_objs)
|
110
|
+
@other_prop_checker = PropChecker.new(other_number_title_objs, number_title_objs)
|
111
|
+
number_title_objs.map { |n| n.prop_checker = @self_prop_checker }
|
112
|
+
other_number_title_objs.map { |n| n.prop_checker = @other_prop_checker }
|
113
|
+
@number_title_objs = number_title_objs
|
114
|
+
@other_number_title_objs = other_number_title_objs
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Creates string representations of diff types
|
119
|
+
class DiffTypeText
|
120
|
+
def self.text(diff_type, obj, other_obj)
|
121
|
+
case diff_type[0]
|
122
|
+
when :equal
|
123
|
+
'The objects are equal'
|
124
|
+
when :title
|
125
|
+
"Title changed: Number \"#{obj.number}\": #{obj.title} -> #{other_obj.title}"
|
126
|
+
when :number
|
127
|
+
number_diff_type_text(diff_type, obj, other_obj)
|
128
|
+
when :both
|
129
|
+
both_diff_type_text(diff_type, obj, other_obj)
|
130
|
+
when :add
|
131
|
+
"Add object with number \"#{other_obj.number}\" and title \"#{other_obj.title}\""
|
132
|
+
when :remove
|
133
|
+
"Remove object with number \"#{obj.number}\" and title \"#{obj.title}\""
|
134
|
+
else
|
135
|
+
raise ArgumentError, "Unknown diff type: #{diff_type}"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def self.number_diff_type_text(diff_type, obj, other_obj)
|
140
|
+
case diff_type[1]
|
141
|
+
when :added
|
142
|
+
"Number changed (New Number): Title \"#{obj.title}\": #{obj.number} -> #{other_obj.number}"
|
143
|
+
when :exists
|
144
|
+
"Number changed (Existing Number): Title \"#{obj.title}\": #{obj.number} -> #{other_obj.number}"
|
145
|
+
else
|
146
|
+
raise ArgumentError, "Unknown diff type for number change: #{diff_type[1]}"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def self.both_diff_type_text(diff_type, obj, other_obj)
|
151
|
+
case diff_type[1]
|
152
|
+
when :added
|
153
|
+
"Added object: Title \"#{other_obj.title}\": Number \"#{other_obj.number}\""
|
154
|
+
when :removed
|
155
|
+
"Removed object: Title \"#{obj.title}\": Number \"#{obj.number}\""
|
156
|
+
else
|
157
|
+
raise ArgumentError, "Unknown diff type for both change: #{diff_type[1]}"
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# Checks properties for existence in both benchmarks.
|
163
|
+
class PropChecker < AbideDevUtils::XCCDF::Diff::PropertyExistenceChecker
|
164
|
+
attr_reader :all_numbers, :all_titles, :other_all_numbers, :other_all_titles
|
165
|
+
|
166
|
+
def initialize(number_title_objs, other_number_title_objs)
|
167
|
+
super
|
168
|
+
@all_numbers = number_title_objs.map(&:number)
|
169
|
+
@all_titles = number_title_objs.map(&:title)
|
170
|
+
@other_all_numbers = other_number_title_objs.map(&:number)
|
171
|
+
@other_all_titles = other_number_title_objs.map(&:title)
|
172
|
+
end
|
173
|
+
|
174
|
+
def title(title)
|
175
|
+
property_existence(title, @all_titles, @other_all_titles)
|
176
|
+
end
|
177
|
+
|
178
|
+
def number(number)
|
179
|
+
property_existence(number, @all_numbers, @other_all_numbers)
|
180
|
+
end
|
181
|
+
|
182
|
+
def added_numbers
|
183
|
+
added(@all_numbers, @other_all_numbers)
|
184
|
+
end
|
185
|
+
|
186
|
+
def removed_numbers
|
187
|
+
removed(@all_numbers, @other_all_numbers)
|
188
|
+
end
|
189
|
+
|
190
|
+
def added_titles
|
191
|
+
added(@all_titles, @other_all_titles)
|
192
|
+
end
|
193
|
+
|
194
|
+
def removed_titles
|
195
|
+
removed(@all_titles, @other_all_titles)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
class NumberTitleDiffError < StandardError; end
|
200
|
+
class InconsistentDiffTypeError < StandardError; end
|
201
|
+
|
202
|
+
# Holds a number and title for a child of a benchmark
|
203
|
+
# and provides methods to compare it to another child.
|
204
|
+
class NumberTitleContainer
|
205
|
+
include ::Comparable
|
206
|
+
attr_accessor :prop_checker
|
207
|
+
attr_reader :child, :number, :title
|
208
|
+
|
209
|
+
def initialize(child, prop_checker = nil)
|
210
|
+
@child = child
|
211
|
+
@number = child.number.to_s
|
212
|
+
@title = child.title.to_s
|
213
|
+
@prop_checker = prop_checker
|
214
|
+
end
|
215
|
+
|
216
|
+
def diff(other)
|
217
|
+
return %i[equal exist] if number == other.number && title == other.title
|
218
|
+
|
219
|
+
if number == other.number && title != other.title
|
220
|
+
c_diff = correlate_prop_diff_types(@prop_checker.title(other.title),
|
221
|
+
other.prop_checker.title(other.title))
|
222
|
+
[:title, c_diff]
|
223
|
+
elsif title == other.title && number != other.number
|
224
|
+
c_diff = correlate_prop_diff_types(@prop_checker.number(other.number),
|
225
|
+
other.prop_checker.number(other.number))
|
226
|
+
[:number, c_diff]
|
227
|
+
else
|
228
|
+
%i[both exist]
|
229
|
+
end
|
230
|
+
rescue StandardError => e
|
231
|
+
err_str = [
|
232
|
+
'Error diffing number and title',
|
233
|
+
"Number: #{number}",
|
234
|
+
"Title: #{title}",
|
235
|
+
"Other number: #{other.number}",
|
236
|
+
"Other title: #{other.title}",
|
237
|
+
e.message,
|
238
|
+
]
|
239
|
+
raise NumberTitleDiffError, err_str.join(', ')
|
240
|
+
end
|
241
|
+
|
242
|
+
def <=>(other)
|
243
|
+
child <=> other.child
|
244
|
+
end
|
245
|
+
|
246
|
+
private
|
247
|
+
|
248
|
+
def correlate_prop_diff_types(self_type, other_type)
|
249
|
+
inverse_diff_type = PropChecker.inverse_existence_state[self_type]
|
250
|
+
return other_type if inverse_diff_type.nil?
|
251
|
+
|
252
|
+
self_type
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
# Stand-in object for a NumberTitleContainer when the NumberTitleContainer
|
257
|
+
# would not exist. This is used when a change is an add or remove.
|
258
|
+
class NumberTitleContainerStandIn
|
259
|
+
attr_reader :child, :number, :title
|
260
|
+
|
261
|
+
def initialize(change_type)
|
262
|
+
@change_type = change_type
|
263
|
+
@child = nil
|
264
|
+
@number = ''
|
265
|
+
@title = ''
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|