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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3978f655b9053e54d4fd63d456d3d65b9079c7684c6fcc88a3f764ead1aa012a
|
4
|
+
data.tar.gz: 746265daf86c8095a0f4ddbf9909d85a30ba2495b2b449e19896584c6e88da2b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a6bbb36782aff2d06e4fd55ea66582cf64c4414c2e13f1c3cca76c8b1c33c420f76474b058c2e2ce49bb294efc01b677588957fa8f745d3b2fa022683253281f
|
7
|
+
data.tar.gz: 78f92a917f547ba04f17610e0bca9cc5816efaadf7b066645cc75e40c303173a9b0d3e27ea0d7a741219f88c5e479810e14f180fc813cd5a31e3d6f0ee4bf65e
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
data/CODEOWNERS
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
* @puppetlabs/abide-team
|
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
|
@@ -84,6 +88,8 @@ Install the gem:
|
|
84
88
|
|
85
89
|
### Overview of Commands
|
86
90
|
|
91
|
+
* `abide comply` - Command namespace for Puppet Comply commands
|
92
|
+
* `abide comply report` - Creates a scan report in YAML format by scraping Puppet Comply
|
87
93
|
* `abide jira` - Command namespace for Jira commands
|
88
94
|
* `abide jira auth` - Authenticate with Jira. Only useful as a stand-alone command to test authentication
|
89
95
|
* `abide jira from_coverage` - Creates a parent issue with subtasks from a Puppet coverage report
|
@@ -96,6 +102,34 @@ Install the gem:
|
|
96
102
|
* `abide xccdf` - Command namespace for XCCDF commands
|
97
103
|
* `abide xccdf to_hiera` - Converts a benchmark XCCDF file to a Hiera yaml file
|
98
104
|
|
105
|
+
### Comply Command Reference
|
106
|
+
|
107
|
+
#### report
|
108
|
+
|
109
|
+
* Required positional parameters:
|
110
|
+
* `COMPLY_URL` - The URL of Puppet Comply
|
111
|
+
* `COMPLY_PASSWORD` - The password for the Puppet Comply user
|
112
|
+
* Options:
|
113
|
+
* `--out-file`, `-o` - The path to save the scan report. Defaults to `./comply_scan_report.yaml`
|
114
|
+
* `--username`, `-u` - The Puppet Comply username. Defaults to `comply`
|
115
|
+
* `--status`, `-s` - A comma-separated list of check statuses to ONLY include in the report. Valid statuses are: `pass`, `fail`, `error`, `notapplicable`, `notchecked`, `unknown`, `informational`
|
116
|
+
* `--only`, `-O` - A comma-separated list of node certnames to ONLY build reports for. No other nodes will have reports built for them except the ones specified. This option is mutually exclusive with `--ignore` and, if both are set, this options will take precedence over `--ignore`.
|
117
|
+
* `--ignore`, `-I` - A comma-separated list of node certnames to ignore building reports for. This options is mutually exclusive with `--only` and, if both are set, `--only` will take precedence over this option.
|
118
|
+
|
119
|
+
Examples:
|
120
|
+
|
121
|
+
Generating a report of all failed and err'd scan checks
|
122
|
+
|
123
|
+
```sh
|
124
|
+
abide comply report https://comply.my.instance 'my_comply_password!' -s fail,error
|
125
|
+
```
|
126
|
+
|
127
|
+
Generating a report for certain nodes only
|
128
|
+
|
129
|
+
```sh
|
130
|
+
abide comply report https://comply.my.instance 'my_comply_password!' -O specific-node.my.instance
|
131
|
+
```
|
132
|
+
|
99
133
|
### Jira Command Reference
|
100
134
|
|
101
135
|
#### from_coverage
|
data/abide_dev_utils.gemspec
CHANGED
@@ -7,14 +7,14 @@ require "abide_dev_utils/version"
|
|
7
7
|
Gem::Specification.new do |spec|
|
8
8
|
spec.name = "abide_dev_utils"
|
9
9
|
spec.version = AbideDevUtils::VERSION
|
10
|
-
spec.authors = ["
|
11
|
-
spec.email = ["
|
10
|
+
spec.authors = ["abide-team"]
|
11
|
+
spec.email = ["abide-team@puppet.com"]
|
12
12
|
|
13
|
-
spec.summary = "Helper utilities for developing
|
14
|
-
spec.description = "Provides a CLI with helpful utilities for developing
|
15
|
-
spec.homepage = "https://github.com/
|
13
|
+
spec.summary = "Helper utilities for developing compliance Puppet code"
|
14
|
+
spec.description = "Provides a CLI with helpful utilities for developing compliance Puppet code"
|
15
|
+
spec.homepage = "https://github.com/puppetlabs/abide_dev_utils"
|
16
16
|
spec.license = "MIT"
|
17
|
-
spec.required_ruby_version = Gem::Requirement.new(">= 2.
|
17
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.7.0")
|
18
18
|
|
19
19
|
# spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
|
20
20
|
|
@@ -34,9 +34,12 @@ Gem::Specification.new do |spec|
|
|
34
34
|
# Prod dependencies
|
35
35
|
spec.add_dependency 'nokogiri', '~> 1.11'
|
36
36
|
spec.add_dependency 'cmdparse', '~> 3.0'
|
37
|
-
spec.add_dependency 'puppet', '>= 6.
|
37
|
+
spec.add_dependency 'puppet', '>= 6.23'
|
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'
|
41
|
+
spec.add_dependency 'google-cloud-storage', '~> 1.34'
|
42
|
+
spec.add_dependency 'hashdiff', '~> 1.0'
|
40
43
|
|
41
44
|
# Dev dependencies
|
42
45
|
spec.add_development_dependency 'bundler'
|
@@ -44,6 +47,7 @@ Gem::Specification.new do |spec|
|
|
44
47
|
spec.add_development_dependency 'console'
|
45
48
|
spec.add_development_dependency 'github_changelog_generator'
|
46
49
|
spec.add_development_dependency 'gem-release'
|
50
|
+
spec.add_development_dependency 'pry'
|
47
51
|
spec.add_development_dependency 'rspec', '~> 3.10'
|
48
52
|
spec.add_development_dependency 'rubocop', '~> 1.8'
|
49
53
|
spec.add_development_dependency 'rubocop-rspec', '~> 2.1'
|
data/itests.rb
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'json'
|
5
|
+
require 'yaml'
|
6
|
+
require 'abide_dev_utils/comply'
|
7
|
+
require 'abide_dev_utils/ppt/api'
|
8
|
+
|
9
|
+
OS_BENCHMARK_MAP = {
|
10
|
+
'centos-7' => 'CIS_CentOS_Linux_7_Benchmark_v3.1.1-xccdf.xml',
|
11
|
+
'centos-8' => 'CIS_CentOS_Linux_8_Benchmark_v1.0.1-xccdf.xml',
|
12
|
+
'rhel-7' => 'CIS_Red_Hat_Enterprise_Linux_7_Benchmark_v3.1.1-xccdf.xml',
|
13
|
+
'rhel-8' => 'CIS_Red_Hat_Enterprise_Linux_8_Benchmark_v1.0.1-xccdf.xml',
|
14
|
+
'serv-2016' => 'CIS_Microsoft_Windows_Server_2016_RTM_(Release_1607)_Benchmark_v1.3.0-xccdf.xml',
|
15
|
+
'serv-2019' => 'CIS_Microsoft_Windows_Server_2019_Benchmark_v1.2.1-xccdf.xml'
|
16
|
+
}
|
17
|
+
EL_PROFILE_1_SERVER = 'xccdf_org.cisecurity.benchmarks_profile_Level_1_-_Server'
|
18
|
+
WIN_PROFILE_1_MS = 'xccdf_org.cisecurity.benchmarks_profile_Level_1_-_Member_Server'
|
19
|
+
NIX_SCAN_HASH = {
|
20
|
+
'nix-centos-7.c.team-sse.internal' => {
|
21
|
+
'benchmark' => OS_BENCHMARK_MAP['centos-7'],
|
22
|
+
'profile' => EL_PROFILE_1_SERVER
|
23
|
+
},
|
24
|
+
'nix-centos-8.c.team-sse.internal' => {
|
25
|
+
'benchmark' => OS_BENCHMARK_MAP['centos-8'],
|
26
|
+
'profile' => EL_PROFILE_1_SERVER
|
27
|
+
},
|
28
|
+
'nix-rhel-7.c.team-sse.internal' => {
|
29
|
+
'benchmark' => OS_BENCHMARK_MAP['rhel-7'],
|
30
|
+
'profile' => EL_PROFILE_1_SERVER
|
31
|
+
},
|
32
|
+
'nix-rhel-8.c.team-sse.internal' => {
|
33
|
+
'benchmark' => OS_BENCHMARK_MAP['rhel-8'],
|
34
|
+
'profile' => EL_PROFILE_1_SERVER
|
35
|
+
}
|
36
|
+
}.freeze
|
37
|
+
WIN_SCAN_HASH = {
|
38
|
+
'win-server-2016.c.team-sse.internal' => {
|
39
|
+
'benchmark' => OS_BENCHMARK_MAP['serv-2016'],
|
40
|
+
'profile' => EL_PROFILE_1_SERVER
|
41
|
+
},
|
42
|
+
'win-serv-2019.c.team-sse.internal' => {
|
43
|
+
'benchmark' => OS_BENCHMARK_MAP['serv-2019'],
|
44
|
+
'profile' => WIN_PROFILE_1_MS
|
45
|
+
}
|
46
|
+
}.freeze
|
47
|
+
|
48
|
+
scan_hash = ENV['ABIDE_OS'] == 'nix' ? NIX_SCAN_HASH : WIN_SCAN_HASH
|
49
|
+
node_group_name = ENV['ABIDE_OS'] == 'nix' ? 'CEM Linux Nodes' : 'CEM Windows Nodes'
|
50
|
+
|
51
|
+
puts 'Creating client...'
|
52
|
+
client = AbideDevUtils::Ppt::ApiClient.new(ENV['PUPPET_HOST'], auth_token: ENV['PE_ACCESS_TOKEN'])
|
53
|
+
puts 'Starting code deploy...'
|
54
|
+
code_manager_deploy = client.post_codemanager_deploys('environments' => ['production'], 'wait' => true)
|
55
|
+
raise 'Code manager deployment failed!' unless code_manager_deploy['status'] == 'complete'
|
56
|
+
|
57
|
+
puts 'Code deploy successful...'
|
58
|
+
puts 'Gathering node group ID...'
|
59
|
+
node_groups = client.get_classifier1_groups
|
60
|
+
node_group_id = nil
|
61
|
+
node_groups.each { |x| node_group_id = x['id'] if x['name'] == node_group_name }
|
62
|
+
raise 'Failed to find requested node group!' if node_group_id.nil?
|
63
|
+
|
64
|
+
puts 'Running Puppet on nodes...'
|
65
|
+
puppet_run = client.post_orchestrator_command_deploy('environment' => 'production', 'scope' => { 'node_group' => node_group_id })
|
66
|
+
puts "Started job #{puppet_run['job']['name']}..."
|
67
|
+
timeout = 0
|
68
|
+
run_complete = false
|
69
|
+
until run_complete || timeout >= 30
|
70
|
+
puts "Waiting on job #{puppet_run['job']['name']} to complete..."
|
71
|
+
status = client.get_orchestrator_jobs(puppet_run['job']['name'])
|
72
|
+
case status['state']
|
73
|
+
when 'failed'
|
74
|
+
raise "Job #{puppet_run['job']['name']} finished with failures!"
|
75
|
+
when 'finished'
|
76
|
+
run_complete = true
|
77
|
+
break
|
78
|
+
else
|
79
|
+
timeout += 1
|
80
|
+
sleep(10)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
raise 'Job timed out waiting for completion' unless run_complete
|
84
|
+
|
85
|
+
puts 'Starting node scans...'
|
86
|
+
scan_job = client.post_orchestrator_command_task(
|
87
|
+
'environment' => 'production',
|
88
|
+
'task' => 'comply::ciscat_scan',
|
89
|
+
'params' => {
|
90
|
+
'comply_port' => '443',
|
91
|
+
'comply_server' => ENV['COMPLY_FQDN'],
|
92
|
+
'ssl_verify_mode' => 'none',
|
93
|
+
'scan_type' => 'desired',
|
94
|
+
'scan_hash' => JSON.generate(scan_hash)
|
95
|
+
},
|
96
|
+
'scope' => {
|
97
|
+
'node_group' => node_group_id
|
98
|
+
}
|
99
|
+
)
|
100
|
+
puts "Started scan #{scan_job['job']['name']}..."
|
101
|
+
timeout = 0
|
102
|
+
scan_complete = false
|
103
|
+
until scan_complete || timeout >= 30
|
104
|
+
puts "Waiting on scan #{scan_job['job']['name']} to complete..."
|
105
|
+
status = client.get_orchestrator_jobs(scan_job['job']['name'])
|
106
|
+
case status['state']
|
107
|
+
when 'failed'
|
108
|
+
raise "Task #{scan_job['job']['name']} finished with failures!"
|
109
|
+
when 'finished'
|
110
|
+
scan_complete = true
|
111
|
+
break
|
112
|
+
else
|
113
|
+
timeout += 1
|
114
|
+
sleep(10)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
raise 'Job timed out waiting for completion' unless scan_complete
|
118
|
+
|
119
|
+
puts 'Collecting scan report from Comply...'
|
120
|
+
onlylist = scan_hash.keys
|
121
|
+
scan_report = AbideDevUtils::Comply.build_report("https://#{ENV['COMPLY_FQDN']}", ENV['COMPLY_PASSWORD'], nil, onlylist: onlylist)
|
122
|
+
puts 'Saving report to nix_report.yaml...'
|
123
|
+
File.open('nix_report.yaml', 'w') { |f| f.write(scan_report.to_yaml) }
|
124
|
+
|
125
|
+
puts 'Comparing current report to last report...'
|
126
|
+
opts = {
|
127
|
+
report_name: 'nix_report.yaml',
|
128
|
+
remote_storage: 'gcloud',
|
129
|
+
upload: true
|
130
|
+
}
|
131
|
+
result = AbideDevUtils::Comply.compare_reports(File.expand_path('./nix_report.yaml'), 'nix_report.yaml', opts)
|
132
|
+
if result
|
133
|
+
puts 'Success!'
|
134
|
+
exit(0)
|
135
|
+
else
|
136
|
+
puts 'Failure!'
|
137
|
+
exit(1)
|
138
|
+
end
|
@@ -0,0 +1,115 @@
|
|
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
|
+
add_command(ComplyCompareReportCommand.new)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class ComplyReportCommand < AbideCommand
|
20
|
+
CMD_NAME = 'report'
|
21
|
+
CMD_SHORT = 'Generates a yaml report of Puppet Comply scan results'
|
22
|
+
CMD_LONG = <<~LONGCMD
|
23
|
+
Generates a yaml file that shows the scan results of all nodes in Puppet Comply.
|
24
|
+
This command utilizes Selenium WebDriver and the Google Chrome browser to automate
|
25
|
+
clicking through the Comply UI and building a report. In order to use this command,
|
26
|
+
you MUST have Google Chrome installed and you MUST install the chromedriver binary.
|
27
|
+
More info and instructions can be found here:
|
28
|
+
https://www.selenium.dev/documentation/en/getting_started_with_webdriver/.
|
29
|
+
LONGCMD
|
30
|
+
CMD_COMPLY_URL = 'The URL (including https://) of Puppet Comply'
|
31
|
+
CMD_COMPLY_PASSWORD = 'The password for Puppet Comply'
|
32
|
+
OPT_TIMEOUT_DESC = <<~EOTO
|
33
|
+
The number of seconds you would like requests to wait before timing out. Defaults
|
34
|
+
to 10 seconds.
|
35
|
+
EOTO
|
36
|
+
OPT_STATUS_DESC = <<~EODESC
|
37
|
+
A comma-separated list of check statuses to ONLY include in the report.
|
38
|
+
Valid statuses are: pass, fail, error, notapplicable, notchecked, unknown, informational
|
39
|
+
EODESC
|
40
|
+
OPT_IGNORE_NODES = <<~EOIGN
|
41
|
+
A comma-separated list of node certnames to ignore building reports for. This
|
42
|
+
options is mutually exclusive with --only and, if both are set, --only will take precedence
|
43
|
+
over this option.
|
44
|
+
EOIGN
|
45
|
+
OPT_ONLY_NODES = <<~EOONLY
|
46
|
+
A comma-separated list of node certnames to ONLY build reports for. No other
|
47
|
+
nodes will have reports built for them except the ones specified. This option
|
48
|
+
is mutually exclusive with --ignore and, if both are set, this options will
|
49
|
+
take precedence over --ignore.
|
50
|
+
EOONLY
|
51
|
+
def initialize
|
52
|
+
super(CMD_NAME, CMD_SHORT, CMD_LONG, takes_commands: false)
|
53
|
+
argument_desc(COMPLY_URL: CMD_COMPLY_URL, COMPLY_PASSWORD: CMD_COMPLY_PASSWORD)
|
54
|
+
options.on('-o [FILE]', '--out-file [FILE]', 'Path to save the report') { |f| @data[:file] = f }
|
55
|
+
options.on('-u [USERNAME]', '--username [USERNAME]', 'The username for Comply (defaults to comply)') do |u|
|
56
|
+
@data[:username] = u
|
57
|
+
end
|
58
|
+
options.on('-t [SECONDS]', '--timeout [SECONDS]', OPT_TIMEOUT_DESC) do |t|
|
59
|
+
@data[:timeout] = t
|
60
|
+
end
|
61
|
+
options.on('-s [X,Y,Z]', '--status [X,Y,Z]',
|
62
|
+
%w[pass fail error notapplicable notchecked unknown informational],
|
63
|
+
Array,
|
64
|
+
OPT_STATUS_DESC) do |s|
|
65
|
+
s&.map! { |i| i == 'notchecked' ? 'not checked' : i }
|
66
|
+
@data[:status] = s
|
67
|
+
end
|
68
|
+
options.on('--only [X,Y,Z]', Array, OPT_ONLY_NODES) do |o|
|
69
|
+
@data[:onlylist] = o
|
70
|
+
end
|
71
|
+
options.on('--ignore [X,Y,Z]', Array, OPT_IGNORE_NODES) do |i|
|
72
|
+
@data[:ignorelist] = i
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def help_arguments
|
77
|
+
<<~ARGHELP
|
78
|
+
Arguments:
|
79
|
+
COMPLY_URL #{CMD_COMPLY_URL}
|
80
|
+
COMPLY_PASSWORD #{CMD_COMPLY_PASSWORD}
|
81
|
+
|
82
|
+
ARGHELP
|
83
|
+
end
|
84
|
+
|
85
|
+
def execute(comply_url = nil, comply_password = nil)
|
86
|
+
Abide::CLI::VALIDATE.filesystem_path(`command -v chromedriver`.strip)
|
87
|
+
conf = config_section('comply')
|
88
|
+
comply_url = conf.fetch(:url) if comply_url.nil?
|
89
|
+
comply_password = comply_password.nil? ? conf.fetch(:password, Abide::CLI::PROMPT.password) : comply_password
|
90
|
+
report = AbideDevUtils::Comply.build_report(comply_url, comply_password, conf, **@data)
|
91
|
+
outfile = @data.fetch(:file, nil).nil? ? conf.fetch(:report_path, 'comply_scan_report.yaml') : @data[:file]
|
92
|
+
Abide::CLI::OUTPUT.yaml(report, file: outfile)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class ComplyCompareReportCommand < AbideCommand
|
97
|
+
CMD_NAME = 'compare-report'
|
98
|
+
CMD_SHORT = 'Compare two Comply reports and get the differences.'
|
99
|
+
CMD_LONG = 'Compare two Comply reports and get the differences. Report A is compared to report B, showing what changes it would take for A to equal B.'
|
100
|
+
CMD_REPORT_A = 'The current Comply report yaml file'
|
101
|
+
CMD_REPORT_B = 'The old Comply report yaml file name or full path'
|
102
|
+
def initialize
|
103
|
+
super(CMD_NAME, CMD_SHORT, CMD_LONG, takes_commands: false)
|
104
|
+
argument_desc(REPORT_A: CMD_REPORT_A, REPORT_B: CMD_REPORT_B)
|
105
|
+
options.on('-u', '--upload-new', 'If you want to upload the new scan report') { @data[:upload] = true }
|
106
|
+
options.on('-s [STORAGE]', '--remote-storage [STORAGE]', 'Remote storage to upload the report to. (Only supports "gcloud")') { |x| @data[:remote_storage] = x }
|
107
|
+
options.on('-r [NAME]', '--name [NAME]', 'The name to upload the report as') { |x| @data[:report_name] = x }
|
108
|
+
end
|
109
|
+
|
110
|
+
def execute(report_a, report_b)
|
111
|
+
AbideDevUtils::Comply.compare_reports(report_a, report_b, @data)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
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
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'abide_dev_utils/cli/abstract'
|
4
|
+
require 'abide_dev_utils/output'
|
5
|
+
require 'abide_dev_utils/ppt'
|
4
6
|
|
5
7
|
module Abide
|
6
8
|
module CLI
|
@@ -12,6 +14,10 @@ module Abide
|
|
12
14
|
super(CMD_NAME, CMD_SHORT, CMD_LONG, takes_commands: true)
|
13
15
|
add_command(PuppetCoverageCommand.new)
|
14
16
|
add_command(PuppetNewCommand.new)
|
17
|
+
add_command(PuppetRenameCommand.new)
|
18
|
+
add_command(PuppetFixClassNamesCommand.new)
|
19
|
+
add_command(PuppetAuditClassNamesCommand.new)
|
20
|
+
add_command(PuppetAddCISCommentCommand.new)
|
15
21
|
end
|
16
22
|
end
|
17
23
|
|
@@ -38,12 +44,12 @@ module Abide
|
|
38
44
|
end
|
39
45
|
|
40
46
|
def execute(class_dir, hiera_file)
|
41
|
-
require 'abide_dev_utils/ppt'
|
47
|
+
require 'abide_dev_utils/ppt/coverage'
|
42
48
|
Abide::CLI::VALIDATE.directory(class_dir)
|
43
49
|
Abide::CLI::VALIDATE.file(hiera_file)
|
44
|
-
coverage = AbideDevUtils::Ppt
|
50
|
+
coverage = AbideDevUtils::Ppt.generate_coverage_report(class_dir, hiera_file, @data[:profile])
|
45
51
|
coverage.each do |k, v|
|
46
|
-
next if
|
52
|
+
next if k.match?(/classes|benchmark/)
|
47
53
|
|
48
54
|
Abide::CLI::OUTPUT.simple("#{k} coverage: #{v[:coverage]}%")
|
49
55
|
end
|
@@ -100,14 +106,133 @@ module Abide
|
|
100
106
|
end
|
101
107
|
|
102
108
|
def execute(type, name)
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
109
|
+
AbideDevUtils::Ppt.build_new_object(type, name, @data)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class PuppetRenameCommand < AbideCommand
|
114
|
+
CMD_NAME = 'rename'
|
115
|
+
CMD_SHORT = 'Renames a Puppet class'
|
116
|
+
CMD_LONG = 'Renames a Puppet class. It does this by renaming the file and also the class name in the file. This command can also move class files based on the new class name.'
|
117
|
+
CMD_FROM_ARG = 'The current full class name'
|
118
|
+
CMD_TO_ARG = 'The new full class name'
|
119
|
+
def initialize
|
120
|
+
super(CMD_NAME, CMD_SHORT, CMD_LONG, takes_commands: false)
|
121
|
+
argument_desc(FROM: CMD_FROM_ARG, TO: CMD_TO_ARG)
|
122
|
+
options.on(
|
123
|
+
'-d',
|
124
|
+
'--declaration-only',
|
125
|
+
'Will not rename the class file, only the class declaration in the file'
|
126
|
+
) { @data[:declaration_only] = true }
|
127
|
+
options.on(
|
128
|
+
'-t',
|
129
|
+
'--declaration-in-to-file',
|
130
|
+
'Use the path derived from the TO class name as the existing file path when renaming class declaration'
|
131
|
+
) { @data[:declaration_in_to_file] = true }
|
132
|
+
options.on(
|
133
|
+
'-f',
|
134
|
+
'--force',
|
135
|
+
'Forces file move operations'
|
136
|
+
) { @data[:force] = true }
|
137
|
+
options.on(
|
138
|
+
'-v',
|
139
|
+
'--verbose',
|
140
|
+
'Sets verbose mode on file operations'
|
141
|
+
) { @data[:verbose] = true }
|
142
|
+
end
|
143
|
+
|
144
|
+
def execute(from, to)
|
145
|
+
AbideDevUtils::Ppt.rename_puppet_class(from, to, **@data)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
class PuppetFixClassNamesCommand < AbideCommand
|
150
|
+
CMD_NAME = 'fix-class-names'
|
151
|
+
CMD_SHORT = 'Fixes Puppet class names that are mismatched'
|
152
|
+
CMD_LONG = 'Fixes Puppet class names that are mismatched'
|
153
|
+
CMD_MODE_ARG = '"file" or "class". If "file", the file names will be changed to match their class declarations. If "class", the class declarations will be changed to match the file names.'
|
154
|
+
CMD_DIR_ARG = 'The directory containing the Puppet class files'
|
155
|
+
def initialize
|
156
|
+
super(CMD_NAME, CMD_SHORT, CMD_LONG, takes_commands: false)
|
157
|
+
argument_desc(MODE: CMD_MODE_ARG, DIR: CMD_DIR_ARG)
|
158
|
+
options.on(
|
159
|
+
'-f',
|
160
|
+
'--force',
|
161
|
+
'Forces file move operations'
|
162
|
+
) { @data[:force] = true }
|
163
|
+
options.on(
|
164
|
+
'-v',
|
165
|
+
'--verbose',
|
166
|
+
'Sets verbose mode on file operations'
|
167
|
+
) { @data[:verbose] = true }
|
168
|
+
end
|
169
|
+
|
170
|
+
def execute(mode, dir)
|
171
|
+
case mode
|
172
|
+
when /^f.*/
|
173
|
+
AbideDevUtils::Ppt.fix_class_names_file_rename(dir, **@data)
|
174
|
+
when /^c.*/
|
175
|
+
AbideDevUtils::Ppt.fix_class_names_class_rename(dir, **@data)
|
176
|
+
else
|
177
|
+
raise ::ArgumentError, "Invalid mode. Mode:#{mode}"
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
class PuppetAuditClassNamesCommand < AbideCommand
|
183
|
+
CMD_NAME = 'audit-class-names'
|
184
|
+
CMD_SHORT = 'Finds Puppet classes in a directory that have names that do not match their path'
|
185
|
+
CMD_LONG = 'Finds Puppet classes in a directory that have names that do not match their path. This is helpful because class names that do not match their path structure break Puppet autoloading.'
|
186
|
+
CMD_DIR_ARG = 'The directory containing the Puppet class files'
|
187
|
+
def initialize
|
188
|
+
super(CMD_NAME, CMD_SHORT, CMD_LONG, takes_commands: false)
|
189
|
+
argument_desc(DIR: CMD_DIR_ARG)
|
190
|
+
options.on('-o [FILE]', '--out-file [FILE]', 'Save results to a file') { |f| @data[:file] = f }
|
191
|
+
options.on('-q', '--quiet', 'Do not print results to console') { @data[:quiet] = true }
|
192
|
+
end
|
193
|
+
|
194
|
+
def execute(dir)
|
195
|
+
if @data.fetch(:quiet, false) && !@data.key?(:file)
|
196
|
+
AbideDevUtils::Output.simple('ERROR: Specifying --quiet without --out-file is useless.', stream: $stderr)
|
197
|
+
exit 1
|
198
|
+
end
|
199
|
+
|
200
|
+
AbideDevUtils::Ppt.audit_class_names(dir, **@data)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
class PuppetAddCISCommentCommand < AbideCommand
|
205
|
+
CMD_NAME = 'add-cis-comment'
|
206
|
+
CMD_SHORT = 'Adds the CIS recommendation name to the top of a .pp file'
|
207
|
+
CMD_LONG = 'Adds the CIS recommendation name to the top of a .pp file. Finds CIS recommendation by pattern-matching the class name against XCCDF recommendations.'
|
208
|
+
CMD_PATH_ARG = 'Path to a .pp file or to a directory containing .pp files'
|
209
|
+
CMD_XCCDF_ARG = 'Path to XCCDF file to source recommendation names from'
|
210
|
+
def initialize
|
211
|
+
super(CMD_NAME, CMD_SHORT, CMD_LONG, takes_commands: false)
|
212
|
+
argument_desc(PATH: CMD_PATH_ARG, XCCDF: CMD_XCCDF_ARG)
|
213
|
+
options.on('-N', '--number-format', 'Matches based on number-formatted control class names') { @data[:number_format] = true }
|
214
|
+
end
|
215
|
+
|
216
|
+
def execute(path, xccdf)
|
217
|
+
AbideDevUtils::Ppt.add_cis_comment(path, xccdf, number_format: @data.fetch(:number_format, false))
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
class PuppetScoreModuleCommand < AbideCommand
|
222
|
+
CMD_NAME = 'score'
|
223
|
+
CMD_SHORT = 'Scores a Puppet module just like Puppet Forge'
|
224
|
+
CMD_LONG = 'Scores a Puppet module just like Puppet Forge. This is a useful quality-check before publishing a module.'
|
225
|
+
def initialize
|
226
|
+
super(CMD_NAME, CMD_SHORT, CMD_LONG, takes_commands: false)
|
227
|
+
options.on('-o [PATH]', '--outfile [PATH]', 'Save results to a file') { |x| @data[:outfile] = x }
|
228
|
+
options.on('-q', '--quiet', FalseClass, 'Do not print results to console') { |x| @data[:quiet] = x }
|
229
|
+
options.on('-c', '--checks', Array, 'Comma-separated list of individual checks to run. Defaults to running all checks.') { |x| @data[:check] = x }
|
230
|
+
options.on('-m [PATH]', '--module [PATH]', 'Path to a Puppet module to score. Defaults to using the current directory.') { |x| @data[:module] = x }
|
231
|
+
end
|
232
|
+
|
233
|
+
def execute
|
234
|
+
module_path = @data.fetch(:module, nil)
|
235
|
+
AbideDevUtils::Ppt.score_module(module_path, **@data)
|
111
236
|
end
|
112
237
|
end
|
113
238
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'abide_dev_utils/cli/abstract'
|
3
4
|
require 'abide_dev_utils/xccdf'
|
4
5
|
|
5
6
|
module Abide
|
@@ -14,11 +15,12 @@ module Abide
|
|
14
15
|
long_desc(CMD_LONG)
|
15
16
|
add_command(CmdParse::HelpCommand.new, default: true)
|
16
17
|
add_command(XccdfToHieraCommand.new)
|
18
|
+
add_command(XccdfDiffCommand.new)
|
17
19
|
end
|
18
20
|
end
|
19
21
|
|
20
22
|
class XccdfToHieraCommand < CmdParse::Command
|
21
|
-
CMD_NAME = '
|
23
|
+
CMD_NAME = 'to-hiera'
|
22
24
|
CMD_SHORT = 'Generates control coverage report'
|
23
25
|
CMD_LONG = 'Generates report of valid Puppet classes that match with Hiera controls'
|
24
26
|
def initialize
|
@@ -37,15 +39,32 @@ module Abide
|
|
37
39
|
|
38
40
|
def execute(xccdf_file)
|
39
41
|
@data[:type] = 'cis' if @data[:type].nil?
|
40
|
-
|
41
|
-
|
42
|
+
hfile = AbideDevUtils::XCCDF.to_hiera(xccdf_file, @data)
|
43
|
+
AbideDevUtils::Output.yaml(hfile, console: @data[:file].nil?, file: @data[:file])
|
42
44
|
end
|
45
|
+
end
|
43
46
|
|
44
|
-
|
47
|
+
class XccdfDiffCommand < AbideCommand
|
48
|
+
CMD_NAME = 'diff'
|
49
|
+
CMD_SHORT = 'Generates a diff report between two XCCDF files'
|
50
|
+
CMD_LONG = 'Generates a diff report between two XCCDF files'
|
51
|
+
CMD_FILE1_ARG = 'path to first XCCDF file'
|
52
|
+
CMD_FILE2_ARG = 'path to second XCCDF file'
|
53
|
+
def initialize
|
54
|
+
super(CMD_NAME, CMD_SHORT, CMD_LONG, takes_commands: false)
|
55
|
+
argument_desc(FILE1: CMD_FILE1_ARG, FILE2: CMD_FILE2_ARG)
|
56
|
+
options.on('-o [PATH]', '--out-file', 'Save the report as a yaml file') { |x| @data[:outfile] = x }
|
57
|
+
options.on('-p [PROFILE]', '--profile', 'Only diff and specific profile in the benchmarks') do |x|
|
58
|
+
@data[:profile] = x
|
59
|
+
end
|
60
|
+
options.on('-q', '--quiet', 'Show no output in the terminal') { @data[:quiet] = false }
|
61
|
+
options.on('--no-diff-profiles', 'Do not diff the profiles in the XCCDF files') { @data[:diff_profiles] = false }
|
62
|
+
options.on('--no-diff-controls', 'Do not diff the controls in the XCCDF files') { @data[:diff_controls] = false }
|
63
|
+
end
|
45
64
|
|
46
|
-
def
|
47
|
-
|
48
|
-
|
65
|
+
def execute(file1, file2)
|
66
|
+
diffreport = AbideDevUtils::XCCDF.diff(file1, file2, @data)
|
67
|
+
AbideDevUtils::Output.yaml(diffreport, console: @data.fetch(:quiet, true), file: @data.fetch(:outfile, nil))
|
49
68
|
end
|
50
69
|
end
|
51
70
|
end
|
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)
|