abide_dev_utils 0.14.2 → 0.16.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b6af74f2a0eea495b7bf71fd843c668f556ee83cf45e3b6ebfafe4242278dcee
4
- data.tar.gz: 6e9f6f60bc30f135bd4f2c6f35962626e81db410fc0c17631761c84e4551e1db
3
+ metadata.gz: 220a0a755c0e337d22a853e106935f4355564e51a759eb93967e3aeba5a9f020
4
+ data.tar.gz: e0272122e07a4e53d3efa71486de5dc7a0229ca0d22214b3311c94df0c90100b
5
5
  SHA512:
6
- metadata.gz: d61f39d043c94ab0ee2db46bd0374c99803fc720022606361f319e3b245c429700bffec7e9157ef11045cc7f5e25b21833b8bdc820ca1dcb32e7022ae7c89e3c
7
- data.tar.gz: c2463568c53de7d0e0dfa0e4950484db5df6965888d2cae2b145f4dfba2816105f41aa413da4c07c9642da33c2a6b264a5cfd6504874db1de0f6de0eeef51675
6
+ metadata.gz: c7710f102655653f4181694c8dd076acff1dfd8040120c54157bc77a89f3db4a892854d2110ac4a47974ead2fdc23c3b3028c4459aa9e7bf75145e71fcebad0b
7
+ data.tar.gz: a5d0d101010f6ba4129346c4c4447f433b5a61e36e9000c3dab653a1709b78ff08203ec2dd014e61489d09eb2a78badfb51e233d8cbe33d85c903c3654cae90f
data/Gemfile.lock CHANGED
@@ -1,8 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- abide_dev_utils (0.14.2)
5
- amatch (~> 0.4)
4
+ abide_dev_utils (0.16.0)
6
5
  cmdparse (~> 3.0)
7
6
  facterdb (>= 1.21)
8
7
  google-cloud-storage (~> 1.34)
@@ -18,16 +17,13 @@ GEM
18
17
  remote: https://rubygems.org/
19
18
  specs:
20
19
  CFPropertyList (2.3.6)
21
- activesupport (7.0.4.1)
20
+ activesupport (7.0.4.3)
22
21
  concurrent-ruby (~> 1.0, >= 1.0.2)
23
22
  i18n (>= 1.6, < 2)
24
23
  minitest (>= 5.1)
25
24
  tzinfo (~> 2.0)
26
25
  addressable (2.8.0)
27
26
  public_suffix (>= 2.0.2, < 5.0)
28
- amatch (0.4.1)
29
- mize
30
- tins (~> 1.0)
31
27
  ast (2.4.2)
32
28
  async (1.30.2)
33
29
  console (~> 1.10)
@@ -61,7 +57,7 @@ GEM
61
57
  diff-lcs (1.5.0)
62
58
  digest-crc (0.6.4)
63
59
  rake (>= 12.0.0, < 14.0.0)
64
- facter (4.3.0)
60
+ facter (4.4.1)
65
61
  hocon (~> 1.3)
66
62
  thor (>= 1.0.1, < 2.0)
67
63
  facterdb (1.21.0)
@@ -112,7 +108,7 @@ GEM
112
108
  google-cloud-core (~> 1.6)
113
109
  googleauth (>= 0.16.2, < 2.a)
114
110
  mini_mime (~> 1.0)
115
- googleauth (1.3.0)
111
+ googleauth (1.5.2)
116
112
  faraday (>= 0.17.3, < 3.a)
117
113
  jwt (>= 1.4, < 3.0)
118
114
  memoist (~> 0.16)
@@ -122,7 +118,7 @@ GEM
122
118
  hashdiff (1.0.1)
123
119
  hashie (5.0.0)
124
120
  hiera (3.12.0)
125
- hocon (1.3.1)
121
+ hocon (1.4.0)
126
122
  httpclient (2.8.3)
127
123
  i18n (1.12.0)
128
124
  concurrent-ruby (~> 1.0)
@@ -132,18 +128,16 @@ GEM
132
128
  atlassian-jwt
133
129
  multipart-post
134
130
  oauth (~> 0.5, >= 0.5.0)
135
- jwt (2.7.0)
131
+ jwt (2.7.1)
136
132
  locale (2.1.3)
137
133
  memoist (0.16.2)
138
134
  method_source (1.0.0)
139
135
  mini_mime (1.1.2)
140
- minitest (5.17.0)
141
- mize (0.4.1)
142
- protocol (~> 2.0)
136
+ minitest (5.18.0)
143
137
  multi_json (1.15.0)
144
138
  multipart-post (2.3.0)
145
139
  nio4r (2.5.8)
146
- nokogiri (1.14.2-x86_64-darwin)
140
+ nokogiri (1.15.2-x86_64-darwin)
147
141
  racc (~> 1.4)
148
142
  oauth (0.6.2)
149
143
  snaky_hash (~> 2.0)
@@ -155,8 +149,6 @@ GEM
155
149
  parallel (1.22.1)
156
150
  parser (3.1.2.0)
157
151
  ast (~> 2.4.1)
158
- protocol (2.0.0)
159
- ruby_parser (~> 3.0)
160
152
  protocol-hpack (1.4.2)
161
153
  protocol-http (0.22.6)
162
154
  protocol-http1 (0.14.4)
@@ -168,7 +160,7 @@ GEM
168
160
  coderay (~> 1.1)
169
161
  method_source (~> 1.0)
170
162
  public_suffix (4.0.7)
171
- puppet (7.23.0-universal-darwin)
163
+ puppet (7.24.0-universal-darwin)
172
164
  CFPropertyList (~> 2.2)
173
165
  concurrent-ruby (~> 1.0, < 1.2.0)
174
166
  deep_merge (~> 1.0)
@@ -182,9 +174,9 @@ GEM
182
174
  semantic_puppet (~> 1.0)
183
175
  puppet-resource_api (1.8.14)
184
176
  hocon (>= 1.0)
185
- puppet-strings (3.0.1)
186
- rgen (~> 0.9.0)
187
- yard (~> 0.9.5)
177
+ puppet-strings (4.0.0)
178
+ rgen (~> 0.9)
179
+ yard (~> 0.9)
188
180
  racc (1.6.2)
189
181
  rainbow (3.1.1)
190
182
  rake (13.0.6)
@@ -229,8 +221,6 @@ GEM
229
221
  rubocop (~> 1.19)
230
222
  ruby-progressbar (1.11.0)
231
223
  ruby2_keywords (0.0.5)
232
- ruby_parser (3.20.0)
233
- sexp_processor (~> 4.16)
234
224
  rubyzip (2.3.2)
235
225
  sawyer (0.9.2)
236
226
  addressable (>= 2.3.5)
@@ -240,8 +230,7 @@ GEM
240
230
  childprocess (>= 0.5, < 5.0)
241
231
  rexml (~> 3.2, >= 3.2.5)
242
232
  rubyzip (>= 1.2.2)
243
- semantic_puppet (1.0.4)
244
- sexp_processor (4.16.1)
233
+ semantic_puppet (1.1.0)
245
234
  signet (0.17.0)
246
235
  addressable (~> 2.8)
247
236
  faraday (>= 0.17.5, < 3.a)
@@ -250,24 +239,21 @@ GEM
250
239
  snaky_hash (2.0.1)
251
240
  hashie
252
241
  version_gem (~> 1.1, >= 1.1.1)
253
- sync (0.5.0)
254
- thor (1.2.1)
242
+ thor (1.2.2)
255
243
  timers (4.3.3)
256
- tins (1.32.1)
257
- sync
258
244
  traces (0.4.1)
259
245
  trailblazer-option (0.1.2)
260
- tzinfo (2.0.5)
246
+ tzinfo (2.0.6)
261
247
  concurrent-ruby (~> 1.0)
262
248
  uber (0.1.0)
263
249
  unicode-display_width (2.1.0)
264
- version_gem (1.1.1)
265
- webrick (1.7.0)
266
- yard (0.9.28)
267
- webrick (~> 1.7.0)
250
+ version_gem (1.1.2)
251
+ webrick (1.8.1)
252
+ yard (0.9.34)
268
253
 
269
254
  PLATFORMS
270
255
  x86_64-darwin-19
256
+ x86_64-darwin-20
271
257
  x86_64-linux
272
258
 
273
259
  DEPENDENCIES
@@ -41,7 +41,6 @@ Gem::Specification.new do |spec|
41
41
  spec.add_dependency 'selenium-webdriver', '~> 4.0.0.beta4'
42
42
  spec.add_dependency 'google-cloud-storage', '~> 1.34'
43
43
  spec.add_dependency 'hashdiff', '~> 1.0'
44
- spec.add_dependency 'amatch', '~> 0.4'
45
44
  spec.add_dependency 'facterdb', '>= 1.21'
46
45
 
47
46
  # Dev dependencies
@@ -85,7 +85,7 @@ module AbideDevUtils
85
85
  next if benchmark.framework == 'stig' && control.id_map_type != 'vulnid'
86
86
 
87
87
  control_md = ControlMarkdown.new(control, @md, @strings, @module_name, benchmark.framework, opts: @opts)
88
- control_md.generate!
88
+ control_md.generate! if control_md.verify_profile_and_level_selections
89
89
  progress_bar.increment unless @opts[:quiet]
90
90
  rescue StandardError => e
91
91
  raise "Failed to generate markdown for control #{control.id}. Original message: #{e.message}"
@@ -248,6 +248,8 @@ module AbideDevUtils
248
248
  @framework = framework
249
249
  @formatter = formatter.nil? ? TypeExprValueFormatter : formatter
250
250
  @opts = opts
251
+ @valid_level = []
252
+ @valid_profile = []
251
253
  @control_data = {}
252
254
  end
253
255
 
@@ -262,6 +264,43 @@ module AbideDevUtils
262
264
  resource_reference_builder
263
265
  end
264
266
 
267
+ # This function act as a filter for controls based on the profile and level selections.
268
+ # There are few scanarios that can happen:
269
+ # 1. If no selections are made for profile or level, then all profiles and levels of control will be selected.
270
+ # 2. If selections are made for profile, then only the selected profile and all levels of control will be selected.
271
+ # 3. If selections are made for level, then only the selected level and all profiles of control will be selected.
272
+ # This function adds in some runtime overhead because we're checking each control's level and profile which is
273
+ # what we're going to be doing later when building the level and profile markdown, but this is
274
+ # necessary to ensure that the reference.md is generated the way we want it to be.
275
+ def verify_profile_and_level_selections
276
+ return true if @opts[:select_profile].nil? && @opts[:select_level].nil?
277
+
278
+ if @opts[:select_profile].nil? && !@opts[:select_level].nil?
279
+ @control.levels.each do |level|
280
+ @valid_level << level if select_control_level(level)
281
+ end
282
+
283
+ return true unless @valid_level.empty?
284
+ elsif !@opts[:select_profile].nil? && @opts[:select_level].nil?
285
+ @control.profiles.each do |profile|
286
+ @valid_profile << profile if select_control_profile(profile)
287
+ end
288
+
289
+ return true unless @valid_profile.empty?
290
+ elsif !@opts[:select_profile].nil? && !@opts[:select_level].nil?
291
+ @control.levels.each do |level|
292
+ @valid_level << level if select_control_level(level)
293
+ end
294
+
295
+ @control.profiles.each do |profile|
296
+ @valid_profile << profile if select_control_profile(profile)
297
+ end
298
+
299
+ # As long as there are valid profiles and levels for the control at this stage, all is good
300
+ !@valid_level.empty? && !@valid_profile.empty?
301
+ end
302
+ end
303
+
265
304
  private
266
305
 
267
306
  def heading_builder
@@ -340,18 +379,36 @@ module AbideDevUtils
340
379
  def control_levels_builder
341
380
  return unless @control.levels
342
381
 
382
+ # @valid_level is populated in verify_profile_and_level_selections from the fact that we've given
383
+ # the generator a list of levels we want to use. If we didn't give it a list of levels, then we
384
+ # want to use all of the levels that the control supports from @control.
343
385
  @md.add_ul('Supported Levels:')
344
- @control.levels.each do |l|
345
- @md.add_ul(@md.code(l), indent: 1)
386
+ if @valid_level.empty?
387
+ @control.levels.each do |l|
388
+ @md.add_ul(@md.code(l), indent: 1)
389
+ end
390
+ else
391
+ @valid_level.each do |l|
392
+ @md.add_ul(@md.code(l), indent: 1)
393
+ end
346
394
  end
347
395
  end
348
396
 
349
397
  def control_profiles_builder
350
398
  return unless @control.profiles
351
399
 
400
+ # @valid_profile is populated in verify_profile_and_level_selections from the fact that we've given
401
+ # the generator a list of profiles we want to use. If we didn't give it a list of profiles, then we
402
+ # want to use all of the profiles that the control supports from @control.
352
403
  @md.add_ul('Supported Profiles:')
353
- @control.profiles.each do |l|
354
- @md.add_ul(@md.code(l), indent: 1)
404
+ if @valid_profile.empty?
405
+ @control.profiles.each do |l|
406
+ @md.add_ul(@md.code(l), indent: 1)
407
+ end
408
+ else
409
+ @valid_profile.each do |l|
410
+ @md.add_ul(@md.code(l), indent: 1)
411
+ end
355
412
  end
356
413
  end
357
414
 
@@ -364,6 +421,18 @@ module AbideDevUtils
364
421
  end
365
422
  end
366
423
 
424
+ # Function that returns true if the profile is in the list of profiles that we want to use.
425
+ # @param profile [String] the profile to filter
426
+ def select_control_profile(profile)
427
+ @opts[:select_profile].include? profile
428
+ end
429
+
430
+ # Function that returns true if the level is in the list of levels that we want to use.
431
+ # @param level [String] the level to filter
432
+ def select_control_level(level)
433
+ @opts[:select_level].include? level
434
+ end
435
+
367
436
  def dependent_controls_builder
368
437
  dep_ctrls = @control.resource.dependent_controls
369
438
  return if dep_ctrls.nil? || dep_ctrls.empty?
@@ -4,7 +4,7 @@ require 'abide_dev_utils/cem'
4
4
  require 'abide_dev_utils/files'
5
5
  require 'abide_dev_utils/output'
6
6
  require 'abide_dev_utils/validate'
7
- require 'abide_dev_utils/xccdf/diff/benchmark'
7
+ require 'abide_dev_utils/xccdf/diff'
8
8
  require 'abide_dev_utils/cli/abstract'
9
9
 
10
10
  module Abide
@@ -113,6 +113,12 @@ module Abide
113
113
  options.on('-s', '--strict', 'Fails if there are any errors') do
114
114
  @data[:strict] = true
115
115
  end
116
+ options.on('-p [PROFILE]', '--select-profile [PROFILE]', 'The list of profiles that the reference.md will use separated by commas') do |pr|
117
+ @data[:select_profile] = pr.split(',')
118
+ end
119
+ options.on('-l [LEVEL]', '--select-level [LEVEL]', 'The list of level that the reference.md will use separated by commas') do |l|
120
+ @data[:select_level] = l.split(',')
121
+ end
116
122
  end
117
123
 
118
124
  def execute
@@ -162,13 +168,14 @@ module Abide
162
168
  end
163
169
 
164
170
  def execute(config_file, cur_xccdf, new_xccdf)
165
- AbideDevUtils::Validate.file(config_file, extension: 'yaml')
166
- AbideDevUtils::Validate.file(cur_xccdf, extension: 'xml')
167
- config_hiera = AbideDevUtils::Files::Reader.read(config_file, safe: true)
168
- diff = AbideDevUtils::XCCDF::Diff::BenchmarkDiff.new(cur_xccdf, new_xccdf).diff[:diff][:number_title]
169
- new_config_hiera, change_report = AbideDevUtils::CEM.update_legacy_config_from_diff(config_hiera, diff)
170
- AbideDevUtils::Output.yaml(new_config_hiera, console: @data[:verbose], file: @data[:out_file])
171
- AbideDevUtils::Output.simple(change_report) unless @data[:quiet]
171
+ warn 'This command is currently non-functional'
172
+ # AbideDevUtils::Validate.file(config_file, extension: 'yaml')
173
+ # AbideDevUtils::Validate.file(cur_xccdf, extension: 'xml')
174
+ # config_hiera = AbideDevUtils::Files::Reader.read(config_file, safe: true)
175
+ # diff = AbideDevUtils::XCCDF::Diff::BenchmarkDiff.new(cur_xccdf, new_xccdf).diff[:diff][:number_title]
176
+ # new_config_hiera, change_report = AbideDevUtils::CEM.update_legacy_config_from_diff(config_hiera, diff)
177
+ # AbideDevUtils::Output.yaml(new_config_hiera, console: @data[:verbose], file: @data[:out_file])
178
+ # AbideDevUtils::Output.simple(change_report) unless @data[:quiet]
172
179
  end
173
180
  end
174
181
 
@@ -23,6 +23,7 @@ module Abide
23
23
  add_command(JiraNewIssueCommand.new)
24
24
  add_command(JiraFromCoverageCommand.new)
25
25
  add_command(JiraFromXccdfCommand.new)
26
+ add_command(JiraFromXccdfDiffCommand.new)
26
27
  end
27
28
  end
28
29
 
@@ -46,7 +47,7 @@ module Abide
46
47
  end
47
48
 
48
49
  class JiraGetIssueCommand < CmdParse::Command
49
- CMD_NAME = 'get_issue'
50
+ CMD_NAME = 'get-issue'
50
51
  CMD_SHORT = 'Gets a specific issue'
51
52
  CMD_LONG = 'Returns JSON of a specific issue from key (<project>-<num>)'
52
53
  def initialize
@@ -67,7 +68,7 @@ module Abide
67
68
  end
68
69
 
69
70
  class JiraNewIssueCommand < CmdParse::Command
70
- CMD_NAME = 'new_issue'
71
+ CMD_NAME = 'new-issue'
71
72
  CMD_SHORT = 'Creates a new issue in a project'
72
73
  CMD_LONG = 'Allows you to create a new issue in a project'
73
74
  def initialize
@@ -93,7 +94,7 @@ module Abide
93
94
  end
94
95
 
95
96
  class JiraFromCoverageCommand < CmdParse::Command
96
- CMD_NAME = 'from_coverage'
97
+ CMD_NAME = 'from-coverage'
97
98
  CMD_SHORT = 'Creates a parent issue with subtasks from a coverage report'
98
99
  CMD_LONG = 'Creates a parent issue with subtasks for a benchmark and any uncovered controls'
99
100
  def initialize
@@ -116,7 +117,7 @@ module Abide
116
117
  end
117
118
 
118
119
  class JiraFromXccdfCommand < CmdParse::Command
119
- CMD_NAME = 'from_xccdf'
120
+ CMD_NAME = 'from-xccdf'
120
121
  CMD_SHORT = 'Creates a parent issue with subtasks from a xccdf file'
121
122
  CMD_LONG = 'Creates a parent issue with subtasks for a benchmark and any uncovered controls'
122
123
  def initialize
@@ -136,5 +137,48 @@ module Abide
136
137
  JIRA.new_issues_from_xccdf(client, proj, path, epic: @data[:epic], dry_run: @data[:dry_run])
137
138
  end
138
139
  end
140
+
141
+ class JiraFromXccdfDiffCommand < CmdParse::Command
142
+ CMD_NAME = 'from-xccdf-diff'
143
+ CMD_SHORT = 'Creates an Epic with tasks from a xccdf diff'
144
+ CMD_LONG = 'Creates an Epic with tasks for changes in a diff of two XCCDF files'
145
+ def initialize
146
+ super(CMD_NAME, takes_commands: false)
147
+ short_desc(CMD_SHORT)
148
+ long_desc(CMD_LONG)
149
+ argument_desc(PATH1: 'An XCCDF file', PATH2: 'An XCCDF file', PROJECT: 'A Jira project')
150
+ options.on('-d', '--dry-run', 'Print to console instead of saving objects') { |_| @data[:dry_run] = true }
151
+ options.on('-y', '--yes', 'Automatically approve all yes / no prompts') { |_| @data[:auto_approve] = true }
152
+ options.on('-e [EPIC]', '--epic [EPIC]', 'If given, tasks will be created and assigned to this epic. Takes form <PROJECT>-<NUM>') { |e| @data[:epic] = e }
153
+ options.on('-p [PROFILE]', '--profile', 'Only diff rules belonging to the matching profile. Takes a string that is treated as RegExp') do |x|
154
+ @data[:diff_opts] ||= {}
155
+ @data[:diff_opts][:profile] = x
156
+ end
157
+ options.on('-l [LEVEL]', '--level', 'Only diff rules belonging to the matching level. Takes a string that is treated as RegExp') do |x|
158
+ @data[:diff_opts] ||= {}
159
+ @data[:diff_opts][:level] = x
160
+ end
161
+ options.on('-i [PROPS]', '--ignore-changed-properties', 'Ignore changes to specified properties. Takes a comma-separated list.') do |x|
162
+ @data[:diff_opts] ||= {}
163
+ @data[:diff_opts][:ignore_changed_properties] = x.split(',')
164
+ end
165
+ end
166
+
167
+ def execute(path1, path2, project)
168
+ Abide::CLI::VALIDATE.file(path1)
169
+ Abide::CLI::VALIDATE.file(path2)
170
+ @data[:dry_run] = false if @data[:dry_run].nil?
171
+ client = JIRA.client(options: {})
172
+ proj = JIRA.project(client, project)
173
+ JIRA.new_issues_from_xccdf_diff(client,
174
+ proj,
175
+ path1,
176
+ path2,
177
+ epic: @data[:epic],
178
+ dry_run: @data[:dry_run],
179
+ auto_approve: @data[:auto_approve],
180
+ diff_opts: @data[:diff_opts])
181
+ end
182
+ end
139
183
  end
140
184
  end
@@ -104,27 +104,21 @@ module Abide
104
104
  super(CMD_NAME, CMD_SHORT, CMD_LONG, takes_commands: false)
105
105
  argument_desc(FILE1: CMD_FILE1_ARG, FILE2: CMD_FILE2_ARG)
106
106
  options.on('-o [PATH]', '--out-file', 'Save the report as a yaml file') { |x| @data[:outfile] = x }
107
- options.on('-p [PROFILE]', '--profile', 'Only diff and specific profile in the benchmarks') do |x|
107
+ options.on('-p [PROFILE]', '--profile', 'Only diff rules belonging to the matching profile. Takes a string that is treated as RegExp') do |x|
108
108
  @data[:profile] = x
109
109
  end
110
- options.on('-l [LEVEL]', '--level', 'Only diff the specific level in the benchmarks') do |x|
110
+ options.on('-l [LEVEL]', '--level', 'Only diff rules belonging to the matching level. Takes a string that is treated as RegExp') do |x|
111
111
  @data[:level] = x
112
112
  end
113
- options.on('-r', '--raw', 'Output the diff in raw hash format') { @data[:raw] = true }
113
+ options.on('-i [PROPS]', '--ignore-changed-properties', 'Ignore changes to specified properties. Takes a comma-separated list.') do |x|
114
+ @data[:ignore_changed_properties] = x.split(',')
115
+ end
116
+ options.on('-r', '--raw', 'Output the diff in raw format') { @data[:raw] = true }
114
117
  options.on('-q', '--quiet', 'Show no output in the terminal') { @data[:quiet] = false }
115
- options.on('--no-diff-profiles', 'Do not diff the profiles in the XCCDF files') { @data[:diff_profiles] = false }
116
- options.on('--no-diff-controls', 'Do not diff the controls in the XCCDF files') { @data[:diff_controls] = false }
117
- options.on('--old-style', 'Use old-style diffs') { @data[:old_style] = true }
118
118
  end
119
119
 
120
120
  def execute(file1, file2)
121
- diffreport = if @data[:old_style]
122
- AbideDevUtils::XCCDF.diff(file1, file2, @data)
123
- else
124
- dr = AbideDevUtils::XCCDF.new_style_diff(file1, file2, @data)
125
- dr[:diff][:number_title].map! { |d| d[:text] }
126
- dr
127
- end
121
+ diffreport = AbideDevUtils::XCCDF.diff(file1, file2, @data)
128
122
  AbideDevUtils::Output.yaml(diffreport, console: @data.fetch(:quiet, true), file: @data.fetch(:outfile, nil))
129
123
  end
130
124
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'yaml'
3
+ require_relative 'files'
4
4
 
5
5
  module AbideDevUtils
6
6
  module Config
@@ -9,15 +9,12 @@ module AbideDevUtils
9
9
  def self.to_h(path = DEFAULT_PATH)
10
10
  return {} unless File.file?(path)
11
11
 
12
- h = YAML.safe_load(File.open(path), [Symbol])
12
+ h = AbideDevUtils::Files::Reader.read(path)
13
13
  h.transform_keys(&:to_sym)
14
14
  end
15
15
 
16
16
  def to_h(path = DEFAULT_PATH)
17
- return {} unless File.file?(path)
18
-
19
- h = YAML.safe_load(File.open(path), [Symbol])
20
- h.transform_keys(&:to_sym)
17
+ self.class.to_h(path)
21
18
  end
22
19
 
23
20
  def self.config_section(section, path = DEFAULT_PATH)
@@ -12,12 +12,7 @@ module AbideDevUtils
12
12
  extension = File.extname(path)
13
13
  case extension
14
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
15
+ read_yaml(path, safe: safe, opts: opts)
21
16
  when '.json'
22
17
  require 'json'
23
18
  return JSON.parse(File.read(path), opts) if safe
@@ -34,6 +29,21 @@ module AbideDevUtils
34
29
  File.read(path)
35
30
  end
36
31
  end
32
+
33
+ def self.read_yaml(path, safe: true, opts: { permitted_classes: [Symbol] })
34
+ permitted_classes = opts[:permitted_classes] || [Symbol]
35
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.0.0')
36
+ require 'psych'
37
+ return Psych.safe_load_file(path, permitted_classes: permitted_classes) if safe
38
+
39
+ Psych.load_file(path)
40
+ else
41
+ require 'yaml'
42
+ return YAML.safe_load(File.read(path), permitted_classes) if safe
43
+
44
+ YAML.load(File.read(path)) # rubocop:disable Security/YAMLLoad
45
+ end
46
+ end
37
47
  end
38
48
 
39
49
  class Writer
@@ -11,6 +11,7 @@ module AbideDevUtils
11
11
  ERRORS = AbideDevUtils::Errors::Jira
12
12
  COV_PARENT_SUMMARY_PREFIX = '::BENCHMARK:: '
13
13
  COV_CHILD_SUMMARY_PREFIX = '::CONTROL:: '
14
+ UPD_EPIC_SUMMARY_PREFIX = '::BENCHMARK UPDATE::'
14
15
  PROGRESS_BAR_FORMAT = '%a %e %P% Created: %c of %C'
15
16
 
16
17
  def self.project(client, project)
@@ -58,7 +59,7 @@ module AbideDevUtils
58
59
  iss.save
59
60
  end
60
61
 
61
- def self.new_issue(client, project, summary, labels: ['abide_dev_utils'], epic: nil, dry_run: false)
62
+ def self.new_issue(client, project, summary, description: nil, labels: ['abide_dev_utils'], epic: nil, dry_run: false)
62
63
  if dry_run
63
64
  sleep(0.2)
64
65
  return Dummy.new(summary)
@@ -69,6 +70,7 @@ module AbideDevUtils
69
70
  fields['reporter'] = myself(client)
70
71
  fields['issuetype'] = issuetype(client, 'Task')
71
72
  fields['priority'] = priority(client, '6')
73
+ fields['description'] = description if description
72
74
  fields['labels'] = labels
73
75
  epic = issue(client, epic) if epic && !epic.is_a?(JIRA::Resource::Issue)
74
76
  fields['customfield_10006'] = epic.key if epic # Epic_Link
@@ -227,6 +229,72 @@ module AbideDevUtils
227
229
  AbideDevUtils::Output.simple("#{dr_prefix(dry_run)}Done creating tasks in Epic '#{epic.summary}'")
228
230
  end
229
231
 
232
+ def self.new_issues_from_xccdf_diff(client, project, xccdf1_path, xccdf2_path, epic: nil, dry_run: false, auto_approve: false, diff_opts: {})
233
+ require 'abide_dev_utils/xccdf/diff'
234
+ diff = AbideDevUtils::XCCDF::Diff::BenchmarkDiff.new(xccdf1_path, xccdf2_path, diff_opts)
235
+ i_attrs = all_project_issues_attrs(project)
236
+ # We need to get the actual epic Issue object, or create it if it doesn't exist
237
+ epic = if epic.nil?
238
+ new_epic_summary = "#{UPD_EPIC_SUMMARY_PREFIX}#{diff.this.title}: v#{diff.this.version} -> #{diff.other.version}"
239
+ if summary_exist?(new_epic_summary, i_attrs)
240
+ issue(client, new_epic_summary)
241
+ else
242
+ unless AbideDevUtils::Prompt.yes_no("#{dr_prefix(dry_run)}Create new epic '#{new_epic_summary}'?", auto_approve: auto_approve)
243
+ AbideDevUtils::Output.simple("#{dr_prefix(dry_run)}Aborting")
244
+ exit(0)
245
+ end
246
+ new_epic(client, project.key, new_epic_summary, dry_run: dry_run)
247
+ end
248
+ else
249
+ issue(client, epic)
250
+ end
251
+ to_create = {}
252
+ diff.diff[:rules].each do |key, val|
253
+ next if val.empty?
254
+
255
+ val.each do |v|
256
+ case key
257
+ when :added
258
+ sum = "Add rule #{v[:number]} - #{v[:title]}"
259
+ sum = "#{sum[0..60]}..." if sum.length > 60
260
+ to_create[sum] = <<~DESC
261
+ Rule #{v[:number]} - #{v[:title]} is added with #{diff.other.title} #{diff.other.version}
262
+ DESC
263
+ when :removed
264
+ sum = "Remove rule #{v[:number]} - #{v[:title]}"
265
+ sum = "#{sum[0..60]}..." if sum.length > 60
266
+ to_create[sum] = <<~DESC
267
+ Rule #{v[:number]} - #{v[:title]} is removed from #{diff.this.title} #{diff.this.version}
268
+ DESC
269
+ else
270
+ sum = "Update rule \"#{v[:from]}\""
271
+ sum = "#{sum[0..60]}..." if sum.length > 60
272
+ to_create[sum] = <<~DESC
273
+ Rule #{v[:from]} is updated in #{diff.other.title} #{diff.other.version}:
274
+ #{v[:changes].collect { |k, v| "#{k}: #{v}" }.join("\n")}
275
+ DESC
276
+ end
277
+ end
278
+ end
279
+ approved_create = {}
280
+ to_create.each do |summary, description|
281
+ if AbideDevUtils::Prompt.yes_no("#{dr_prefix(dry_run)}Create new issue '#{summary}' with description:\n#{description}", auto_approve: auto_approve)
282
+ approved_create[summary] = description
283
+ end
284
+ end
285
+ AbideDevUtils::Output.simple("#{dr_prefix(dry_run)}Creating #{approved_create.keys.count} new Jira issues")
286
+ progress = AbideDevUtils::Output.progress(title: "#{dr_prefix(dry_run)}Creating issues",
287
+ total: approved_create.keys.count,
288
+ format: PROGRESS_BAR_FORMAT)
289
+ approved_create.each do |summary, description|
290
+ progress.log("#{dr_prefix(dry_run)}Creating #{summary}...")
291
+ new_issue(client, project.key, summary, description: description, labels: [], epic: epic, dry_run: dry_run)
292
+ progress.increment
293
+ end
294
+ progress.finish
295
+ AbideDevUtils::Output.simple("#{dr_prefix(dry_run)}Done creating tasks in Epic '#{epic.summary}'")
296
+ end
297
+
230
298
  def self.merge_options(options)
231
299
  config.merge(options)
232
300
  end
@@ -4,7 +4,9 @@ require 'io/console'
4
4
 
5
5
  module AbideDevUtils
6
6
  module Prompt
7
- def self.yes_no(msg)
7
+ def self.yes_no(msg, auto_approve: false)
8
+ return true if auto_approve
9
+
8
10
  print "#{msg} (Y/n): "
9
11
  return true if $stdin.cooked(&:gets).match?(/^[Yy].*/)
10
12
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AbideDevUtils
4
- VERSION = "0.14.2"
4
+ VERSION = "0.16.0"
5
5
  end