abide_dev_utils 0.4.2 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|