abide_dev_utils 0.4.2 → 0.5.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/README.md +4 -0
- data/abide_dev_utils.gemspec +1 -0
- data/lib/abide_dev_utils/cli.rb +2 -0
- data/lib/abide_dev_utils/cli/abstract.rb +2 -0
- data/lib/abide_dev_utils/cli/comply.rb +97 -0
- data/lib/abide_dev_utils/cli/jira.rb +2 -2
- data/lib/abide_dev_utils/comply.rb +130 -0
- data/lib/abide_dev_utils/config.rb +19 -0
- data/lib/abide_dev_utils/jira.rb +15 -0
- data/lib/abide_dev_utils/version.rb +1 -1
- metadata +18 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a27976b9f740b67261fa080eeba927bc6fc98e73ffb592fafdd91d4d612cc51f
|
4
|
+
data.tar.gz: 64778a0d476e2f96d70a14ab318b517c597aa3c5e33afd8939173385b013fe6c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e31df40e6dd34a57156ff517d39fcc036f5655ce2f31bf86b1ebb66754304575b7379e0a3751ae378bc81bc1520dbe095255012dc7e10b24e4f6c3f4a85e6551
|
7
|
+
data.tar.gz: c9dd204d55a37d389c0c8a4d3d281310b87241cbbd149ca029a3fa6a38fd8414bcb350467cd348cdd330fe946e6ee75625bfe2828319ad25d9eb8ed5cc37a9a3
|
data/README.md
CHANGED
@@ -29,6 +29,10 @@ Issues and pull requests are welcome!
|
|
29
29
|
|
30
30
|
* Create Jira issues in bulk from coverage reports
|
31
31
|
|
32
|
+
### Puppet Comply Report Generation
|
33
|
+
|
34
|
+
* Allows you ot programatically generate compliance reports from Puppet Comply
|
35
|
+
|
32
36
|
### Supports Configuration via Local YAML file
|
33
37
|
|
34
38
|
* Fully configurable via the `~/.abide_dev.yaml` file
|
data/abide_dev_utils.gemspec
CHANGED
@@ -37,6 +37,7 @@ Gem::Specification.new do |spec|
|
|
37
37
|
spec.add_dependency 'puppet', '>= 6.19'
|
38
38
|
spec.add_dependency 'jira-ruby', '~> 2.1'
|
39
39
|
spec.add_dependency 'ruby-progressbar', '~> 1.11'
|
40
|
+
spec.add_dependency 'selenium-webdriver', '~> 4.0.0.beta4'
|
40
41
|
|
41
42
|
# Dev dependencies
|
42
43
|
spec.add_development_dependency 'bundler'
|
data/lib/abide_dev_utils/cli.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require 'cmdparse'
|
4
4
|
require 'abide_dev_utils/version'
|
5
5
|
require 'abide_dev_utils/constants'
|
6
|
+
require 'abide_dev_utils/cli/comply'
|
6
7
|
require 'abide_dev_utils/cli/puppet'
|
7
8
|
require 'abide_dev_utils/cli/xccdf'
|
8
9
|
require 'abide_dev_utils/cli/test'
|
@@ -21,6 +22,7 @@ module Abide
|
|
21
22
|
parser.main_options.banner = ROOT_CMD_BANNER
|
22
23
|
parser.add_command(CmdParse::HelpCommand.new, default: true)
|
23
24
|
parser.add_command(CmdParse::VersionCommand.new(add_switches: true))
|
25
|
+
parser.add_command(ComplyCommand.new)
|
24
26
|
parser.add_command(PuppetCommand.new)
|
25
27
|
parser.add_command(XccdfCommand.new)
|
26
28
|
parser.add_command(TestCommand.new)
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'abide_dev_utils/comply'
|
4
|
+
require 'abide_dev_utils/cli/abstract'
|
5
|
+
|
6
|
+
module Abide
|
7
|
+
module CLI
|
8
|
+
class ComplyCommand < AbideCommand
|
9
|
+
CMD_NAME = 'comply'
|
10
|
+
CMD_SHORT = 'Commands related to Puppet Comply'
|
11
|
+
CMD_LONG = 'Namespace for commands related to Puppet Comply'
|
12
|
+
def initialize
|
13
|
+
super(CMD_NAME, CMD_SHORT, CMD_LONG, takes_commands: true)
|
14
|
+
add_command(ComplyReportCommand.new)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class ComplyReportCommand < AbideCommand
|
19
|
+
CMD_NAME = 'report'
|
20
|
+
CMD_SHORT = 'Generates a yaml report of Puppet Comply scan results'
|
21
|
+
CMD_LONG = <<~LONGCMD
|
22
|
+
Generates a yaml file that shows the scan results of all nodes in Puppet Comply.
|
23
|
+
This command utilizes Selenium WebDriver and the Google Chrome browser to automate
|
24
|
+
clicking through the Comply UI and building a report. In order to use this command,
|
25
|
+
you MUST have Google Chrome installed and you MUST install the chromedriver binary.
|
26
|
+
More info and instructions can be found here:
|
27
|
+
https://www.selenium.dev/documentation/en/getting_started_with_webdriver/.
|
28
|
+
LONGCMD
|
29
|
+
CMD_COMPLY_URL = 'The URL (including https://) of Puppet Comply'
|
30
|
+
CMD_COMPLY_PASSWORD = 'The password for Puppet Comply'
|
31
|
+
OPT_STATUS_DESC = <<~EODESC
|
32
|
+
A comma-separated list of check statuses to ONLY include in the report.
|
33
|
+
Valid statuses are: pass, fail, error, notapplicable, notchecked, unknown, informational
|
34
|
+
EODESC
|
35
|
+
OPT_IGNORE_NODES = <<~EOIGN
|
36
|
+
A comma-separated list of node certnames to ignore building reports for. This
|
37
|
+
options is mutually exclusive with --only and, if both are set, --only will take precedence
|
38
|
+
over this option.
|
39
|
+
EOIGN
|
40
|
+
OPT_ONLY_NODES = <<~EOONLY
|
41
|
+
A comma-separated list of node certnames to ONLY build reports for. No other
|
42
|
+
nodes will have reports built for them except the ones specified. This option
|
43
|
+
is mutually exclusive with --ignore and, if both are set, this options will
|
44
|
+
take precedence over --ignore.
|
45
|
+
EOONLY
|
46
|
+
def initialize
|
47
|
+
super(CMD_NAME, CMD_SHORT, CMD_LONG, takes_commands: false)
|
48
|
+
argument_desc(COMPLY_URL: CMD_COMPLY_URL, COMPLY_PASSWORD: CMD_COMPLY_PASSWORD)
|
49
|
+
options.on('-o [FILE]', '--out-file [FILE]', 'Path to save the report') { |f| @data[:file] = f }
|
50
|
+
options.on('-u [USERNAME]', '--username [USERNAME]', 'The username for Comply (defaults to comply)') do |u|
|
51
|
+
@data[:username] = u
|
52
|
+
end
|
53
|
+
options.on('-s [STATUS]', '--status [STATUS]', OPT_STATUS_DESC) do |s|
|
54
|
+
status_array = s.nil? ? nil : s.split(',').map(&:downcase)
|
55
|
+
status_array&.map! { |i| i == 'notchecked' ? 'not checked' : i }
|
56
|
+
@data[:status] = status_array
|
57
|
+
end
|
58
|
+
options.on('-O [CERTNAME]', '--only [CERTNAME]', OPT_ONLY_NODES) do |o|
|
59
|
+
only_array = o.nil? ? nil : s.split(',').map(&:downcase)
|
60
|
+
@data[:only] = only_array
|
61
|
+
end
|
62
|
+
options.on('-I [CERTNAME]', '--ignore [CERTNAME]', OPT_IGNORE_NODES) do |i|
|
63
|
+
ignore_array = i.nil? ? nil : i.split(',').map(&:downcase)
|
64
|
+
@data[:ignore] = ignore_array
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def help_arguments
|
69
|
+
<<~ARGHELP
|
70
|
+
Arguments:
|
71
|
+
COMPLY_URL #{CMD_COMPLY_URL}
|
72
|
+
COMPLY_PASSWORD #{CMD_COMPLY_PASSWORD}
|
73
|
+
|
74
|
+
ARGHELP
|
75
|
+
end
|
76
|
+
|
77
|
+
def execute(comply_url = nil, comply_password = nil)
|
78
|
+
Abide::CLI::VALIDATE.filesystem_path(`command -v chromedriver`.strip)
|
79
|
+
conf = config_section('comply')
|
80
|
+
comply_url = conf.fetch(:url) if comply_url.nil?
|
81
|
+
comply_password = comply_password.nil? ? conf.fetch(:password, Abide::CLI::PROMPT.password) : comply_password
|
82
|
+
username = @data.fetch(:username, nil).nil? ? conf.fetch(:username, 'comply') : @data[:username]
|
83
|
+
status = @data.fetch(:status, nil).nil? ? conf.fecth(:status, nil) : @data[:status]
|
84
|
+
ignorelist = @data.fetch(:ignore, nil).nil? ? conf.fetch(:ignore, nil) : @data[:ignore]
|
85
|
+
onlylist = @data.fetch(:only, nil).nil? ? conf.fetch(:only, nil) : @data[:only]
|
86
|
+
report = AbideDevUtils::Comply.scan_report(comply_url,
|
87
|
+
comply_password,
|
88
|
+
username: username,
|
89
|
+
status: status,
|
90
|
+
ignorelist: ignorelist,
|
91
|
+
onlylist: onlylist)
|
92
|
+
outfile = @data.fetch(:file, nil).nil? ? conf.fetch(:report_path, 'comply_scan_report.yaml') : @data[:file]
|
93
|
+
Abide::CLI::OUTPUT.yaml(report, file: outfile)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -59,8 +59,8 @@ module Abide
|
|
59
59
|
def execute(issue)
|
60
60
|
client = JIRA.client(options: {})
|
61
61
|
issue = client.Issue.find(issue)
|
62
|
-
console = @data[:file].nil?
|
63
|
-
out_json = issue.attrs.select { |_,v| !v.nil? || !v.empty? }
|
62
|
+
console = @data[:file].nil?
|
63
|
+
out_json = issue.attrs.select { |_, v| !v.nil? || !v.empty? }
|
64
64
|
Abide::CLI::OUTPUT.json(out_json, console: console, file: @data[:file])
|
65
65
|
end
|
66
66
|
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
require 'selenium-webdriver'
|
5
|
+
require 'abide_dev_utils/output'
|
6
|
+
|
7
|
+
module AbideDevUtils
|
8
|
+
module Comply
|
9
|
+
def self.scan_report(url, password, username: 'comply', status: nil, ignorelist: nil, onlylist: nil)
|
10
|
+
begin
|
11
|
+
AbideDevUtils::Output.simple 'Starting headless Chrome...'
|
12
|
+
options = Selenium::WebDriver::Chrome::Options.new
|
13
|
+
options.args = %w[
|
14
|
+
--headless
|
15
|
+
--test-type
|
16
|
+
--disable-gpu
|
17
|
+
--no-first-run
|
18
|
+
--no-default-browser-check
|
19
|
+
--ignore-certificate-errors
|
20
|
+
--start-maximized
|
21
|
+
]
|
22
|
+
driver = Selenium::WebDriver.for :chrome, options: options
|
23
|
+
driver.get(url)
|
24
|
+
bypass_ssl_warning_page(driver)
|
25
|
+
AbideDevUtils::Output.simple "Logging into Comply at #{url}..."
|
26
|
+
login_to_comply(driver, username: username, password: password)
|
27
|
+
AbideDevUtils::Output.simple 'Finding nodes with scan reports...'
|
28
|
+
links = find_node_report_links(driver)
|
29
|
+
AbideDevUtils::Output.simple 'Building scan reports, this may take a while...'
|
30
|
+
build_report(driver, links, status: status, ignorelist: ignorelist, onlylist: onlylist)
|
31
|
+
ensure
|
32
|
+
driver.quit
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.ignore_no_such_element
|
37
|
+
begin
|
38
|
+
yield
|
39
|
+
rescue Selenium::WebDriver::Error::NoSuchElementError => e
|
40
|
+
AbideDevUtils::Output.simple "Ignored exception #{e}", stream: $stderr
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.wait_on(timeout = 10)
|
45
|
+
Selenium::WebDriver::Wait.new(timeout: timeout).until do
|
46
|
+
yield
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.bypass_ssl_warning_page(driver)
|
51
|
+
ignore_no_such_element do
|
52
|
+
driver.find_element(id: 'details-button').click
|
53
|
+
driver.find_element(id: 'proceed-link').click
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.login_to_comply(driver, username: 'comply', password: 'compliance')
|
58
|
+
wait_on { driver.find_element(id: 'username') }
|
59
|
+
driver.find_element(id: 'username').send_keys username
|
60
|
+
driver.find_element(id: 'password').send_keys password
|
61
|
+
driver.find_element(id: 'kc-login').click
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.find_node_report_links(driver)
|
65
|
+
wait_on { driver.find_element(class: 'metric-containers-failed-hosts-count') }
|
66
|
+
hosts = driver.find_element(class: 'metric-containers-failed-hosts-count')
|
67
|
+
table = hosts.find_element(class: 'rc-table')
|
68
|
+
table_body = table.find_element(tag_name: 'tbody')
|
69
|
+
wait_on { table_body.find_element(tag_name: 'a') }
|
70
|
+
table_body.find_elements(tag_name: 'a')
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.build_report(driver, links, status: nil, ignorelist: nil, onlylist: nil)
|
74
|
+
all_checks = {}
|
75
|
+
original_window = driver.window_handle
|
76
|
+
links.each do |link|
|
77
|
+
if !onlylist.nil? && !onlylist.empty?
|
78
|
+
next unless onlylist.include?(link.text)
|
79
|
+
elsif !ignorelist.nil? && !ignorelist.empty?
|
80
|
+
next if ignorelist.include?(link.text)
|
81
|
+
end
|
82
|
+
begin
|
83
|
+
node_name = link.text
|
84
|
+
progress = AbideDevUtils::Output.progress title: "Builingd report for #{node_name}", total: nil
|
85
|
+
link_url = link.attribute('href')
|
86
|
+
driver.manage.new_window(:tab)
|
87
|
+
wait_on { driver.window_handles.length == 2 }
|
88
|
+
progress.increment
|
89
|
+
driver.switch_to.window driver.window_handles[1]
|
90
|
+
driver.get(link_url)
|
91
|
+
wait_on { driver.find_element(class: 'details-scan-info') }
|
92
|
+
progress.increment
|
93
|
+
wait_on { driver.find_element(class: 'details-table') }
|
94
|
+
progress.increment
|
95
|
+
report = {}
|
96
|
+
report['scan_results'] = {}
|
97
|
+
scan_info_table = driver.find_element(class: 'details-scan-info')
|
98
|
+
scan_info_table_rows = scan_info_table.find_elements(tag_name: 'tr')
|
99
|
+
progress.increment
|
100
|
+
check_table_body = driver.find_element(tag_name: 'tbody')
|
101
|
+
check_table_rows = check_table_body.find_elements(tag_name: 'tr')
|
102
|
+
progress.increment
|
103
|
+
scan_info_table_rows.each do |row|
|
104
|
+
key = row.find_element(tag_name: 'h5').text
|
105
|
+
value = row.find_element(tag_name: 'strong').text
|
106
|
+
report[key.downcase.gsub(/:/, '').gsub(/ /, '_')] = value
|
107
|
+
progress.increment
|
108
|
+
end
|
109
|
+
check_table_rows.each do |row|
|
110
|
+
chk_objs = row.find_elements(tag_name: 'td')
|
111
|
+
chk_objs.map!(&:text)
|
112
|
+
if status.nil? || status.include?(chk_objs[1].downcase)
|
113
|
+
report['scan_results'][chk_objs[0][/^[0-9.]+/, 0]] = {
|
114
|
+
'name' => chk_objs[0].gsub(/\n/, ' '),
|
115
|
+
'status' => chk_objs[1]
|
116
|
+
}
|
117
|
+
end
|
118
|
+
progress.increment
|
119
|
+
end
|
120
|
+
all_checks[node_name] = report
|
121
|
+
driver.close
|
122
|
+
AbideDevUtils::Output.simple "Created report for #{node_name}"
|
123
|
+
ensure
|
124
|
+
driver.switch_to.window original_window
|
125
|
+
end
|
126
|
+
end
|
127
|
+
all_checks
|
128
|
+
end
|
129
|
+
end
|
130
|
+
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
|
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' => {
|
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.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Heston Snodgrass
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-06-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|
@@ -80,6 +80,20 @@ dependencies:
|
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '1.11'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: selenium-webdriver
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 4.0.0.beta4
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 4.0.0.beta4
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
98
|
name: bundler
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -275,10 +289,12 @@ files:
|
|
275
289
|
- lib/abide_dev_utils.rb
|
276
290
|
- lib/abide_dev_utils/cli.rb
|
277
291
|
- lib/abide_dev_utils/cli/abstract.rb
|
292
|
+
- lib/abide_dev_utils/cli/comply.rb
|
278
293
|
- lib/abide_dev_utils/cli/jira.rb
|
279
294
|
- lib/abide_dev_utils/cli/puppet.rb
|
280
295
|
- lib/abide_dev_utils/cli/test.rb
|
281
296
|
- lib/abide_dev_utils/cli/xccdf.rb
|
297
|
+
- lib/abide_dev_utils/comply.rb
|
282
298
|
- lib/abide_dev_utils/config.rb
|
283
299
|
- lib/abide_dev_utils/constants.rb
|
284
300
|
- lib/abide_dev_utils/errors.rb
|