abide_dev_utils 0.9.7 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
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