abide_dev_utils 0.9.7 → 0.11.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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +7 -1
  4. data/Gemfile.lock +82 -64
  5. data/Rakefile +28 -0
  6. data/abide_dev_utils.gemspec +3 -1
  7. data/lib/abide_dev_utils/cem/benchmark.rb +291 -0
  8. data/lib/abide_dev_utils/cem/coverage_report.rb +348 -0
  9. data/lib/abide_dev_utils/cem/generate/reference.rb +116 -0
  10. data/lib/abide_dev_utils/cem/generate.rb +10 -0
  11. data/lib/abide_dev_utils/cem/mapping/mapper.rb +155 -0
  12. data/lib/abide_dev_utils/cem.rb +74 -0
  13. data/lib/abide_dev_utils/cli/cem.rb +153 -0
  14. data/lib/abide_dev_utils/cli/jira.rb +1 -1
  15. data/lib/abide_dev_utils/cli/xccdf.rb +15 -1
  16. data/lib/abide_dev_utils/cli.rb +2 -0
  17. data/lib/abide_dev_utils/errors/cem.rb +22 -0
  18. data/lib/abide_dev_utils/errors/general.rb +8 -2
  19. data/lib/abide_dev_utils/errors/ppt.rb +4 -0
  20. data/lib/abide_dev_utils/errors.rb +6 -0
  21. data/lib/abide_dev_utils/files.rb +34 -0
  22. data/lib/abide_dev_utils/markdown.rb +104 -0
  23. data/lib/abide_dev_utils/ppt/facter_utils.rb +140 -0
  24. data/lib/abide_dev_utils/ppt/hiera.rb +297 -0
  25. data/lib/abide_dev_utils/ppt/puppet_module.rb +74 -0
  26. data/lib/abide_dev_utils/ppt.rb +3 -5
  27. data/lib/abide_dev_utils/validate.rb +14 -0
  28. data/lib/abide_dev_utils/version.rb +1 -1
  29. data/lib/abide_dev_utils/xccdf/diff/benchmark/number_title.rb +270 -0
  30. data/lib/abide_dev_utils/xccdf/diff/benchmark/profile.rb +104 -0
  31. data/lib/abide_dev_utils/xccdf/diff/benchmark/property.rb +127 -0
  32. data/lib/abide_dev_utils/xccdf/diff/benchmark/property_existence.rb +47 -0
  33. data/lib/abide_dev_utils/xccdf/diff/benchmark.rb +267 -0
  34. data/lib/abide_dev_utils/xccdf/diff/utils.rb +30 -0
  35. data/lib/abide_dev_utils/xccdf/diff.rb +233 -0
  36. data/lib/abide_dev_utils/xccdf/parser/objects/digest_object.rb +118 -0
  37. data/lib/abide_dev_utils/xccdf/parser/objects/numbered_object.rb +104 -0
  38. data/lib/abide_dev_utils/xccdf/parser/objects.rb +741 -0
  39. data/lib/abide_dev_utils/xccdf/parser.rb +52 -0
  40. data/lib/abide_dev_utils/xccdf.rb +14 -124
  41. data/new_diff.rb +48 -0
  42. metadata +60 -9
  43. data/lib/abide_dev_utils/ppt/coverage.rb +0 -86
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'abide_dev_utils/files'
4
+ require 'abide_dev_utils/xccdf/parser/objects'
5
+
6
+ module AbideDevUtils
7
+ module XCCDF
8
+ # Contains methods and classes for parsing XCCDF files,
9
+ module Parser
10
+ def self.parse(file_path)
11
+ doc = AbideDevUtils::Files::Reader.read(file_path)
12
+ benchmark = AbideDevUtils::XCCDF::Parser::Objects::Benchmark.new(doc)
13
+ Linker.resolve_links(benchmark)
14
+ benchmark
15
+ end
16
+
17
+ # Links XCCDF objects by reference.
18
+ # Each link is resolved and then a bidirectional link is established
19
+ # between the two objects.
20
+ module Linker
21
+ def self.resolve_links(benchmark)
22
+ link_profile_rules(benchmark)
23
+ link_rule_values(benchmark)
24
+ end
25
+
26
+ def self.link_profile_rules(benchmark)
27
+ rules = benchmark.find_children_by_class(AbideDevUtils::XCCDF::Parser::Objects::Rule, recurse: true)
28
+ benchmark.profile.each do |profile|
29
+ profile.xccdf_select.each do |sel|
30
+ rules.select { |rule| rule.id == sel.idref }.each do |rule|
31
+ rule.add_link(profile)
32
+ profile.add_link(rule)
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ def self.link_rule_values(benchmark)
39
+ rules = benchmark.find_children_by_class(AbideDevUtils::XCCDF::Parser::Objects::Rule, recurse: true)
40
+ benchmark.value.each do |value|
41
+ rules.each do |rule|
42
+ unless rule.find_children_by_attribute_value('value-id', value.id, recurse: true).empty?
43
+ rule.add_link(value)
44
+ value.add_link(rule)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -34,21 +34,17 @@ module AbideDevUtils
34
34
 
35
35
  # Diffs two xccdf files
36
36
  def self.diff(file1, file2, opts)
37
+ require 'abide_dev_utils/xccdf/diff'
37
38
  bm1 = Benchmark.new(file1)
38
39
  bm2 = Benchmark.new(file2)
39
- profile = opts.fetch(:profile, nil)
40
- profile_diff = if profile.nil?
41
- bm1.diff_profiles(bm2).each do |_, v|
42
- v.transform_values! { |x| x.map!(&:to_s) }
43
- end
44
- else
45
- bm1.diff_profiles(bm2)[profile].transform_values! { |x| x.map!(&:to_s) }
46
- end
47
- profile_key = profile.nil? ? 'all_profiles' : profile
48
- {
49
- 'benchmark' => bm1.diff_title_version(bm2),
50
- profile_key => profile_diff
51
- }
40
+ AbideDevUtils::XCCDF::Diff.diff_benchmarks(bm1, bm2, opts)
41
+ end
42
+
43
+ # Use new-style diff
44
+ def self.new_style_diff(file1, file2, opts)
45
+ require 'abide_dev_utils/xccdf/diff/benchmark'
46
+ bm_diff = AbideDevUtils::XCCDF::Diff::BenchmarkDiff.new(file1, file2, opts)
47
+ bm_diff.diff
52
48
  end
53
49
 
54
50
  # Common constants and methods included by nearly everything else
@@ -164,25 +160,6 @@ module AbideDevUtils
164
160
  diff_properties.map { |x| send(x) } == other.diff_properties.map { |x| other.send(x) }
165
161
  end
166
162
 
167
- def default_diff_opts
168
- {
169
- similarity: 1,
170
- strict: true,
171
- strip: true,
172
- array_path: true,
173
- delimiter: '//',
174
- use_lcs: false
175
- }
176
- end
177
-
178
- def diff(other, **opts)
179
- Hashdiff.diff(
180
- to_h,
181
- other.to_h,
182
- default_diff_opts.merge(opts)
183
- )
184
- end
185
-
186
163
  def abide_object?
187
164
  true
188
165
  end
@@ -231,9 +208,12 @@ module AbideDevUtils
231
208
  profiles.select { |x| x.title == profile_title }.controls
232
209
  end
233
210
 
234
- def gen_map(dir: nil, type: 'CIS', parent_key_prefix: '', **_)
211
+ def gen_map(dir: nil, type: 'cis', parent_key_prefix: '', version_output_dir: false, **_)
235
212
  os, ver = facter_platform
236
- mapping_dir = dir ? File.expand_path(File.join(dir, type, os, ver)) : ''
213
+ output_path = [type, os, ver]
214
+ output_path.unshift(File.expand_path(dir)) if dir
215
+ output_path << version if version_output_dir
216
+ mapping_dir = File.expand_path(File.join(output_path))
237
217
  parent_key_prefix = '' if parent_key_prefix.nil?
238
218
  MAP_INDICES.each_with_object({}) do |idx, h|
239
219
  map_file_path = "#{mapping_dir}/#{idx}.yaml"
@@ -257,33 +237,6 @@ module AbideDevUtils
257
237
  }
258
238
  end
259
239
 
260
- def diff_title_version(other)
261
- Hashdiff.diff(
262
- to_h.reject { |k, _| k.to_s == 'profiles' },
263
- other.to_h.reject { |k, _| k.to_s == 'profiles' },
264
- default_diff_opts
265
- )
266
- end
267
-
268
- def diff_profiles(other)
269
- this_diff = {}
270
- other_hash = other.to_h[:profiles]
271
- to_h[:profiles].each do |name, data|
272
- diff_h = Hashdiff.diff(data, other_hash[name], default_diff_opts).each_with_object({}) do |x, a|
273
- val_to = x.length == 4 ? x[3] : nil
274
- a_key = x[2].is_a?(Hash) ? x[2][:title] : x[2]
275
- a[a_key] = [] unless a.key?(a_key)
276
- a[a_key] << ChangeSet.new(change: x[0], key: x[1], value: x[2], value_to: val_to)
277
- end
278
- this_diff[name] = diff_h
279
- end
280
- this_diff
281
- end
282
-
283
- def diff_controls(other)
284
- controls.diff(other.controls)
285
- end
286
-
287
240
  def map_indexed(index: 'title', framework: 'cis', key_prefix: '')
288
241
  c_map = profiles.each_with_object({}) do |profile, obj|
289
242
  obj[profile.level.downcase] = {} unless obj[profile.level.downcase].is_a?(Hash)
@@ -396,69 +349,6 @@ module AbideDevUtils
396
349
  end
397
350
  end
398
351
 
399
- class ChangeSet
400
- attr_reader :change, :key, :value, :value_to
401
-
402
- def initialize(change:, key:, value:, value_to: nil)
403
- validate_change(change)
404
- @change = change
405
- @key = key
406
- @value = value
407
- @value_to = value_to
408
- end
409
-
410
- def to_s
411
- val_to_str = value_to.nil? ? ' ' : " to #{value_to} "
412
- "#{change_string} value #{value}#{val_to_str}at #{key}"
413
- end
414
-
415
- def can_merge?(other)
416
- return false unless (change == '-' && other.change == '+') || (change == '+' && other.change == '-')
417
- return false unless key == other.key || value_hash_equality(other)
418
-
419
- true
420
- end
421
-
422
- def merge(other)
423
- unless can_merge?(other)
424
- raise ArgumentError, 'Cannot merge. Possible causes: change is identical; key or value do not match'
425
- end
426
-
427
- new_to_value = value == other.value ? nil : other.value
428
- ChangeSet.new(
429
- change: '~',
430
- key: key,
431
- value: value,
432
- value_to: new_to_value
433
- )
434
- end
435
-
436
- private
437
-
438
- def value_hash_equality(other)
439
- equality = false
440
- value.each do |k, v|
441
- equality = true if v == other.value[k]
442
- end
443
- equality
444
- end
445
-
446
- def validate_change(change)
447
- raise ArgumentError, "Change type #{change} in invalid" unless ['+', '-', '~'].include?(change)
448
- end
449
-
450
- def change_string
451
- case change
452
- when '-'
453
- 'remove'
454
- when '+'
455
- 'add'
456
- else
457
- 'change'
458
- end
459
- end
460
- end
461
-
462
352
  class ObjectContainer
463
353
  include AbideDevUtils::XCCDF::Common
464
354
 
data/new_diff.rb ADDED
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'yaml'
5
+ require 'pry'
6
+ require 'abide_dev_utils/xccdf/diff/benchmark'
7
+
8
+ xml_file1 = File.expand_path(ARGV[0])
9
+ xml_file2 = File.expand_path(ARGV[1])
10
+ legacy_config = ARGV.length > 2 ? YAML.load_file(File.expand_path(ARGV[2])) : nil
11
+
12
+ def convert_legacy_config(config, num_title_diff, key_format: :hiera_num)
13
+ nt_diff = num_title_diff.diff(key: :number)
14
+ updated_config = config['config']['control_configs'].each_with_object({}) do |(key, value), h|
15
+ next if value.nil?
16
+
17
+ diff_key = key.to_s.gsub(/^c/, '').tr('_', '.') if key_format == :hiera_num
18
+ if nt_diff.key?(diff_key)
19
+ if nt_diff[diff_key][0][:diff] == :number
20
+ new_key = "c#{nt_diff[diff_key][0][:other_number].to_s.tr('.', '_')}"
21
+ h[new_key] = value
22
+ puts "Converted #{key} to #{new_key}"
23
+ elsif nt_diff[diff_key][0][:diff] == :title
24
+
25
+ h[key] = value
26
+ end
27
+ else
28
+ h[key] = value
29
+ end
30
+ end
31
+ { 'config' => { 'control_configs' => updated_config } }.to_yaml
32
+ end
33
+
34
+ start_time = Time.now
35
+
36
+ bm_diff = AbideDevUtils::XCCDF::Diff::BenchmarkDiff.new(xml_file1, xml_file2)
37
+ self_nc_count, other_nc_count = bm_diff.numbered_children_counts
38
+ puts "Benchmark numbered children count: #{self_nc_count}"
39
+ puts "Other benchmark numbered children count: #{other_nc_count}"
40
+ puts "Rule count difference: #{bm_diff.numbered_children_count_diff}"
41
+ num_diff = bm_diff.number_title_diff
42
+ binding.pry if legacy_config.nil?
43
+ File.open('/tmp/legacy_converted.yaml', 'w') do |f|
44
+ converted = convert_legacy_config(legacy_config, num_diff)
45
+ f.write(converted)
46
+ end
47
+
48
+ puts "Computation time: #{Time.now - start_time}"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: abide_dev_utils
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.7
4
+ version: 0.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - abide-team
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-03-02 00:00:00.000000000 Z
11
+ date: 2022-07-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.11'
19
+ version: '1.13'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.11'
26
+ version: '1.13'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: cmdparse
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -122,6 +122,34 @@ dependencies:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
124
  version: '1.0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: amatch
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '0.4'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '0.4'
139
+ - !ruby/object:Gem::Dependency
140
+ name: facterdb
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '1.18'
146
+ type: :runtime
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '1.18'
125
153
  - !ruby/object:Gem::Dependency
126
154
  name: bundler
127
155
  requirement: !ruby/object:Gem::Requirement
@@ -330,8 +358,15 @@ files:
330
358
  - bin/setup
331
359
  - exe/abide
332
360
  - lib/abide_dev_utils.rb
361
+ - lib/abide_dev_utils/cem.rb
362
+ - lib/abide_dev_utils/cem/benchmark.rb
363
+ - lib/abide_dev_utils/cem/coverage_report.rb
364
+ - lib/abide_dev_utils/cem/generate.rb
365
+ - lib/abide_dev_utils/cem/generate/reference.rb
366
+ - lib/abide_dev_utils/cem/mapping/mapper.rb
333
367
  - lib/abide_dev_utils/cli.rb
334
368
  - lib/abide_dev_utils/cli/abstract.rb
369
+ - lib/abide_dev_utils/cli/cem.rb
335
370
  - lib/abide_dev_utils/cli/comply.rb
336
371
  - lib/abide_dev_utils/cli/jira.rb
337
372
  - lib/abide_dev_utils/cli/puppet.rb
@@ -342,6 +377,7 @@ files:
342
377
  - lib/abide_dev_utils/constants.rb
343
378
  - lib/abide_dev_utils/errors.rb
344
379
  - lib/abide_dev_utils/errors/base.rb
380
+ - lib/abide_dev_utils/errors/cem.rb
345
381
  - lib/abide_dev_utils/errors/comply.rb
346
382
  - lib/abide_dev_utils/errors/gcloud.rb
347
383
  - lib/abide_dev_utils/errors/general.rb
@@ -351,19 +387,34 @@ files:
351
387
  - lib/abide_dev_utils/files.rb
352
388
  - lib/abide_dev_utils/gcloud.rb
353
389
  - lib/abide_dev_utils/jira.rb
390
+ - lib/abide_dev_utils/markdown.rb
354
391
  - lib/abide_dev_utils/mixins.rb
355
392
  - lib/abide_dev_utils/output.rb
356
393
  - lib/abide_dev_utils/ppt.rb
357
394
  - lib/abide_dev_utils/ppt/api.rb
358
395
  - lib/abide_dev_utils/ppt/class_utils.rb
359
- - lib/abide_dev_utils/ppt/coverage.rb
396
+ - lib/abide_dev_utils/ppt/facter_utils.rb
397
+ - lib/abide_dev_utils/ppt/hiera.rb
360
398
  - lib/abide_dev_utils/ppt/new_obj.rb
399
+ - lib/abide_dev_utils/ppt/puppet_module.rb
361
400
  - lib/abide_dev_utils/ppt/score_module.rb
362
401
  - lib/abide_dev_utils/prompt.rb
363
402
  - lib/abide_dev_utils/resources/generic_spec.erb
364
403
  - lib/abide_dev_utils/validate.rb
365
404
  - lib/abide_dev_utils/version.rb
366
405
  - lib/abide_dev_utils/xccdf.rb
406
+ - lib/abide_dev_utils/xccdf/diff.rb
407
+ - lib/abide_dev_utils/xccdf/diff/benchmark.rb
408
+ - lib/abide_dev_utils/xccdf/diff/benchmark/number_title.rb
409
+ - lib/abide_dev_utils/xccdf/diff/benchmark/profile.rb
410
+ - lib/abide_dev_utils/xccdf/diff/benchmark/property.rb
411
+ - lib/abide_dev_utils/xccdf/diff/benchmark/property_existence.rb
412
+ - lib/abide_dev_utils/xccdf/diff/utils.rb
413
+ - lib/abide_dev_utils/xccdf/parser.rb
414
+ - lib/abide_dev_utils/xccdf/parser/objects.rb
415
+ - lib/abide_dev_utils/xccdf/parser/objects/digest_object.rb
416
+ - lib/abide_dev_utils/xccdf/parser/objects/numbered_object.rb
417
+ - new_diff.rb
367
418
  homepage: https://github.com/puppetlabs/abide_dev_utils
368
419
  licenses:
369
420
  - MIT
@@ -371,7 +422,7 @@ metadata:
371
422
  homepage_uri: https://github.com/puppetlabs/abide_dev_utils
372
423
  source_code_uri: https://github.com/puppetlabs/abide_dev_utils
373
424
  changelog_uri: https://github.com/puppetlabs/abide_dev_utils
374
- post_install_message:
425
+ post_install_message:
375
426
  rdoc_options: []
376
427
  require_paths:
377
428
  - lib
@@ -386,8 +437,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
386
437
  - !ruby/object:Gem::Version
387
438
  version: '0'
388
439
  requirements: []
389
- rubygems_version: 3.1.6
390
- signing_key:
440
+ rubygems_version: 3.1.4
441
+ signing_key:
391
442
  specification_version: 4
392
443
  summary: Helper utilities for developing compliance Puppet code
393
444
  test_files: []
@@ -1,86 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'json'
4
- require 'pathname'
5
- require 'yaml'
6
- require 'puppet_pal'
7
- require 'abide_dev_utils/ppt/class_utils'
8
-
9
- module AbideDevUtils
10
- module Ppt
11
- class CoverageReport
12
- def self.generate(puppet_class_dir, hiera_path, profile = nil)
13
- coverage = {}
14
- coverage['classes'] = {}
15
- all_cap = ClassUtils.find_all_classes_and_paths(puppet_class_dir)
16
- invalid_classes = find_invalid_classes(all_cap)
17
- valid_classes = find_valid_classes(all_cap, invalid_classes)
18
- coverage['classes']['invalid'] = invalid_classes
19
- coverage['classes']['valid'] = valid_classes
20
- hiera = YAML.safe_load(File.open(hiera_path))
21
- profile&.gsub!(/^profile_/, '') unless profile.nil?
22
-
23
- matcher = profile.nil? ? /^profile_/ : /^profile_#{profile}/
24
- hiera.each do |k, v|
25
- key_base = k.split('::')[-1]
26
- coverage['benchmark'] = v if key_base == 'title'
27
- next unless key_base.match?(matcher)
28
-
29
- coverage[key_base] = generate_uncovered_data(v, valid_classes)
30
- end
31
- coverage
32
- end
33
-
34
- def self.generate_uncovered_data(ctrl_list, valid_classes)
35
- out_hash = {}
36
- out_hash[:num_total] = ctrl_list.length
37
- out_hash[:uncovered] = []
38
- out_hash[:covered] = []
39
- ctrl_list.each do |c|
40
- if valid_classes.include?(c)
41
- out_hash[:covered] << c
42
- else
43
- out_hash[:uncovered] << c
44
- end
45
- end
46
- out_hash[:num_covered] = out_hash[:covered].length
47
- out_hash[:num_uncovered] = out_hash[:uncovered].length
48
- out_hash[:coverage] = Float(
49
- (Float(out_hash[:num_covered]) / Float(out_hash[:num_total])) * 100.0
50
- ).floor(3)
51
- out_hash
52
- end
53
-
54
- def self.find_valid_classes(all_cap, invalid_classes)
55
- all_classes = all_cap.dup.transpose[0]
56
- return [] if all_classes.nil?
57
-
58
- return all_classes - invalid_classes unless invalid_classes.nil?
59
-
60
- all_classes
61
- end
62
-
63
- def self.find_invalid_classes(all_cap)
64
- invalid_classes = []
65
- all_cap.each do |cap|
66
- invalid_classes << cap[0] unless class_valid?(cap[1])
67
- end
68
- invalid_classes
69
- end
70
-
71
- def self.class_valid?(manifest_path)
72
- compiler = Puppet::Pal::Compiler.new(nil)
73
- ast = compiler.parse_file(manifest_path)
74
- ast.body.body.statements.each do |s|
75
- next unless s.respond_to?(:arguments)
76
- next unless s.arguments.respond_to?(:each)
77
-
78
- s.arguments.each do |i|
79
- return false if i.value == 'Not implemented'
80
- end
81
- end
82
- true
83
- end
84
- end
85
- end
86
- end