abide_dev_utils 0.6.0 → 0.9.3
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 -2
- data/.rubocop.yml +1 -1
- data/CODEOWNERS +1 -0
- data/Gemfile.lock +273 -0
- data/abide_dev_utils.gemspec +7 -6
- data/lib/abide_dev_utils/cli/comply.rb +26 -7
- data/lib/abide_dev_utils/cli/puppet.rb +18 -0
- data/lib/abide_dev_utils/cli/xccdf.rb +77 -11
- data/lib/abide_dev_utils/comply.rb +240 -169
- data/lib/abide_dev_utils/errors/comply.rb +4 -0
- data/lib/abide_dev_utils/errors/general.rb +9 -0
- data/lib/abide_dev_utils/errors/xccdf.rb +12 -0
- data/lib/abide_dev_utils/gcloud.rb +2 -1
- 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/score_module.rb +162 -0
- data/lib/abide_dev_utils/ppt.rb +22 -19
- 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 +627 -11
- metadata +30 -16
- data/.dockerignore +0 -1
- data/Dockerfile +0 -23
- data/lib/abide_dev_utils/xccdf/cis/hiera.rb +0 -166
- data/lib/abide_dev_utils/xccdf/cis.rb +0 -3
- data/lib/abide_dev_utils/xccdf/utils.rb +0 -85
@@ -1,11 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'json'
|
3
4
|
require 'yaml'
|
4
5
|
require 'selenium-webdriver'
|
5
6
|
require 'abide_dev_utils/errors/comply'
|
6
7
|
require 'abide_dev_utils/gcloud'
|
7
8
|
require 'abide_dev_utils/output'
|
8
9
|
require 'abide_dev_utils/prompt'
|
10
|
+
require 'pry'
|
9
11
|
|
10
12
|
module AbideDevUtils
|
11
13
|
# Holds module methods and a class for dealing with Puppet Comply
|
@@ -16,56 +18,55 @@ module AbideDevUtils
|
|
16
18
|
ReportScraper.new(url, config, **opts).build_report(password)
|
17
19
|
end
|
18
20
|
|
19
|
-
def self.
|
20
|
-
|
21
|
-
|
22
|
-
|
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))
|
23
27
|
else
|
24
|
-
File.
|
28
|
+
report_name = File.basename(report_b) if report_name.nil?
|
29
|
+
ScanReport.new.from_yaml(File.read(report_b))
|
25
30
|
end
|
26
|
-
result, details =
|
31
|
+
result, details = current_report.report_comparison(last_report, check_goodness: true)
|
27
32
|
if result
|
28
|
-
|
33
|
+
AbideDevUtils::Output.simple('No negative differences detected...')
|
34
|
+
AbideDevUtils::Output.simple(JSON.pretty_generate(details))
|
29
35
|
else
|
30
|
-
|
31
|
-
|
36
|
+
AbideDevUtils::Output.simple('Negative differences detected!', stream: $stderr)
|
37
|
+
AbideDevUtils::Output.simple(JSON.pretty_generate(details), stream: $stderr)
|
32
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
|
33
45
|
end
|
34
46
|
|
35
47
|
# Class that uses Selenium WebDriver to gather scan reports from Puppet Comply
|
36
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
|
+
|
37
58
|
def initialize(url, config = nil, **opts)
|
38
59
|
@url = url
|
39
60
|
@config = config
|
40
61
|
@opts = opts
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
@
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
@
|
49
|
-
end
|
50
|
-
|
51
|
-
def status
|
52
|
-
@status ||= fetch_option(:status)
|
53
|
-
end
|
54
|
-
|
55
|
-
def ignorelist
|
56
|
-
@ignorelist ||= fetch_option(:ignorelist, [])
|
57
|
-
end
|
58
|
-
|
59
|
-
def onlylist
|
60
|
-
@onlylist ||= fetch_option(:onlylist, [])
|
61
|
-
end
|
62
|
-
|
63
|
-
def screenshot_on_error
|
64
|
-
@screenshot_on_error ||= fetch_option(:screenshot_on_error, true)
|
65
|
-
end
|
66
|
-
|
67
|
-
def page_source_on_error
|
68
|
-
@page_source_on_error ||= fetch_option(:page_source_on_error, true)
|
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)
|
69
70
|
end
|
70
71
|
|
71
72
|
def build_report(password)
|
@@ -119,6 +120,7 @@ module AbideDevUtils
|
|
119
120
|
--headless
|
120
121
|
--test-type
|
121
122
|
--disable-gpu
|
123
|
+
--no-sandbox
|
122
124
|
--no-first-run
|
123
125
|
--no-default-browser-check
|
124
126
|
--ignore-certificate-errors
|
@@ -133,27 +135,37 @@ module AbideDevUtils
|
|
133
135
|
subject.find_element(**kwargs)
|
134
136
|
end
|
135
137
|
|
136
|
-
def
|
138
|
+
def find_elements(subject = driver, **kwargs)
|
139
|
+
driver.manage.window.resize_to(1920, 1080)
|
140
|
+
subject.find_elements(**kwargs)
|
141
|
+
end
|
142
|
+
|
143
|
+
def wait_on(timeout: @timeout,
|
144
|
+
ignore_nse: false,
|
145
|
+
quit_driver: true,
|
146
|
+
quiet: false,
|
147
|
+
ignore: [Selenium::WebDriver::Error::NoSuchElementError],
|
148
|
+
&block)
|
137
149
|
raise 'wait_on must be passed a block' unless block
|
138
150
|
|
139
151
|
value = nil
|
140
152
|
if ignore_nse
|
141
153
|
begin
|
142
|
-
Selenium::WebDriver::Wait.new(ignore: [], timeout: timeout).until do
|
154
|
+
Selenium::WebDriver::Wait.new(ignore: [], timeout: timeout, interval: 1).until do
|
143
155
|
value = yield
|
144
156
|
end
|
145
157
|
rescue Selenium::WebDriver::Error::NoSuchElementError
|
146
158
|
return value
|
147
|
-
rescue => e
|
148
|
-
raise_error(e)
|
159
|
+
rescue StandardError => e
|
160
|
+
raise_error(e, AbideDevUtils::Comply::WaitOnError, quit_driver: quit_driver, quiet: quiet)
|
149
161
|
end
|
150
162
|
else
|
151
163
|
begin
|
152
|
-
Selenium::WebDriver::Wait.new(ignore: ignore, timeout: timeout).until do
|
164
|
+
Selenium::WebDriver::Wait.new(ignore: ignore, timeout: timeout, interval: 1).until do
|
153
165
|
value = yield
|
154
166
|
end
|
155
|
-
rescue => e
|
156
|
-
raise_error(e)
|
167
|
+
rescue StandardError => e
|
168
|
+
raise_error(e, AbideDevUtils::Comply::WaitOnError, quit_driver: quit_driver, quiet: quiet)
|
157
169
|
end
|
158
170
|
end
|
159
171
|
value
|
@@ -169,20 +181,21 @@ module AbideDevUtils
|
|
169
181
|
FileUtils.mkdir_p path
|
170
182
|
end
|
171
183
|
|
172
|
-
def raise_error(
|
173
|
-
output.simple 'Something went wrong!'
|
184
|
+
def raise_error(original, err_class = nil, quit_driver: true, quiet: false)
|
185
|
+
output.simple 'Something went wrong!' unless quiet
|
174
186
|
if screenshot_on_error
|
175
|
-
output.simple 'Taking a screenshot of current page state...'
|
187
|
+
output.simple 'Taking a screenshot of current page state...' unless quiet
|
176
188
|
screenshot
|
177
189
|
end
|
178
190
|
|
179
191
|
if page_source_on_error
|
180
|
-
output.simple 'Saving page source of current page...'
|
192
|
+
output.simple 'Saving page source of current page...' unless quiet
|
181
193
|
page_source
|
182
194
|
end
|
183
195
|
|
184
|
-
driver.quit
|
185
|
-
|
196
|
+
driver.quit if quit_driver
|
197
|
+
actual_err_class = err_class.nil? ? original.class : err_class
|
198
|
+
raise actual_err_class, original.message
|
186
199
|
end
|
187
200
|
|
188
201
|
def screenshot
|
@@ -223,15 +236,59 @@ module AbideDevUtils
|
|
223
236
|
error_text = wait_on(ignore_nse: true) { find_element(class: 'kc-feedback-text').text }
|
224
237
|
return if error_text.nil? || error_text.empty?
|
225
238
|
|
226
|
-
raise ComplyLoginFailedError, error_text
|
239
|
+
raise AbideDevUtils::Comply::ComplyLoginFailedError, error_text
|
240
|
+
end
|
241
|
+
|
242
|
+
def filter_node_report_links(node_report_links)
|
243
|
+
if onlylist.empty? && ignorelist.empty?
|
244
|
+
output.simple 'No filters set, using all node reports...'
|
245
|
+
return node_report_links
|
246
|
+
end
|
247
|
+
|
248
|
+
unless onlylist.empty?
|
249
|
+
output.simple 'Onlylist found, filtering node reports...'
|
250
|
+
return node_report_links.select { |l| onlylist.include?(l[:name]) }
|
251
|
+
end
|
252
|
+
|
253
|
+
output.simple 'Ignorelist found, filtering node reports...'
|
254
|
+
node_report_links.reject { |l| ignorelist.include?(l[:name]) }
|
255
|
+
end
|
256
|
+
|
257
|
+
def find_node_report_table(subject)
|
258
|
+
wait_on { find_element(subject, class: 'metric-containers-failed-hosts-count') }
|
259
|
+
hosts = find_element(subject, class: 'metric-containers-failed-hosts-count')
|
260
|
+
table = find_element(hosts, class: 'rc-table')
|
261
|
+
wait_on { find_element(table, tag_name: 'tbody') }
|
262
|
+
find_element(table, tag_name: 'tbody')
|
263
|
+
end
|
264
|
+
|
265
|
+
def wait_for_node_report_links(table_body)
|
266
|
+
wait_on(timeout: 2, quit_driver: false, quiet: true) { table_body.find_element(tag_name: 'a') }
|
267
|
+
output.simple 'Found node report links...'
|
268
|
+
table_body.find_elements(tag_name: 'a')
|
269
|
+
rescue AbideDevUtils::Comply::WaitOnError
|
270
|
+
[]
|
227
271
|
end
|
228
272
|
|
229
273
|
def find_node_report_links
|
230
274
|
output.simple 'Finding nodes with scan reports...'
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
275
|
+
node_report_links = []
|
276
|
+
(1..max_pagination).each do |page|
|
277
|
+
output.simple "Trying page #{page}..."
|
278
|
+
driver.get("#{@url}/dashboard?page=#{page}&limit=50")
|
279
|
+
table_body = find_node_report_table(driver)
|
280
|
+
elems = wait_for_node_report_links(table_body)
|
281
|
+
if elems.empty?
|
282
|
+
output.simple "No links found on page #{page}, stopping search..."
|
283
|
+
break
|
284
|
+
end
|
285
|
+
|
286
|
+
elems.each do |elem|
|
287
|
+
node_report_links << { name: elem.text, url: elem.attribute('href') }
|
288
|
+
end
|
289
|
+
end
|
290
|
+
driver.get(@url)
|
291
|
+
filter_node_report_links(node_report_links)
|
235
292
|
end
|
236
293
|
|
237
294
|
def connect(password)
|
@@ -249,67 +306,69 @@ module AbideDevUtils
|
|
249
306
|
nstr
|
250
307
|
end
|
251
308
|
|
309
|
+
def wait_on_element_and_increment(subject = driver, **element_id)
|
310
|
+
element = wait_on { find_element(subject, **element_id) }
|
311
|
+
progress.increment
|
312
|
+
element
|
313
|
+
end
|
314
|
+
|
315
|
+
def wait_on_elements_and_increment(subject = driver, **element_id)
|
316
|
+
elements = wait_on { find_elements(subject, **element_id) }
|
317
|
+
progress.increment
|
318
|
+
elements
|
319
|
+
end
|
320
|
+
|
252
321
|
def scrape_report
|
253
322
|
output.simple 'Building scan reports, this may take a while...'
|
254
323
|
all_checks = {}
|
255
324
|
original_window = driver.window_handle
|
256
|
-
if !onlylist.empty?
|
257
|
-
node_report_links.reject! { |l| !onlylist.include?(l.text) }
|
258
|
-
elsif !ignorelist.empty?
|
259
|
-
node_report_links.reject! { |l| ignorelist.include?(l.text) }
|
260
|
-
end
|
261
325
|
node_report_links.each do |link|
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
326
|
+
node_name = link[:name]
|
327
|
+
link_url = link[:url]
|
328
|
+
new_progress(node_name)
|
329
|
+
driver.manage.new_window(:tab)
|
330
|
+
progress.increment
|
331
|
+
wait_on { driver.window_handles.length == 2 }
|
332
|
+
progress.increment
|
333
|
+
driver.switch_to.window driver.window_handles[1]
|
334
|
+
driver.get(link_url)
|
335
|
+
wait_on_element_and_increment(class: 'details-header')
|
336
|
+
wait_on_element_and_increment(class: 'details-scan-info')
|
337
|
+
wait_on_element_and_increment(class: 'details-table')
|
338
|
+
report = { 'scan_results' => {} }
|
339
|
+
scan_info_table = wait_on_element_and_increment(class: 'details-scan-info')
|
340
|
+
scan_info_table_rows = wait_on_elements_and_increment(scan_info_table, tag_name: 'tr')
|
341
|
+
check_table_body = wait_on_element_and_increment(tag_name: 'tbody')
|
342
|
+
check_table_rows = wait_on_elements_and_increment(check_table_body, tag_name: 'tr')
|
343
|
+
scan_info_table_rows.each do |row|
|
344
|
+
key = find_element(row, tag_name: 'h5').text
|
345
|
+
value = find_element(row, tag_name: 'strong').text
|
346
|
+
report[key.downcase.tr(':', '').tr(' ', '_')] = value
|
267
347
|
progress.increment
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
scan_info_table = find_element(class: 'details-scan-info')
|
278
|
-
scan_info_table_rows = scan_info_table.find_elements(tag_name: 'tr')
|
279
|
-
progress.increment
|
280
|
-
check_table_body = find_element(tag_name: 'tbody')
|
281
|
-
check_table_rows = check_table_body.find_elements(tag_name: 'tr')
|
282
|
-
progress.increment
|
283
|
-
scan_info_table_rows.each do |row|
|
284
|
-
key = find_element(row, tag_name: 'h5').text
|
285
|
-
value = find_element(row, tag_name: 'strong').text
|
286
|
-
report[key.downcase.tr(':', '').tr(' ', '_')] = value
|
287
|
-
progress.increment
|
288
|
-
end
|
289
|
-
check_table_rows.each do |row|
|
290
|
-
chk_objs = row.find_elements(tag_name: 'td')
|
291
|
-
chk_objs.map!(&:text)
|
292
|
-
if status.nil? || status.include?(chk_objs[1].downcase)
|
293
|
-
name_parts = chk_objs[0].match(/^([0-9.]+) (.+)$/)
|
294
|
-
key = normalize_cis_rec_name(name_parts[2])
|
295
|
-
unless report['scan_results'].key?(chk_objs[1])
|
296
|
-
report['scan_results'][chk_objs[1]] = {}
|
297
|
-
end
|
298
|
-
report['scan_results'][chk_objs[1]][key] = {
|
299
|
-
'name' => name_parts[2].chomp,
|
300
|
-
'number' => name_parts[1].chomp
|
301
|
-
}
|
348
|
+
end
|
349
|
+
check_table_rows.each do |row|
|
350
|
+
chk_objs = row.find_elements(tag_name: 'td')
|
351
|
+
chk_objs.map!(&:text)
|
352
|
+
if status.nil? || status.include?(chk_objs[1].downcase)
|
353
|
+
name_parts = chk_objs[0].match(/^([0-9.]+) (.+)$/)
|
354
|
+
key = normalize_cis_rec_name(name_parts[2])
|
355
|
+
unless report['scan_results'].key?(chk_objs[1])
|
356
|
+
report['scan_results'][chk_objs[1]] = {}
|
302
357
|
end
|
303
|
-
|
358
|
+
report['scan_results'][chk_objs[1]][key] = {
|
359
|
+
'name' => name_parts[2].chomp,
|
360
|
+
'number' => name_parts[1].chomp
|
361
|
+
}
|
304
362
|
end
|
305
|
-
|
306
|
-
driver.close
|
307
|
-
output.simple "Created report for #{node_name}"
|
308
|
-
rescue => e
|
309
|
-
raise_error(e)
|
310
|
-
ensure
|
311
|
-
driver.switch_to.window original_window
|
363
|
+
progress.increment
|
312
364
|
end
|
365
|
+
all_checks[node_name] = report
|
366
|
+
driver.close
|
367
|
+
output.simple "Created report for #{node_name}"
|
368
|
+
rescue ::StandardError => e
|
369
|
+
raise_error(e)
|
370
|
+
ensure
|
371
|
+
driver.switch_to.window original_window
|
313
372
|
end
|
314
373
|
all_checks
|
315
374
|
end
|
@@ -317,6 +376,8 @@ module AbideDevUtils
|
|
317
376
|
|
318
377
|
# Contains multiple NodeScanReport objects
|
319
378
|
class ScanReport
|
379
|
+
attr_reader :node_scan_reports
|
380
|
+
|
320
381
|
def from_yaml(report)
|
321
382
|
@scan_report = if report.is_a? Hash
|
322
383
|
report
|
@@ -325,7 +386,65 @@ module AbideDevUtils
|
|
325
386
|
else
|
326
387
|
YAML.safe_load(report)
|
327
388
|
end
|
328
|
-
build_node_scan_reports
|
389
|
+
@node_scan_reports = build_node_scan_reports
|
390
|
+
self
|
391
|
+
end
|
392
|
+
|
393
|
+
def to_h
|
394
|
+
node_scan_reports.each_with_object({}) do |node, h|
|
395
|
+
h[node.name] = node.hash
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
def to_yaml
|
400
|
+
to_h.to_yaml
|
401
|
+
end
|
402
|
+
|
403
|
+
def self.storage_bucket
|
404
|
+
@storage_bucket ||= AbideDevUtils::GCloud.storage_bucket
|
405
|
+
end
|
406
|
+
|
407
|
+
def self.fetch_report(name: 'comply_report.yaml')
|
408
|
+
report = storage_bucket.file(name)
|
409
|
+
report.download.read
|
410
|
+
end
|
411
|
+
|
412
|
+
def self.upload_report(report, name: 'comply_report.yaml')
|
413
|
+
storage_bucket.create_file(report, name)
|
414
|
+
end
|
415
|
+
|
416
|
+
def report_comparison(other, check_goodness: false)
|
417
|
+
comparison = []
|
418
|
+
node_scan_reports.zip(other.node_scan_reports).each do |cr, lr|
|
419
|
+
comparison << { cr.name => { diff: {}, node_presense: :new } } if lr.nil?
|
420
|
+
comparison << { lr.name => { diff: {}, node_presense: :dropped } } if cr.nil?
|
421
|
+
comparison << { cr.name => { diff: cr.diff(lr), node_presence: :same } } unless cr.nil? || lr.nil?
|
422
|
+
end
|
423
|
+
comparison.inject(&:merge)
|
424
|
+
return good_comparison?(comparison) if check_goodness
|
425
|
+
|
426
|
+
compairison
|
427
|
+
end
|
428
|
+
|
429
|
+
def good_comparison?(report_comparison)
|
430
|
+
good = true
|
431
|
+
not_good = {}
|
432
|
+
report_comparison.each do |node_report|
|
433
|
+
node_name = node_report.keys[0]
|
434
|
+
report = node_report[node_name]
|
435
|
+
next if report[:diff].empty?
|
436
|
+
|
437
|
+
not_good[node_name] = {}
|
438
|
+
unless report.dig(:diff, :passing, :other).nil?
|
439
|
+
good = false
|
440
|
+
not_good[node_name][:new_not_passing] = report[:diff][:passing][:other]
|
441
|
+
end
|
442
|
+
unless report.dig(:diff, :failing, :self).nil?
|
443
|
+
good = false
|
444
|
+
not_good[node_name][:new_failing] = report[:diff][:failing][:self]
|
445
|
+
end
|
446
|
+
end
|
447
|
+
[good, not_good]
|
329
448
|
end
|
330
449
|
|
331
450
|
private
|
@@ -341,20 +460,22 @@ module AbideDevUtils
|
|
341
460
|
|
342
461
|
# Class representation of a Comply node scan report
|
343
462
|
class NodeScanReport
|
344
|
-
attr_reader :name, :passing, :failing, :not_checked, :informational, :benchmark, :last_scan, :profile
|
463
|
+
attr_reader :name, :passing, :failing, :error, :not_checked, :informational, :benchmark, :last_scan, :profile
|
345
464
|
|
346
|
-
DIFF_PROPERTIES = %i[passing failing not_checked informational].freeze
|
465
|
+
DIFF_PROPERTIES = %i[passing failing error not_checked informational].freeze
|
347
466
|
|
348
467
|
def initialize(node_name, node_hash)
|
349
468
|
@name = node_name
|
350
469
|
@hash = node_hash
|
351
470
|
@passing = node_hash.dig('scan_results', 'Pass') || {}
|
352
471
|
@failing = node_hash.dig('scan_results', 'Fail') || {}
|
472
|
+
@error = node_hash.dig('scan_results', 'Error') || {}
|
353
473
|
@not_checked = node_hash.dig('scan_results', 'Not checked') || {}
|
354
474
|
@informational = node_hash.dig('scan_results', 'Informational') || {}
|
355
475
|
@benchmark = node_hash['benchmark']
|
356
476
|
@last_scan = node_hash['last_scan']
|
357
477
|
@profile = node_hash.fetch('custom_profile', nil) || node_hash.fetch('profile', nil)
|
478
|
+
create_equality_methods
|
358
479
|
end
|
359
480
|
|
360
481
|
def diff(other)
|
@@ -365,21 +486,17 @@ module AbideDevUtils
|
|
365
486
|
diff
|
366
487
|
end
|
367
488
|
|
368
|
-
|
369
|
-
case method_name
|
370
|
-
when method_name.match?(/^(passing|failing|not_checked|informational)_equal?$/)
|
371
|
-
property_equal?(method_name.delete_suffix('_equal?'), *args)
|
372
|
-
when method_name.match?(/^(to_h|to_yaml)$/)
|
373
|
-
@hash.send(method_name.to_sym)
|
374
|
-
end
|
375
|
-
end
|
489
|
+
private
|
376
490
|
|
377
|
-
def
|
378
|
-
|
491
|
+
def create_equality_methods
|
492
|
+
DIFF_PROPERTIES.each do |prop|
|
493
|
+
meth_name = "#{prop.to_s}_equal?"
|
494
|
+
self.class.define_method(meth_name) do |other|
|
495
|
+
property_equal?(prop, other)
|
496
|
+
end
|
497
|
+
end
|
379
498
|
end
|
380
499
|
|
381
|
-
private
|
382
|
-
|
383
500
|
def property_diff(property, other)
|
384
501
|
{
|
385
502
|
self: send(property).keys - other.send(property).keys,
|
@@ -388,54 +505,8 @@ module AbideDevUtils
|
|
388
505
|
end
|
389
506
|
|
390
507
|
def property_equal?(property, other_property)
|
391
|
-
send(property
|
392
|
-
end
|
393
|
-
end
|
394
|
-
|
395
|
-
def self.storage_bucket
|
396
|
-
@storage_bucket ||= AbideDevUtils::GCloud.storage_bucket
|
397
|
-
end
|
398
|
-
|
399
|
-
def self.fetch_report
|
400
|
-
report = storage_bucket.file('comply_report.yaml')
|
401
|
-
report.download.read
|
402
|
-
end
|
403
|
-
|
404
|
-
def self.upload_report(report)
|
405
|
-
file_to_upload = report.is_a?(Hash) ? report.to_yaml : report
|
406
|
-
storage_bucket.create_file(file_to_upload, 'comply_report.yaml')
|
407
|
-
end
|
408
|
-
|
409
|
-
def self.report_comparison(current, last)
|
410
|
-
current_report = ScanReport.new.from_yaml(current)
|
411
|
-
last_report = ScanReport.new.from_yaml(last)
|
412
|
-
|
413
|
-
comparison = []
|
414
|
-
current_report.zip(last_report).each do |cr, lr|
|
415
|
-
comparison << { cr.name => { diff: {}, node_presense: :new } } if lr.nil?
|
416
|
-
comparison << { lr.name => { diff: {}, node_presense: :dropped } } if cr.nil?
|
417
|
-
comparison << { cr.name => { diff: cr.diff(lr), node_presence: :same } } unless cr.nil? || lr.nil?
|
418
|
-
end
|
419
|
-
comparison.inject(&:merge)
|
420
|
-
end
|
421
|
-
|
422
|
-
def self.good_comparison?(report_comparison)
|
423
|
-
good = true
|
424
|
-
not_good = {}
|
425
|
-
report_comparison.each do |node_name, report|
|
426
|
-
next if report[:diff].empty?
|
427
|
-
|
428
|
-
not_good[node_name] = {}
|
429
|
-
unless report[:diff][:passing][:other].empty?
|
430
|
-
good = false
|
431
|
-
not_good[node_name][:new_not_passing] = report[:diff][:passing][:other]
|
432
|
-
end
|
433
|
-
unless report[:diff][:failing][:self].empty?
|
434
|
-
good = false
|
435
|
-
not_good[node_name][:new_failing] = report[:diff][:failing][:self]
|
436
|
-
end
|
508
|
+
send(property) == other_property
|
437
509
|
end
|
438
|
-
[good, not_good]
|
439
510
|
end
|
440
511
|
end
|
441
512
|
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:'
|
@@ -48,5 +53,9 @@ module AbideDevUtils
|
|
48
53
|
class NotHashableError < GenericError
|
49
54
|
@default = 'Object does not respond to #to_hash or #to_h:'
|
50
55
|
end
|
56
|
+
|
57
|
+
class CliOptionsConflict < GenericError
|
58
|
+
@default = "Console options conflict."
|
59
|
+
end
|
51
60
|
end
|
52
61
|
end
|
@@ -12,5 +12,17 @@ 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
|
23
|
+
|
24
|
+
class UnsupportedXCCDFError < GenericError
|
25
|
+
@default = "XCCDF type is unsupported!"
|
26
|
+
end
|
15
27
|
end
|
16
28
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'json'
|
3
4
|
require 'abide_dev_utils/errors/gcloud'
|
4
5
|
|
5
6
|
module AbideDevUtils
|
@@ -14,7 +15,7 @@ module AbideDevUtils
|
|
14
15
|
require 'google/cloud/storage'
|
15
16
|
@bucket = Google::Cloud::Storage.new(
|
16
17
|
project_id: project || ENV['ABIDE_GCLOUD_PROJECT'],
|
17
|
-
credentials: credentials || ENV['ABIDE_GCLOUD_CREDENTIALS']
|
18
|
+
credentials: credentials || JSON.parse(ENV['ABIDE_GCLOUD_CREDENTIALS'])
|
18
19
|
).bucket(name || ENV['ABIDE_GCLOUD_BUCKET'])
|
19
20
|
end
|
20
21
|
end
|
@@ -22,9 +22,13 @@ module AbideDevUtils
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def self.yaml(in_obj, console: false, file: nil)
|
25
|
-
|
26
|
-
|
27
|
-
|
25
|
+
yaml_out = if in_obj.is_a? String
|
26
|
+
in_obj
|
27
|
+
else
|
28
|
+
AbideDevUtils::Validate.hashable(in_obj)
|
29
|
+
# Use object's #to_yaml method if it exists, convert to hash if not
|
30
|
+
in_obj.respond_to?(:to_yaml) ? in_obj.to_yaml : in_obj.to_h.to_yaml
|
31
|
+
end
|
28
32
|
simple(yaml_out) if console
|
29
33
|
FWRITER.write_yaml(yaml_out, file: file) unless file.nil?
|
30
34
|
end
|