abide_dev_utils 0.4.2 → 0.8.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/.gitignore +2 -1
- data/.rubocop.yml +1 -1
- data/CODEOWNERS +1 -0
- data/README.md +34 -0
- data/abide_dev_utils.gemspec +11 -7
- data/itests.rb +138 -0
- data/lib/abide_dev_utils/cli/abstract.rb +2 -0
- data/lib/abide_dev_utils/cli/comply.rb +115 -0
- data/lib/abide_dev_utils/cli/jira.rb +2 -2
- data/lib/abide_dev_utils/cli/puppet.rb +136 -11
- data/lib/abide_dev_utils/cli/xccdf.rb +26 -7
- data/lib/abide_dev_utils/cli.rb +2 -0
- data/lib/abide_dev_utils/comply.rb +498 -0
- data/lib/abide_dev_utils/config.rb +19 -0
- data/lib/abide_dev_utils/errors/comply.rb +17 -0
- data/lib/abide_dev_utils/errors/gcloud.rb +27 -0
- data/lib/abide_dev_utils/errors/general.rb +5 -0
- data/lib/abide_dev_utils/errors/ppt.rb +12 -0
- data/lib/abide_dev_utils/errors/xccdf.rb +8 -0
- data/lib/abide_dev_utils/errors.rb +2 -0
- data/lib/abide_dev_utils/gcloud.rb +22 -0
- data/lib/abide_dev_utils/jira.rb +15 -0
- data/lib/abide_dev_utils/mixins.rb +16 -0
- data/lib/abide_dev_utils/output.rb +7 -3
- data/lib/abide_dev_utils/ppt/api.rb +219 -0
- data/lib/abide_dev_utils/ppt/class_utils.rb +184 -0
- data/lib/abide_dev_utils/ppt/coverage.rb +2 -3
- data/lib/abide_dev_utils/ppt/score_module.rb +162 -0
- data/lib/abide_dev_utils/ppt.rb +138 -49
- data/lib/abide_dev_utils/validate.rb +5 -1
- data/lib/abide_dev_utils/version.rb +1 -1
- data/lib/abide_dev_utils/xccdf.rb +567 -9
- data/lib/abide_dev_utils.rb +1 -0
- metadata +82 -17
- data/lib/abide_dev_utils/utils/general.rb +0 -9
- data/lib/abide_dev_utils/xccdf/cis/hiera.rb +0 -161
- data/lib/abide_dev_utils/xccdf/cis.rb +0 -3
@@ -0,0 +1,498 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'yaml'
|
5
|
+
require 'selenium-webdriver'
|
6
|
+
require 'abide_dev_utils/errors/comply'
|
7
|
+
require 'abide_dev_utils/gcloud'
|
8
|
+
require 'abide_dev_utils/output'
|
9
|
+
require 'abide_dev_utils/prompt'
|
10
|
+
require 'pry'
|
11
|
+
|
12
|
+
module AbideDevUtils
|
13
|
+
# Holds module methods and a class for dealing with Puppet Comply
|
14
|
+
module Comply
|
15
|
+
include AbideDevUtils::Errors::Comply
|
16
|
+
|
17
|
+
def self.build_report(url, password, config = nil, **opts)
|
18
|
+
ReportScraper.new(url, config, **opts).build_report(password)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.compare_reports(report_a, report_b, **opts)
|
22
|
+
report_name = opts.fetch(:report_name, nil)
|
23
|
+
current_report = ScanReport.new.from_yaml(report_a)
|
24
|
+
last_report = if opts.fetch(:remote_storage, '') == 'gcloud'
|
25
|
+
report_name = report_b if report_name.nil?
|
26
|
+
ScanReport.new.from_yaml(ScanReport.fetch_report(name: report_b))
|
27
|
+
else
|
28
|
+
report_name = File.basename(report_b) if report_name.nil?
|
29
|
+
ScanReport.new.from_yaml(File.read(report_b))
|
30
|
+
end
|
31
|
+
result, details = current_report.report_comparison(last_report, check_goodness: true)
|
32
|
+
if result
|
33
|
+
AbideDevUtils::Output.simple('No negative differences detected...')
|
34
|
+
AbideDevUtils::Output.simple(JSON.pretty_generate(details))
|
35
|
+
else
|
36
|
+
AbideDevUtils::Output.simple('Negative differences detected!', stream: $stderr)
|
37
|
+
AbideDevUtils::Output.simple(JSON.pretty_generate(details), stream: $stderr)
|
38
|
+
end
|
39
|
+
if opts.fetch(:upload, false) && !opts.fetch(:remote_storage, '').empty? && !report_name.nil?
|
40
|
+
AbideDevUtils::Output.simple('Uploading current report...')
|
41
|
+
ScanReport.upload_report(File.expand_path(report_a), name: report_name)
|
42
|
+
AbideDevUtils::Output.simple('Successfully uploaded report.')
|
43
|
+
end
|
44
|
+
result
|
45
|
+
end
|
46
|
+
|
47
|
+
# Class that uses Selenium WebDriver to gather scan reports from Puppet Comply
|
48
|
+
class ReportScraper
|
49
|
+
attr_reader :timeout,
|
50
|
+
:username,
|
51
|
+
:status,
|
52
|
+
:ignorelist,
|
53
|
+
:onlylist,
|
54
|
+
:max_pagination,
|
55
|
+
:screenshot_on_error,
|
56
|
+
:page_source_on_error
|
57
|
+
|
58
|
+
def initialize(url, config = nil, **opts)
|
59
|
+
@url = url
|
60
|
+
@config = config
|
61
|
+
@opts = opts
|
62
|
+
@timeout = fetch_option(:timeout, 10).to_i
|
63
|
+
@username = fetch_option(:username, 'comply')
|
64
|
+
@status = fetch_option(:status)
|
65
|
+
@ignorelist = fetch_option(:ignorelist, [])
|
66
|
+
@onlylist = fetch_option(:onlylist, [])
|
67
|
+
@max_pagination = fetch_option(:max_pagination, 5).to_i
|
68
|
+
@screenshot_on_error = fetch_option(:screenshot_on_error, false)
|
69
|
+
@page_source_on_error = fetch_option(:page_source_on_error, false)
|
70
|
+
end
|
71
|
+
|
72
|
+
def build_report(password)
|
73
|
+
connect(password)
|
74
|
+
scrape_report
|
75
|
+
ensure
|
76
|
+
driver.quit
|
77
|
+
end
|
78
|
+
|
79
|
+
def file_dir
|
80
|
+
@file_dir ||= File.expand_path('~/abide_dev_utils')
|
81
|
+
end
|
82
|
+
|
83
|
+
def file_dir=(path)
|
84
|
+
@file_dir = new_file_dir(path)
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
attr_reader :progress
|
90
|
+
|
91
|
+
def fetch_option(option, default = nil)
|
92
|
+
return @opts.fetch(option, default) if @config.nil?
|
93
|
+
|
94
|
+
@opts.key?(option) ? @opts[option] : @config.fetch(option, default)
|
95
|
+
end
|
96
|
+
|
97
|
+
def node_report_links
|
98
|
+
@node_report_links ||= find_node_report_links
|
99
|
+
end
|
100
|
+
|
101
|
+
def driver
|
102
|
+
@driver ||= new_driver
|
103
|
+
end
|
104
|
+
|
105
|
+
def output
|
106
|
+
AbideDevUtils::Output
|
107
|
+
end
|
108
|
+
|
109
|
+
def prompt
|
110
|
+
AbideDevUtils::Prompt
|
111
|
+
end
|
112
|
+
|
113
|
+
def new_progress(node_name)
|
114
|
+
@progress = AbideDevUtils::Output.progress title: "Building report for #{node_name}", total: nil
|
115
|
+
end
|
116
|
+
|
117
|
+
def new_driver
|
118
|
+
options = Selenium::WebDriver::Chrome::Options.new
|
119
|
+
options.args = @opts.fetch(:driveropts, %w[
|
120
|
+
--headless
|
121
|
+
--test-type
|
122
|
+
--disable-gpu
|
123
|
+
--no-sandbox
|
124
|
+
--no-first-run
|
125
|
+
--no-default-browser-check
|
126
|
+
--ignore-certificate-errors
|
127
|
+
--start-maximized
|
128
|
+
])
|
129
|
+
output.simple 'Starting headless Chrome...'
|
130
|
+
Selenium::WebDriver.for(:chrome, options: options)
|
131
|
+
end
|
132
|
+
|
133
|
+
def find_element(subject = driver, **kwargs)
|
134
|
+
driver.manage.window.resize_to(1920, 1080)
|
135
|
+
subject.find_element(**kwargs)
|
136
|
+
end
|
137
|
+
|
138
|
+
def wait_on(timeout: @timeout,
|
139
|
+
ignore_nse: false,
|
140
|
+
quit_driver: true,
|
141
|
+
quiet: false,
|
142
|
+
ignore: [Selenium::WebDriver::Error::NoSuchElementError],
|
143
|
+
&block)
|
144
|
+
raise 'wait_on must be passed a block' unless block
|
145
|
+
|
146
|
+
value = nil
|
147
|
+
if ignore_nse
|
148
|
+
begin
|
149
|
+
Selenium::WebDriver::Wait.new(ignore: [], timeout: timeout, interval: 1).until do
|
150
|
+
value = yield
|
151
|
+
end
|
152
|
+
rescue Selenium::WebDriver::Error::NoSuchElementError
|
153
|
+
return value
|
154
|
+
rescue StandardError => e
|
155
|
+
raise_error(e, AbideDevUtils::Comply::WaitOnError, quit_driver: quit_driver, quiet: quiet)
|
156
|
+
end
|
157
|
+
else
|
158
|
+
begin
|
159
|
+
Selenium::WebDriver::Wait.new(ignore: ignore, timeout: timeout, interval: 1).until do
|
160
|
+
value = yield
|
161
|
+
end
|
162
|
+
rescue StandardError => e
|
163
|
+
raise_error(e, AbideDevUtils::Comply::WaitOnError, quit_driver: quit_driver, quiet: quiet)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
value
|
167
|
+
end
|
168
|
+
|
169
|
+
def new_file_dir(path)
|
170
|
+
return File.expand_path(path) if Dir.exist?(File.expand_path(path))
|
171
|
+
|
172
|
+
create_dir = prompt.yes_no("Directory #{path} does not exist. Create directory?")
|
173
|
+
return unless create_dir
|
174
|
+
|
175
|
+
require 'fileutils'
|
176
|
+
FileUtils.mkdir_p path
|
177
|
+
end
|
178
|
+
|
179
|
+
def raise_error(original, err_class = nil, quit_driver: true, quiet: false)
|
180
|
+
output.simple 'Something went wrong!' unless quiet
|
181
|
+
if screenshot_on_error
|
182
|
+
output.simple 'Taking a screenshot of current page state...' unless quiet
|
183
|
+
screenshot
|
184
|
+
end
|
185
|
+
|
186
|
+
if page_source_on_error
|
187
|
+
output.simple 'Saving page source of current page...' unless quiet
|
188
|
+
page_source
|
189
|
+
end
|
190
|
+
|
191
|
+
driver.quit if quit_driver
|
192
|
+
actual_err_class = err_class.nil? ? original.class : err_class
|
193
|
+
raise actual_err_class, original.message
|
194
|
+
end
|
195
|
+
|
196
|
+
def screenshot
|
197
|
+
driver.save_screenshot(File.join(file_dir, "comply_error_#{Time.now.to_i}.png"))
|
198
|
+
rescue Errno::ENOENT
|
199
|
+
save_default = prompt.yes_no(
|
200
|
+
"Directory #{file_dir} does not exist. Save screenshot to current directory?"
|
201
|
+
)
|
202
|
+
driver.save_screenshot(File.join(File.expand_path('.'), "comply_error_#{Time.now.to_i}.png")) if save_default
|
203
|
+
end
|
204
|
+
|
205
|
+
def page_source
|
206
|
+
File.open(File.join(file_dir, "comply_error_#{Time.now.to_i}.txt"), 'w') { |f| f.write(driver.page_source) }
|
207
|
+
rescue Errno::ENOENT
|
208
|
+
save_default = prompt.yes_no(
|
209
|
+
"Directory #{file_dir} does not exist. Save page source to current directory?"
|
210
|
+
)
|
211
|
+
if save_default
|
212
|
+
File.open(File.join(File.expand_path('.'), "comply_error_#{Time.now.to_i}.html"), 'w') do |f|
|
213
|
+
f.write(driver.page_source)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def bypass_ssl_warning_page
|
219
|
+
wait_on(ignore_nse: true) do
|
220
|
+
find_element(id: 'details-button').click
|
221
|
+
find_element(id: 'proceed-link').click
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def login_to_comply(password)
|
226
|
+
output.simple "Logging into Comply at #{@url}..."
|
227
|
+
wait_on { driver.find_element(id: 'username') }
|
228
|
+
find_element(id: 'username').send_keys username
|
229
|
+
find_element(id: 'password').send_keys password
|
230
|
+
find_element(id: 'kc-login').click
|
231
|
+
error_text = wait_on(ignore_nse: true) { find_element(class: 'kc-feedback-text').text }
|
232
|
+
return if error_text.nil? || error_text.empty?
|
233
|
+
|
234
|
+
raise ComplyLoginFailedError, error_text
|
235
|
+
end
|
236
|
+
|
237
|
+
def filter_node_report_links(node_report_links)
|
238
|
+
if onlylist.empty? && ignorelist.empty?
|
239
|
+
output.simple 'No filters set, using all node reports...'
|
240
|
+
return node_report_links
|
241
|
+
end
|
242
|
+
|
243
|
+
unless onlylist.empty?
|
244
|
+
output.simple 'Onlylist found, filtering node reports...'
|
245
|
+
return node_report_links.select { |l| onlylist.include?(l[:name]) }
|
246
|
+
end
|
247
|
+
|
248
|
+
output.simple 'Ignorelist found, filtering node reports...'
|
249
|
+
node_report_links.reject { |l| ignorelist.include?(l[:name]) }
|
250
|
+
end
|
251
|
+
|
252
|
+
def find_node_report_table(subject)
|
253
|
+
wait_on { find_element(subject, class: 'metric-containers-failed-hosts-count') }
|
254
|
+
hosts = find_element(subject, class: 'metric-containers-failed-hosts-count')
|
255
|
+
table = find_element(hosts, class: 'rc-table')
|
256
|
+
wait_on { find_element(table, tag_name: 'tbody') }
|
257
|
+
find_element(table, tag_name: 'tbody')
|
258
|
+
end
|
259
|
+
|
260
|
+
def wait_for_node_report_links(table_body)
|
261
|
+
wait_on(timeout: 2, quit_driver: false, quiet: true) { table_body.find_element(tag_name: 'a') }
|
262
|
+
output.simple 'Found node report links...'
|
263
|
+
table_body.find_elements(tag_name: 'a')
|
264
|
+
rescue AbideDevUtils::Comply::WaitOnError
|
265
|
+
[]
|
266
|
+
end
|
267
|
+
|
268
|
+
def find_node_report_links
|
269
|
+
output.simple 'Finding nodes with scan reports...'
|
270
|
+
node_report_links = []
|
271
|
+
(1..max_pagination).each do |page|
|
272
|
+
output.simple "Trying page #{page}..."
|
273
|
+
driver.get("#{@url}/dashboard?page=#{page}&limit=50")
|
274
|
+
table_body = find_node_report_table(driver)
|
275
|
+
elems = wait_for_node_report_links(table_body)
|
276
|
+
if elems.empty?
|
277
|
+
output.simple "No links found on page #{page}, stopping search..."
|
278
|
+
break
|
279
|
+
end
|
280
|
+
|
281
|
+
elems.each do |elem|
|
282
|
+
node_report_links << { name: elem.text, url: elem.attribute('href') }
|
283
|
+
end
|
284
|
+
end
|
285
|
+
driver.get(@url)
|
286
|
+
filter_node_report_links(node_report_links)
|
287
|
+
end
|
288
|
+
|
289
|
+
def connect(password)
|
290
|
+
output.simple "Connecting to #{@url}..."
|
291
|
+
driver.get(@url)
|
292
|
+
bypass_ssl_warning_page
|
293
|
+
login_to_comply(password)
|
294
|
+
end
|
295
|
+
|
296
|
+
def normalize_cis_rec_name(name)
|
297
|
+
nstr = name.downcase
|
298
|
+
nstr.delete!('(/|\\|\+|:|\'|")')
|
299
|
+
nstr.gsub!(/(\s|\(|\)|-|\.)/, '_')
|
300
|
+
nstr.strip!
|
301
|
+
nstr
|
302
|
+
end
|
303
|
+
|
304
|
+
def scrape_report
|
305
|
+
output.simple 'Building scan reports, this may take a while...'
|
306
|
+
all_checks = {}
|
307
|
+
original_window = driver.window_handle
|
308
|
+
node_report_links.each do |link|
|
309
|
+
node_name = link[:name]
|
310
|
+
link_url = link[:url]
|
311
|
+
new_progress(node_name)
|
312
|
+
driver.manage.new_window(:tab)
|
313
|
+
progress.increment
|
314
|
+
wait_on { driver.window_handles.length == 2 }
|
315
|
+
progress.increment
|
316
|
+
driver.switch_to.window driver.window_handles[1]
|
317
|
+
driver.get(link_url)
|
318
|
+
wait_on { find_element(class: 'details-scan-info') }
|
319
|
+
progress.increment
|
320
|
+
wait_on { find_element(class: 'details-table') }
|
321
|
+
progress.increment
|
322
|
+
report = { 'scan_results' => {} }
|
323
|
+
scan_info_table = find_element(class: 'details-scan-info')
|
324
|
+
scan_info_table_rows = scan_info_table.find_elements(tag_name: 'tr')
|
325
|
+
progress.increment
|
326
|
+
check_table_body = find_element(tag_name: 'tbody')
|
327
|
+
check_table_rows = check_table_body.find_elements(tag_name: 'tr')
|
328
|
+
progress.increment
|
329
|
+
scan_info_table_rows.each do |row|
|
330
|
+
key = find_element(row, tag_name: 'h5').text
|
331
|
+
value = find_element(row, tag_name: 'strong').text
|
332
|
+
report[key.downcase.tr(':', '').tr(' ', '_')] = value
|
333
|
+
progress.increment
|
334
|
+
end
|
335
|
+
check_table_rows.each do |row|
|
336
|
+
chk_objs = row.find_elements(tag_name: 'td')
|
337
|
+
chk_objs.map!(&:text)
|
338
|
+
if status.nil? || status.include?(chk_objs[1].downcase)
|
339
|
+
name_parts = chk_objs[0].match(/^([0-9.]+) (.+)$/)
|
340
|
+
key = normalize_cis_rec_name(name_parts[2])
|
341
|
+
unless report['scan_results'].key?(chk_objs[1])
|
342
|
+
report['scan_results'][chk_objs[1]] = {}
|
343
|
+
end
|
344
|
+
report['scan_results'][chk_objs[1]][key] = {
|
345
|
+
'name' => name_parts[2].chomp,
|
346
|
+
'number' => name_parts[1].chomp
|
347
|
+
}
|
348
|
+
end
|
349
|
+
progress.increment
|
350
|
+
end
|
351
|
+
all_checks[node_name] = report
|
352
|
+
driver.close
|
353
|
+
output.simple "Created report for #{node_name}"
|
354
|
+
rescue ::StandardError => e
|
355
|
+
raise_error(e)
|
356
|
+
ensure
|
357
|
+
driver.switch_to.window original_window
|
358
|
+
end
|
359
|
+
all_checks
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
# Contains multiple NodeScanReport objects
|
364
|
+
class ScanReport
|
365
|
+
attr_reader :node_scan_reports
|
366
|
+
|
367
|
+
def from_yaml(report)
|
368
|
+
@scan_report = if report.is_a? Hash
|
369
|
+
report
|
370
|
+
elsif File.file?(report)
|
371
|
+
File.open(report.to_s, 'r') { |f| YAML.safe_load(f.read) }
|
372
|
+
else
|
373
|
+
YAML.safe_load(report)
|
374
|
+
end
|
375
|
+
@node_scan_reports = build_node_scan_reports
|
376
|
+
self
|
377
|
+
end
|
378
|
+
|
379
|
+
def to_h
|
380
|
+
node_scan_reports.each_with_object({}) do |node, h|
|
381
|
+
h[node.name] = node.hash
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
def to_yaml
|
386
|
+
to_h.to_yaml
|
387
|
+
end
|
388
|
+
|
389
|
+
def self.storage_bucket
|
390
|
+
@storage_bucket ||= AbideDevUtils::GCloud.storage_bucket
|
391
|
+
end
|
392
|
+
|
393
|
+
def self.fetch_report(name: 'comply_report.yaml')
|
394
|
+
report = storage_bucket.file(name)
|
395
|
+
report.download.read
|
396
|
+
end
|
397
|
+
|
398
|
+
def self.upload_report(report, name: 'comply_report.yaml')
|
399
|
+
storage_bucket.create_file(report, name)
|
400
|
+
end
|
401
|
+
|
402
|
+
def report_comparison(other, check_goodness: false)
|
403
|
+
comparison = []
|
404
|
+
node_scan_reports.zip(other.node_scan_reports).each do |cr, lr|
|
405
|
+
comparison << { cr.name => { diff: {}, node_presense: :new } } if lr.nil?
|
406
|
+
comparison << { lr.name => { diff: {}, node_presense: :dropped } } if cr.nil?
|
407
|
+
comparison << { cr.name => { diff: cr.diff(lr), node_presence: :same } } unless cr.nil? || lr.nil?
|
408
|
+
end
|
409
|
+
comparison.inject(&:merge)
|
410
|
+
return good_comparison?(comparison) if check_goodness
|
411
|
+
|
412
|
+
compairison
|
413
|
+
end
|
414
|
+
|
415
|
+
def good_comparison?(report_comparison)
|
416
|
+
good = true
|
417
|
+
not_good = {}
|
418
|
+
report_comparison.each do |node_report|
|
419
|
+
node_name = node_report.keys[0]
|
420
|
+
report = node_report[node_name]
|
421
|
+
next if report[:diff].empty?
|
422
|
+
|
423
|
+
not_good[node_name] = {}
|
424
|
+
unless report.dig(:diff, :passing, :other).nil?
|
425
|
+
good = false
|
426
|
+
not_good[node_name][:new_not_passing] = report[:diff][:passing][:other]
|
427
|
+
end
|
428
|
+
unless report.dig(:diff, :failing, :self).nil?
|
429
|
+
good = false
|
430
|
+
not_good[node_name][:new_failing] = report[:diff][:failing][:self]
|
431
|
+
end
|
432
|
+
end
|
433
|
+
[good, not_good]
|
434
|
+
end
|
435
|
+
|
436
|
+
private
|
437
|
+
|
438
|
+
def build_node_scan_reports
|
439
|
+
node_scan_reports = []
|
440
|
+
@scan_report.each do |node_name, node_hash|
|
441
|
+
node_scan_reports << NodeScanReport.new(node_name, node_hash)
|
442
|
+
end
|
443
|
+
node_scan_reports.sort_by(&:name)
|
444
|
+
end
|
445
|
+
end
|
446
|
+
|
447
|
+
# Class representation of a Comply node scan report
|
448
|
+
class NodeScanReport
|
449
|
+
attr_reader :name, :passing, :failing, :error, :not_checked, :informational, :benchmark, :last_scan, :profile
|
450
|
+
|
451
|
+
DIFF_PROPERTIES = %i[passing failing error not_checked informational].freeze
|
452
|
+
|
453
|
+
def initialize(node_name, node_hash)
|
454
|
+
@name = node_name
|
455
|
+
@hash = node_hash
|
456
|
+
@passing = node_hash.dig('scan_results', 'Pass') || {}
|
457
|
+
@failing = node_hash.dig('scan_results', 'Fail') || {}
|
458
|
+
@error = node_hash.dig('scan_results', 'Error') || {}
|
459
|
+
@not_checked = node_hash.dig('scan_results', 'Not checked') || {}
|
460
|
+
@informational = node_hash.dig('scan_results', 'Informational') || {}
|
461
|
+
@benchmark = node_hash['benchmark']
|
462
|
+
@last_scan = node_hash['last_scan']
|
463
|
+
@profile = node_hash.fetch('custom_profile', nil) || node_hash.fetch('profile', nil)
|
464
|
+
create_equality_methods
|
465
|
+
end
|
466
|
+
|
467
|
+
def diff(other)
|
468
|
+
diff = {}
|
469
|
+
DIFF_PROPERTIES.each do |prop|
|
470
|
+
diff[prop] = send("#{prop.to_s}_equal?".to_sym, other.send(prop)) ? {} : property_diff(prop, other)
|
471
|
+
end
|
472
|
+
diff
|
473
|
+
end
|
474
|
+
|
475
|
+
private
|
476
|
+
|
477
|
+
def create_equality_methods
|
478
|
+
DIFF_PROPERTIES.each do |prop|
|
479
|
+
meth_name = "#{prop.to_s}_equal?"
|
480
|
+
self.class.define_method(meth_name) do |other|
|
481
|
+
property_equal?(prop, other)
|
482
|
+
end
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
def property_diff(property, other)
|
487
|
+
{
|
488
|
+
self: send(property).keys - other.send(property).keys,
|
489
|
+
other: other.send(property).keys - send(property).keys
|
490
|
+
}
|
491
|
+
end
|
492
|
+
|
493
|
+
def property_equal?(property, other_property)
|
494
|
+
send(property) == other_property
|
495
|
+
end
|
496
|
+
end
|
497
|
+
end
|
498
|
+
end
|
@@ -13,6 +13,13 @@ module AbideDevUtils
|
|
13
13
|
h.transform_keys(&:to_sym)
|
14
14
|
end
|
15
15
|
|
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)
|
21
|
+
end
|
22
|
+
|
16
23
|
def self.config_section(section, path = DEFAULT_PATH)
|
17
24
|
h = to_h(path)
|
18
25
|
s = h.fetch(section.to_sym, nil)
|
@@ -21,8 +28,20 @@ module AbideDevUtils
|
|
21
28
|
s.transform_keys(&:to_sym)
|
22
29
|
end
|
23
30
|
|
31
|
+
def config_section(section, path = DEFAULT_PATH)
|
32
|
+
h = to_h(path)
|
33
|
+
s = h.fetch(section.to_sym, nil)
|
34
|
+
return {} if s.nil?
|
35
|
+
|
36
|
+
s.transform_keys(&:to_sym)
|
37
|
+
end
|
38
|
+
|
24
39
|
def self.fetch(key, default = nil, path = DEFAULT_PATH)
|
25
40
|
to_h(path).fetch(key, default)
|
26
41
|
end
|
42
|
+
|
43
|
+
def fetch(key, default = nil, path = DEFAULT_PATH)
|
44
|
+
to_h(path).fetch(key, default)
|
45
|
+
end
|
27
46
|
end
|
28
47
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'abide_dev_utils/errors/base'
|
4
|
+
|
5
|
+
module AbideDevUtils
|
6
|
+
module Errors
|
7
|
+
module Comply
|
8
|
+
class ComplyLoginFailedError < GenericError
|
9
|
+
@default = 'Failed to login to Comply:'
|
10
|
+
end
|
11
|
+
|
12
|
+
class WaitOnError < GenericError
|
13
|
+
@default = 'wait_on failed due to error:'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'abide_dev_utils/errors/base'
|
4
|
+
|
5
|
+
module AbideDevUtils
|
6
|
+
module Errors
|
7
|
+
module GCloud
|
8
|
+
class MissingCredentialsError < GenericError
|
9
|
+
@default = <<~EOERR
|
10
|
+
Storage credentials not given. Please set environment variable ABIDE_GCLOUD_CREDENTIALS.
|
11
|
+
EOERR
|
12
|
+
end
|
13
|
+
|
14
|
+
class MissingProjectError < GenericError
|
15
|
+
@default = <<~EOERR
|
16
|
+
Storage project not given. Please set the environment variable ABIDE_GCLOUD_PROJECT.
|
17
|
+
EOERR
|
18
|
+
end
|
19
|
+
|
20
|
+
class MissingBucketNameError < GenericError
|
21
|
+
@default = <<~EOERR
|
22
|
+
Storage bucket name not given. Please set the environment variable ABIDE_GCLOUD_BUCKET.
|
23
|
+
EOERR
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -29,6 +29,11 @@ module AbideDevUtils
|
|
29
29
|
@default = 'Path is not a directory:'
|
30
30
|
end
|
31
31
|
|
32
|
+
# Raised when a file extension is not correct
|
33
|
+
class FileExtensionIncorrectError < GenericError
|
34
|
+
@default = 'File extension does not match specified extension:'
|
35
|
+
end
|
36
|
+
|
32
37
|
# Raised when a searched for service is not found in the parser
|
33
38
|
class ServiceNotFoundError < GenericError
|
34
39
|
@default = 'Service not found:'
|
@@ -24,6 +24,18 @@ module AbideDevUtils
|
|
24
24
|
class FailedToCreateFileError < GenericError
|
25
25
|
@default = 'Failed to create file:'
|
26
26
|
end
|
27
|
+
|
28
|
+
class ClassFileNotFoundError < GenericError
|
29
|
+
@default = 'Class file was not found:'
|
30
|
+
end
|
31
|
+
|
32
|
+
class ClassDeclarationNotFoundError < GenericError
|
33
|
+
@default = 'Class declaration was not found:'
|
34
|
+
end
|
35
|
+
|
36
|
+
class InvalidClassNameError < GenericError
|
37
|
+
@default = 'Not a valid Puppet class name:'
|
38
|
+
end
|
27
39
|
end
|
28
40
|
end
|
29
41
|
end
|
@@ -12,5 +12,13 @@ module AbideDevUtils
|
|
12
12
|
class StrategyInvalidError < GenericError
|
13
13
|
@default = 'Invalid strategy selected. Should be either \'name\' or \'num\''
|
14
14
|
end
|
15
|
+
|
16
|
+
class ControlPartsError < GenericError
|
17
|
+
@default = 'Failed to extract parts from control name:'
|
18
|
+
end
|
19
|
+
|
20
|
+
class ProfilePartsError < GenericError
|
21
|
+
@default = 'Failed to extract parts from profile name:'
|
22
|
+
end
|
15
23
|
end
|
16
24
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'abide_dev_utils/errors/base'
|
4
|
+
require 'abide_dev_utils/errors/comply'
|
5
|
+
require 'abide_dev_utils/errors/gcloud'
|
4
6
|
require 'abide_dev_utils/errors/general'
|
5
7
|
require 'abide_dev_utils/errors/jira'
|
6
8
|
require 'abide_dev_utils/errors/xccdf'
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'abide_dev_utils/errors/gcloud'
|
5
|
+
|
6
|
+
module AbideDevUtils
|
7
|
+
module GCloud
|
8
|
+
include AbideDevUtils::Errors::GCloud
|
9
|
+
|
10
|
+
def self.storage_bucket(name: nil, project: nil, credentials: nil)
|
11
|
+
raise MissingProjectError if project.nil? && ENV['ABIDE_GCLOUD_PROJECT'].nil?
|
12
|
+
raise MissingCredentialsError if credentials.nil? && ENV['ABIDE_GCLOUD_CREDENTIALS'].nil?
|
13
|
+
raise MissingBucketNameError if name.nil? && ENV['ABIDE_GCLOUD_BUCKET'].nil?
|
14
|
+
|
15
|
+
require 'google/cloud/storage'
|
16
|
+
@bucket = Google::Cloud::Storage.new(
|
17
|
+
project_id: project || ENV['ABIDE_GCLOUD_PROJECT'],
|
18
|
+
credentials: credentials || JSON.parse(ENV['ABIDE_GCLOUD_CREDENTIALS'])
|
19
|
+
).bucket(name || ENV['ABIDE_GCLOUD_BUCKET'])
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/abide_dev_utils/jira.rb
CHANGED
@@ -135,6 +135,16 @@ module AbideDevUtils
|
|
135
135
|
end
|
136
136
|
end
|
137
137
|
|
138
|
+
# def self.new_issues_from_comply_report(client, project, report, dry_run: false)
|
139
|
+
# dr_prefix = dry_run ? 'DRY RUN: ' : ''
|
140
|
+
# i_attrs = all_project_issues_attrs(project)
|
141
|
+
# rep_sums = summaries_from_coverage_report(report)
|
142
|
+
# rep_sums.each do |k, v|
|
143
|
+
# next if summary_exist?(k, i_attrs)
|
144
|
+
|
145
|
+
# progress = AbideDevUtils::Output.progress(title: "#{dr_prefix}Creating Tasks", total: nil)
|
146
|
+
# v.each do |s|
|
147
|
+
|
138
148
|
def self.merge_options(options)
|
139
149
|
config.merge(options)
|
140
150
|
end
|
@@ -167,6 +177,11 @@ module AbideDevUtils
|
|
167
177
|
summaries.transform_keys { |k| "#{COV_PARENT_SUMMARY_PREFIX}#{benchmark}-#{k}"}
|
168
178
|
end
|
169
179
|
|
180
|
+
# def self.summaries_from_comply_report(report)
|
181
|
+
# summaries = {}
|
182
|
+
# report.each do |_, v|
|
183
|
+
# end
|
184
|
+
|
170
185
|
class Dummy
|
171
186
|
def attrs
|
172
187
|
{ 'fields' => {
|