inspec_tools 1.4.2 → 1.7.1
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/CHANGELOG.md +244 -36
- data/README.md +34 -41
- data/Rakefile +6 -0
- data/exe/inspec_tools +1 -1
- data/lib/data/NIST_Map_02052020_CIS_Controls_Version_7.1_Implementation_Groups_1.2.xlsx +0 -0
- data/lib/data/README.TXT +1 -1
- data/lib/data/attributes.yml +8 -7
- data/lib/happy_mapper_tools/stig_checklist.rb +0 -1
- data/lib/inspec_tools.rb +1 -0
- data/lib/inspec_tools/cli.rb +26 -199
- data/lib/inspec_tools/csv.rb +2 -4
- data/lib/inspec_tools/inspec.rb +2 -2
- data/lib/inspec_tools/pdf.rb +2 -3
- data/lib/inspec_tools/plugin.rb +15 -0
- data/lib/inspec_tools/plugin_cli.rb +275 -0
- data/lib/inspec_tools/summary.rb +1 -1
- data/lib/inspec_tools/version.rb +1 -1
- data/lib/inspec_tools/xccdf.rb +2 -3
- data/lib/inspec_tools/xlsx.rb +117 -0
- data/lib/inspec_tools_plugin.rb +7 -0
- data/lib/utilities/inspec_util.rb +93 -34
- metadata +56 -53
- data/lib/data/debug_text +0 -5941
- data/lib/inspec_tools/command.rb +0 -50
- data/test/unit/inspec_tools/csv_test.rb +0 -30
- data/test/unit/inspec_tools/inspec_test.rb +0 -54
- data/test/unit/inspec_tools/pdf_test.rb +0 -24
- data/test/unit/inspec_tools/summary_test.rb +0 -42
- data/test/unit/inspec_tools/xccdf_test.rb +0 -50
- data/test/unit/inspec_tools_test.rb +0 -7
- data/test/unit/test_helper.rb +0 -5
- data/test/unit/utils/inspec_util_test.rb +0 -44
data/Rakefile
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
2
|
require "rake/testtask"
|
3
|
+
require File.expand_path('../lib/inspec_tools/version', __FILE__)
|
3
4
|
|
4
5
|
Rake::TestTask.new(:test) do |t|
|
5
6
|
t.libs << "test"
|
@@ -21,4 +22,9 @@ namespace :test do
|
|
21
22
|
end
|
22
23
|
end
|
23
24
|
|
25
|
+
desc 'Build and publish the gem'
|
26
|
+
task publish: :build do
|
27
|
+
system("gem push pkg/inspec_tools-#{InspecTools::VERSION}.gem")
|
28
|
+
end
|
29
|
+
|
24
30
|
task :default => :test
|
data/exe/inspec_tools
CHANGED
Binary file
|
data/lib/data/README.TXT
CHANGED
@@ -18,7 +18,7 @@ singular, actionable statements that comprise an IA control or IA best practice.
|
|
18
18
|
- Stylesheet for viewing CCI List as HTML
|
19
19
|
(open U_CCI_List.xml in a web browser to view)
|
20
20
|
|
21
|
-
see: https://
|
21
|
+
see: https://public.cyber.mil/stigs/cci/
|
22
22
|
|
23
23
|
* NIST_Map_09212017B_CSC-CIS_Critical_Security_Controls_VER_6.1_Excel_9.1.2016.xlsx
|
24
24
|
|
data/lib/data/attributes.yml
CHANGED
@@ -1,23 +1,24 @@
|
|
1
1
|
---
|
2
2
|
benchmark.title: PostgreSQL 9.x Security Technical Implementation Guide
|
3
3
|
benchmark.id: PostgreSQL_9-x_STIG
|
4
|
-
benchmark.description:
|
4
|
+
benchmark.description:
|
5
|
+
"This Security Technical Implementation Guide is published
|
5
6
|
as a tool to improve the security of Department of Defense (DoD) information systems.
|
6
7
|
The requirements are derived from the National Institute of Standards and Technology
|
7
8
|
(NIST) 800-53 and related documents. Comments or proposed revisions to this document
|
8
|
-
should be sent via email to the following address: disa.stig_spt@mail.mil.
|
9
|
-
benchmark.version:
|
9
|
+
should be sent via email to the following address: disa.stig_spt@mail.mil."
|
10
|
+
benchmark.version: "1"
|
10
11
|
benchmark.status: accepted
|
11
|
-
benchmark.status.date:
|
12
|
+
benchmark.status.date: "2017-01-20"
|
12
13
|
benchmark.notice.id: terms-of-use
|
13
|
-
benchmark.plaintext:
|
14
|
+
benchmark.plaintext: "Release: 1 Benchmark Date: 20 Jan 2017"
|
14
15
|
benchmark.plaintext.id: release-info
|
15
|
-
reference.href:
|
16
|
+
reference.href: https://public.cyber.mil/
|
16
17
|
reference.dc.publisher: DISA
|
17
18
|
reference.dc.source: STIG.DOD.MIL
|
18
19
|
reference.dc.title: DPMS Target PostgreSQL 9.x
|
19
20
|
reference.dc.subject: PostgreSQL 9.x
|
20
21
|
reference.dc.type: DPMS Target
|
21
|
-
reference.dc.identifier:
|
22
|
+
reference.dc.identifier: "3087"
|
22
23
|
content_ref.name: M
|
23
24
|
content_ref.href: DPMS_XCCDF_Benchmark_PostgreSQL_9-x_STIG.xml
|
data/lib/inspec_tools.rb
CHANGED
data/lib/inspec_tools/cli.rb
CHANGED
@@ -1,207 +1,34 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
require 'json'
|
3
|
+
|
4
|
+
require 'inspec-objects'
|
5
|
+
require_relative 'version'
|
6
|
+
|
3
7
|
require_relative '../utilities/inspec_util'
|
4
8
|
require_relative '../utilities/csv_util'
|
9
|
+
require_relative './plugin_cli.rb'
|
5
10
|
|
6
|
-
#
|
7
|
-
|
11
|
+
# This tells the ruby cli app to use the same argument parsing as the plugin
|
8
12
|
module InspecTools
|
9
|
-
|
10
|
-
|
11
|
-
long_desc Help.text(:xccdf2inspec)
|
12
|
-
option :xccdf, required: true, aliases: '-x'
|
13
|
-
option :attributes, required: false, aliases: '-a'
|
14
|
-
option :output, required: false, aliases: '-o', default: 'profile'
|
15
|
-
option :format, required: false, aliases: '-f', enum: %w{ruby hash}, default: 'ruby'
|
16
|
-
option :separate_files, required: false, type: :boolean, default: true, aliases: '-s'
|
17
|
-
option :replace_tags, required: false, aliases: '-r'
|
18
|
-
option :metadata, required: false, aliases: '-m'
|
19
|
-
def xccdf2inspec
|
20
|
-
xccdf = XCCDF.new(File.read(options[:xccdf]), options[:replace_tags])
|
21
|
-
profile = xccdf.to_inspec
|
22
|
-
|
23
|
-
if !options[:metadata].nil?
|
24
|
-
xccdf.inject_metadata(File.read(options[:metadata]))
|
25
|
-
end
|
26
|
-
|
27
|
-
Utils::InspecUtil.unpack_inspec_json(options[:output], profile, options[:separate_files], options[:format])
|
28
|
-
if !options[:attributes].nil?
|
29
|
-
attributes = xccdf.to_attributes
|
30
|
-
File.write(options[:attributes], YAML.dump(attributes))
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
desc 'inspec2xccdf', 'inspec2xccdf translates an inspec profile and attributes files to an xccdf file'
|
35
|
-
long_desc Help.text(:inspec2xccdf)
|
36
|
-
option :inspec_json, required: true, aliases: '-j'
|
37
|
-
option :attributes, required: true, aliases: '-a'
|
38
|
-
option :output, required: true, aliases: '-o'
|
39
|
-
def inspec2xccdf
|
40
|
-
json = File.read(options[:inspec_json])
|
41
|
-
inspec_tool = InspecTools::Inspec.new(json)
|
42
|
-
attr_hsh = YAML.load_file(options[:attributes])
|
43
|
-
xccdf = inspec_tool.to_xccdf(attr_hsh)
|
44
|
-
File.write(options[:output], xccdf)
|
45
|
-
end
|
46
|
-
|
47
|
-
desc 'csv2inspec', 'csv2inspec translates CSV to Inspec controls using a mapping file'
|
48
|
-
long_desc Help.text(:csv2inspec)
|
49
|
-
option :csv, required: true, aliases: '-c'
|
50
|
-
option :mapping, required: true, aliases: '-m'
|
51
|
-
option :verbose, required: false, type: :boolean, aliases: '-V'
|
52
|
-
option :output, required: false, aliases: '-o', default: 'profile'
|
53
|
-
option :format, required: false, aliases: '-f', enum: %w{ruby hash}, default: 'ruby'
|
54
|
-
option :separate_files, required: false, type: :boolean, default: true, aliases: '-s'
|
55
|
-
def csv2inspec
|
56
|
-
csv = CSV.read(options[:csv], encoding: 'ISO8859-1')
|
57
|
-
mapping = YAML.load_file(options[:mapping])
|
58
|
-
profile = CSVTool.new(csv, mapping, options[:csv].split('/')[-1].split('.')[0], options[:verbose]).to_inspec
|
59
|
-
Utils::InspecUtil.unpack_inspec_json(options[:output], profile, options[:separate_files], options[:format])
|
60
|
-
end
|
61
|
-
|
62
|
-
desc 'inspec2csv', 'inspec2csv translates Inspec controls to CSV'
|
63
|
-
long_desc Help.text(:inspec2csv)
|
64
|
-
option :inspec_json, required: true, aliases: '-j'
|
65
|
-
option :output, required: true, aliases: '-o'
|
66
|
-
option :verbose, required: false, type: :boolean, aliases: '-V'
|
67
|
-
def inspec2csv
|
68
|
-
csv = Inspec.new(File.read(options[:inspec_json])).to_csv
|
69
|
-
Utils::CSVUtil.unpack_csv(csv, options[:output])
|
70
|
-
end
|
71
|
-
|
72
|
-
desc 'inspec2ckl', 'inspec2ckl translates an inspec json file to a Checklist file'
|
73
|
-
long_desc Help.text(:inspec2ckl)
|
74
|
-
option :inspec_json, required: true, aliases: '-j'
|
75
|
-
option :output, required: true, aliases: '-o'
|
76
|
-
option :verbose, type: :boolean, aliases: '-V'
|
77
|
-
option :metadata, required: false, aliases: '-m'
|
78
|
-
def inspec2ckl
|
79
|
-
metadata = '{}'
|
80
|
-
if !options[:metadata].nil?
|
81
|
-
metadata = File.read(options[:metadata])
|
82
|
-
end
|
83
|
-
ckl = InspecTools::Inspec.new(File.read(options[:inspec_json]), metadata).to_ckl
|
84
|
-
File.write(options[:output], ckl)
|
85
|
-
end
|
86
|
-
|
87
|
-
desc 'pdf2inspec', 'pdf2inspec translates a PDF Security Control Speficication to Inspec Security Profile'
|
88
|
-
long_desc Help.text(:pdf2inspec)
|
89
|
-
option :pdf, required: true, aliases: '-p'
|
90
|
-
option :output, required: false, aliases: '-o', default: 'profile'
|
91
|
-
option :debug, required: false, aliases: '-d', type: :boolean, default: false
|
92
|
-
option :format, required: false, aliases: '-f', enum: %w{ruby hash}, default: 'ruby'
|
93
|
-
option :separate_files, required: false, type: :boolean, default: true, aliases: '-s'
|
94
|
-
def pdf2inspec
|
95
|
-
pdf = File.open(options[:pdf])
|
96
|
-
profile = InspecTools::PDF.new(pdf, options[:output], options[:debug]).to_inspec
|
97
|
-
Utils::InspecUtil.unpack_inspec_json(options[:output], profile, options[:separate_files], options[:format])
|
98
|
-
end
|
99
|
-
|
100
|
-
desc 'generate_map', 'Generates mapping template from CSV to Inspec Controls'
|
101
|
-
def generate_map
|
102
|
-
template = '
|
103
|
-
# Setting csv_header to true will skip the csv file header
|
104
|
-
skip_csv_header: true
|
105
|
-
width : 80
|
106
|
-
|
107
|
-
|
108
|
-
control.id: 0
|
109
|
-
control.title: 15
|
110
|
-
control.desc: 16
|
111
|
-
control.tags:
|
112
|
-
severity: 1
|
113
|
-
rid: 8
|
114
|
-
stig_id: 3
|
115
|
-
cci: 2
|
116
|
-
check: 12
|
117
|
-
fix: 10
|
118
|
-
'
|
119
|
-
myfile = File.new('mapping.yml', 'w')
|
120
|
-
myfile.puts template
|
121
|
-
myfile.close
|
122
|
-
end
|
123
|
-
|
124
|
-
desc 'generate_ckl_metadata', 'Generate metadata file that can be passed to inspec2ckl'
|
125
|
-
def generate_ckl_metadata
|
126
|
-
metadata = {}
|
127
|
-
|
128
|
-
metadata['stigid'] = ask('STID ID: ')
|
129
|
-
metadata['role'] = ask('Role: ')
|
130
|
-
metadata['type'] = ask('Type: ')
|
131
|
-
metadata['hostname'] = ask('Hostname: ')
|
132
|
-
metadata['ip'] = ask('IP Address: ')
|
133
|
-
metadata['mac'] = ask('MAC Address: ')
|
134
|
-
metadata['fqdn'] = ask('FQDN: ')
|
135
|
-
metadata['tech_area'] = ask('Tech Area: ')
|
136
|
-
metadata['target_key'] = ask('Target Key: ')
|
137
|
-
metadata['web_or_database'] = ask('Web or Database: ')
|
138
|
-
metadata['web_db_site'] = ask('Web DB Site: ')
|
139
|
-
metadata['web_db_instance'] = ask('Web DB Instance: ')
|
140
|
-
|
141
|
-
metadata.delete_if { |_key, value| value.empty? }
|
142
|
-
File.open('metadata.json', 'w') do |f|
|
143
|
-
f.write(metadata.to_json)
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
desc 'generate_inspec_metadata', 'Generate mapping file that can be passed to xccdf2inspec'
|
148
|
-
def generate_inspec_metadata
|
149
|
-
metadata = {}
|
150
|
-
|
151
|
-
metadata['maintainer'] = ask('Maintainer: ')
|
152
|
-
metadata['copyright'] = ask('Copyright: ')
|
153
|
-
metadata['copyright_email'] = ask('Copyright Email: ')
|
154
|
-
metadata['license'] = ask('License: ')
|
155
|
-
metadata['version'] = ask('Version: ')
|
156
|
-
|
157
|
-
metadata.delete_if { |_key, value| value.empty? }
|
158
|
-
File.open('metadata.json', 'w') do |f|
|
159
|
-
f.write(metadata.to_json)
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
desc 'summary', 'summary parses an inspec results json to create a summary json'
|
164
|
-
long_desc Help.text(:summary)
|
165
|
-
option :inspec_json, required: true, aliases: '-j'
|
166
|
-
option :output, required: false, aliases: '-o'
|
167
|
-
option :cli, required: false, aliases: '-c'
|
168
|
-
option :verbose, type: :boolean, aliases: '-V'
|
169
|
-
|
170
|
-
def summary
|
171
|
-
summary = InspecTools::Summary.new(File.read(options[:inspec_json])).to_summary
|
172
|
-
|
173
|
-
puts "\ncompliance: #{summary[:compliance]}%\n\n"
|
174
|
-
summary[:status].keys.each do |status|
|
175
|
-
puts status
|
176
|
-
summary[:status][status.to_sym].keys.each do |impact|
|
177
|
-
print "\t#{impact} : #{summary[:status][status.to_sym][impact.to_sym]}\n"
|
178
|
-
end
|
179
|
-
end if options[:cli]
|
180
|
-
|
181
|
-
File.write(options[:output], summary.to_json) if options[:output]
|
182
|
-
end
|
183
|
-
|
184
|
-
desc 'compliance', 'compliance parses an inspec results json to check if the compliance level meets a specified threshold'
|
185
|
-
long_desc Help.text(:compliance)
|
186
|
-
option :inspec_json, required: true, aliases: '-j'
|
187
|
-
option :threshold_file, required: false, aliases: '-f'
|
188
|
-
option :threshold_inline, required: false, aliases: '-i'
|
189
|
-
option :verbose, type: :boolean, aliases: '-V'
|
190
|
-
|
191
|
-
def compliance
|
192
|
-
if options[:threshold_file].nil? && options[:threshold_inline].nil?
|
193
|
-
puts 'Please provide threshold as a yaml file or inline yaml'
|
194
|
-
exit(1)
|
195
|
-
end
|
196
|
-
threshold = YAML.load_file(options[:threshold_file]) unless options[:threshold_file].nil?
|
197
|
-
threshold = YAML.safe_load(options[:threshold_inline]) unless options[:threshold_inline].nil?
|
198
|
-
compliance = InspecTools::Summary.new(File.read(options[:inspec_json])).threshold(threshold)
|
199
|
-
compliance ? exit(0) : exit(1)
|
200
|
-
end
|
13
|
+
CLI = InspecPlugins::InspecToolsPlugin::CliCommand
|
14
|
+
end
|
201
15
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
16
|
+
#=====================================================================#
|
17
|
+
# Pre-Flight Code
|
18
|
+
#=====================================================================#
|
19
|
+
help_commands = ['-h', '--help', 'help']
|
20
|
+
log_commands = ['-l', '--log-directory']
|
21
|
+
version_commands = ['-v', '--version', 'version']
|
22
|
+
|
23
|
+
#---------------------------------------------------------------------#
|
24
|
+
# Adjustments for non-required version commands
|
25
|
+
#---------------------------------------------------------------------#
|
26
|
+
unless (version_commands & ARGV).empty?
|
27
|
+
puts InspecTools::VERSION
|
28
|
+
exit 0
|
207
29
|
end
|
30
|
+
|
31
|
+
#---------------------------------------------------------------------#
|
32
|
+
# Adjustments for non-required log-directory
|
33
|
+
#---------------------------------------------------------------------#
|
34
|
+
ARGV.push("--log-directory=#{Dir.pwd}/logs") if (log_commands & ARGV).empty? && (help_commands & ARGV).empty?
|
data/lib/inspec_tools/csv.rb
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
require 'csv'
|
2
2
|
require 'nokogiri'
|
3
|
-
require 'inspec/objects'
|
4
3
|
require 'word_wrap'
|
5
4
|
require 'yaml'
|
6
5
|
require 'digest'
|
7
|
-
require 'inspec'
|
8
6
|
|
9
7
|
require_relative '../utilities/inspec_util'
|
10
8
|
|
@@ -57,8 +55,8 @@ module InspecTools
|
|
57
55
|
@profile['supports'] = []
|
58
56
|
@profile['attributes'] = []
|
59
57
|
@profile['generator'] = {
|
60
|
-
'name': '
|
61
|
-
'version':
|
58
|
+
'name': 'inspec_tools',
|
59
|
+
'version': VERSION
|
62
60
|
}
|
63
61
|
end
|
64
62
|
|
data/lib/inspec_tools/inspec.rb
CHANGED
@@ -18,7 +18,7 @@ require_relative 'csv'
|
|
18
18
|
module InspecTools
|
19
19
|
class Inspec
|
20
20
|
def initialize(inspec_json, metadata = '{}')
|
21
|
-
@json = JSON.parse(inspec_json)
|
21
|
+
@json = JSON.parse(inspec_json.gsub(/\\+u0000/, ''))
|
22
22
|
@metadata = JSON.parse(metadata)
|
23
23
|
end
|
24
24
|
|
@@ -300,7 +300,7 @@ module InspecTools
|
|
300
300
|
group.rule.reference.dc_identifier = @attribute['reference.dc.identifier']
|
301
301
|
|
302
302
|
group.rule.ident = HappyMapperTools::Benchmark::Ident.new
|
303
|
-
group.rule.ident.system = '
|
303
|
+
group.rule.ident.system = 'https://public.cyber.mil/stigs/cci/'
|
304
304
|
group.rule.ident.ident = control['cci']
|
305
305
|
|
306
306
|
group.rule.fixtext = HappyMapperTools::Benchmark::Fixtext.new
|
data/lib/inspec_tools/pdf.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'digest'
|
2
|
-
require 'inspec'
|
3
2
|
|
4
3
|
require_relative '../utilities/inspec_util'
|
5
4
|
require_relative '../utilities/extract_pdf_text'
|
@@ -92,8 +91,8 @@ module InspecTools
|
|
92
91
|
@profile['supports'] = []
|
93
92
|
@profile['attributes'] = []
|
94
93
|
@profile['generator'] = {
|
95
|
-
'name': '
|
96
|
-
'version':
|
94
|
+
'name': 'inspec_tools',
|
95
|
+
'version': VERSION
|
97
96
|
}
|
98
97
|
end
|
99
98
|
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module InspecToolsPlugin
|
4
|
+
class Plugin < Inspec.plugin(2)
|
5
|
+
# Metadata
|
6
|
+
# Must match entry in plugins.json
|
7
|
+
plugin_name :'inspec-tools_plugin'
|
8
|
+
|
9
|
+
# Activation hooks (CliCommand as an example)
|
10
|
+
cli_command :tools do
|
11
|
+
require_relative 'plugin_cli'
|
12
|
+
InspecPlugins::InspecToolsPlugin::CliCommand
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,275 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
require 'roo'
|
5
|
+
require_relative 'version'
|
6
|
+
|
7
|
+
require_relative '../utilities/inspec_util'
|
8
|
+
require_relative '../utilities/csv_util'
|
9
|
+
|
10
|
+
module InspecTools
|
11
|
+
autoload :Help, 'inspec_tools/help'
|
12
|
+
autoload :Command, 'inspec_tools/command'
|
13
|
+
autoload :XCCDF, 'inspec_tools/xccdf'
|
14
|
+
autoload :PDF, 'inspec_tools/pdf'
|
15
|
+
autoload :CSVTool, 'inspec_tools/csv'
|
16
|
+
autoload :CKL, 'inspec_tools/ckl'
|
17
|
+
autoload :Inspec, 'inspec_tools/inspec'
|
18
|
+
autoload :Summary, 'inspec_tools/summary'
|
19
|
+
autoload :Threshold, 'inspec_tools/threshold'
|
20
|
+
autoload :XLSXTool, 'inspec_tools/xlsx'
|
21
|
+
end
|
22
|
+
|
23
|
+
# rubocop:disable Style/GuardClause
|
24
|
+
module InspecPlugins
|
25
|
+
module InspecToolsPlugin
|
26
|
+
class CliCommand < Inspec.plugin(2, :cli_command) # rubocop:disable Metrics/ClassLength
|
27
|
+
POSSIBLE_LOG_LEVELS = %w{debug info warn error fatal}.freeze
|
28
|
+
|
29
|
+
class_option :log_directory, type: :string, aliases: :l, desc: 'Provie log location'
|
30
|
+
class_option :log_level, type: :string, desc: "Set the logging level: #{POSSIBLE_LOG_LEVELS}"
|
31
|
+
|
32
|
+
subcommand_desc 'tools [COMMAND]', 'Runs inspec_tools commands through Inspec'
|
33
|
+
|
34
|
+
desc 'xccdf2inspec', 'xccdf2inspec translates an xccdf file to an inspec profile'
|
35
|
+
long_desc InspecTools::Help.text(:xccdf2inspec)
|
36
|
+
option :xccdf, required: true, aliases: '-x'
|
37
|
+
option :attributes, required: false, aliases: '-a'
|
38
|
+
option :output, required: false, aliases: '-o', default: 'profile'
|
39
|
+
option :format, required: false, aliases: '-f', enum: %w{ruby hash}, default: 'ruby'
|
40
|
+
option :separate_files, required: false, type: :boolean, default: true, aliases: '-s'
|
41
|
+
option :replace_tags, required: false, aliases: '-r'
|
42
|
+
option :metadata, required: false, aliases: '-m'
|
43
|
+
def xccdf2inspec
|
44
|
+
xccdf = InspecTools::XCCDF.new(File.read(options[:xccdf]), options[:replace_tags])
|
45
|
+
profile = xccdf.to_inspec
|
46
|
+
|
47
|
+
if !options[:metadata].nil?
|
48
|
+
xccdf.inject_metadata(File.read(options[:metadata]))
|
49
|
+
end
|
50
|
+
|
51
|
+
Utils::InspecUtil.unpack_inspec_json(options[:output], profile, options[:separate_files], options[:format])
|
52
|
+
if !options[:attributes].nil?
|
53
|
+
attributes = xccdf.to_attributes
|
54
|
+
File.write(options[:attributes], YAML.dump(attributes))
|
55
|
+
end
|
56
|
+
puts InspecTools.methods
|
57
|
+
end
|
58
|
+
|
59
|
+
desc 'inspec2xccdf', 'inspec2xccdf translates an inspec profile and attributes files to an xccdf file'
|
60
|
+
long_desc InspecTools::Help.text(:inspec2xccdf)
|
61
|
+
option :inspec_json, required: true, aliases: '-j'
|
62
|
+
option :attributes, required: true, aliases: '-a'
|
63
|
+
option :output, required: true, aliases: '-o'
|
64
|
+
def inspec2xccdf
|
65
|
+
json = File.read(options[:inspec_json])
|
66
|
+
inspec_tool = InspecTools::Inspec.new(json)
|
67
|
+
attr_hsh = YAML.load_file(options[:attributes])
|
68
|
+
xccdf = inspec_tool.to_xccdf(attr_hsh)
|
69
|
+
File.write(options[:output], xccdf)
|
70
|
+
end
|
71
|
+
|
72
|
+
desc 'csv2inspec', 'csv2inspec translates CSV to Inspec controls using a mapping file'
|
73
|
+
long_desc InspecTools::Help.text(:csv2inspec)
|
74
|
+
option :csv, required: true, aliases: '-c'
|
75
|
+
option :mapping, required: true, aliases: '-m'
|
76
|
+
option :verbose, required: false, type: :boolean, aliases: '-V'
|
77
|
+
option :output, required: false, aliases: '-o', default: 'profile'
|
78
|
+
option :format, required: false, aliases: '-f', enum: %w{ruby hash}, default: 'ruby'
|
79
|
+
option :separate_files, required: false, type: :boolean, default: true, aliases: '-s'
|
80
|
+
def csv2inspec
|
81
|
+
csv = CSV.read(options[:csv], encoding: 'ISO8859-1')
|
82
|
+
mapping = YAML.load_file(options[:mapping])
|
83
|
+
profile = InspecTools::CSVTool.new(csv, mapping, options[:csv].split('/')[-1].split('.')[0], options[:verbose]).to_inspec
|
84
|
+
Utils::InspecUtil.unpack_inspec_json(options[:output], profile, options[:separate_files], options[:format])
|
85
|
+
end
|
86
|
+
|
87
|
+
desc 'xlsx2inspec', 'xlsx2inspec translates CIS XLSX to Inspec controls using a mapping file'
|
88
|
+
long_desc InspecTools::Help.text(:xlsx2inspec)
|
89
|
+
option :xlsx, required: true, aliases: '-x'
|
90
|
+
option :mapping, required: true, aliases: '-m'
|
91
|
+
option :control_name_prefix, required: true, aliases: '-p'
|
92
|
+
option :verbose, required: false, type: :boolean, aliases: '-V'
|
93
|
+
option :output, required: false, aliases: '-o', default: 'profile'
|
94
|
+
option :format, required: false, aliases: '-f', enum: %w{ruby hash}, default: 'ruby'
|
95
|
+
option :separate_files, required: false, type: :boolean, default: true, aliases: '-s'
|
96
|
+
def xlsx2inspec
|
97
|
+
xlsx = Roo::Spreadsheet.open(options[:xlsx])
|
98
|
+
mapping = YAML.load_file(options[:mapping])
|
99
|
+
profile = InspecTools::XLSXTool.new(xlsx, mapping, options[:xlsx].split('/')[-1].split('.')[0], options[:verbose]).to_inspec(options[:control_name_prefix])
|
100
|
+
Utils::InspecUtil.unpack_inspec_json(options[:output], profile, options[:separate_files], options[:format])
|
101
|
+
end
|
102
|
+
|
103
|
+
desc 'inspec2csv', 'inspec2csv translates Inspec controls to CSV'
|
104
|
+
long_desc InspecTools::Help.text(:inspec2csv)
|
105
|
+
option :inspec_json, required: true, aliases: '-j'
|
106
|
+
option :output, required: true, aliases: '-o'
|
107
|
+
option :verbose, required: false, type: :boolean, aliases: '-V'
|
108
|
+
def inspec2csv
|
109
|
+
csv = InspecTools::Inspec.new(File.read(options[:inspec_json])).to_csv
|
110
|
+
Utils::CSVUtil.unpack_csv(csv, options[:output])
|
111
|
+
end
|
112
|
+
|
113
|
+
desc 'inspec2ckl', 'inspec2ckl translates an inspec json file to a Checklist file'
|
114
|
+
long_desc InspecTools::Help.text(:inspec2ckl)
|
115
|
+
option :inspec_json, required: true, aliases: '-j'
|
116
|
+
option :output, required: true, aliases: '-o'
|
117
|
+
option :verbose, type: :boolean, aliases: '-V'
|
118
|
+
option :metadata, required: false, aliases: '-m'
|
119
|
+
def inspec2ckl
|
120
|
+
metadata = '{}'
|
121
|
+
if !options[:metadata].nil?
|
122
|
+
metadata = File.read(options[:metadata])
|
123
|
+
end
|
124
|
+
ckl = InspecTools::Inspec.new(File.read(options[:inspec_json]), metadata).to_ckl
|
125
|
+
File.write(options[:output], ckl)
|
126
|
+
end
|
127
|
+
|
128
|
+
desc 'pdf2inspec', 'pdf2inspec translates a PDF Security Control Speficication to Inspec Security Profile'
|
129
|
+
long_desc InspecTools::Help.text(:pdf2inspec)
|
130
|
+
option :pdf, required: true, aliases: '-p'
|
131
|
+
option :output, required: false, aliases: '-o', default: 'profile'
|
132
|
+
option :debug, required: false, aliases: '-d', type: :boolean, default: false
|
133
|
+
option :format, required: false, aliases: '-f', enum: %w{ruby hash}, default: 'ruby'
|
134
|
+
option :separate_files, required: false, type: :boolean, default: true, aliases: '-s'
|
135
|
+
def pdf2inspec
|
136
|
+
pdf = File.open(options[:pdf])
|
137
|
+
profile = InspecTools::PDF.new(pdf, options[:output], options[:debug]).to_inspec
|
138
|
+
Utils::InspecUtil.unpack_inspec_json(options[:output], profile, options[:separate_files], options[:format])
|
139
|
+
end
|
140
|
+
|
141
|
+
desc 'generate_map', 'Generates mapping template from CSV to Inspec Controls'
|
142
|
+
def generate_map
|
143
|
+
template = '
|
144
|
+
# Setting csv_header to true will skip the csv file header
|
145
|
+
skip_csv_header: true
|
146
|
+
width : 80
|
147
|
+
|
148
|
+
|
149
|
+
control.id: 0
|
150
|
+
control.title: 15
|
151
|
+
control.desc: 16
|
152
|
+
control.tags:
|
153
|
+
severity: 1
|
154
|
+
rid: 8
|
155
|
+
stig_id: 3
|
156
|
+
cci: 2
|
157
|
+
check: 12
|
158
|
+
fix: 10
|
159
|
+
'
|
160
|
+
myfile = File.new('mapping.yml', 'w')
|
161
|
+
myfile.puts template
|
162
|
+
myfile.close
|
163
|
+
end
|
164
|
+
|
165
|
+
desc 'generate_ckl_metadata', 'Generate metadata file that can be passed to inspec2ckl'
|
166
|
+
def generate_ckl_metadata
|
167
|
+
metadata = {}
|
168
|
+
|
169
|
+
metadata['stigid'] = ask('STID ID: ')
|
170
|
+
metadata['role'] = ask('Role: ')
|
171
|
+
metadata['type'] = ask('Type: ')
|
172
|
+
metadata['hostname'] = ask('Hostname: ')
|
173
|
+
metadata['ip'] = ask('IP Address: ')
|
174
|
+
metadata['mac'] = ask('MAC Address: ')
|
175
|
+
metadata['fqdn'] = ask('FQDN: ')
|
176
|
+
metadata['tech_area'] = ask('Tech Area: ')
|
177
|
+
metadata['target_key'] = ask('Target Key: ')
|
178
|
+
metadata['web_or_database'] = ask('Web or Database: ')
|
179
|
+
metadata['web_db_site'] = ask('Web DB Site: ')
|
180
|
+
metadata['web_db_instance'] = ask('Web DB Instance: ')
|
181
|
+
|
182
|
+
metadata.delete_if { |_key, value| value.empty? }
|
183
|
+
File.open('metadata.json', 'w') do |f|
|
184
|
+
f.write(metadata.to_json)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
desc 'generate_inspec_metadata', 'Generate mapping file that can be passed to xccdf2inspec'
|
189
|
+
def generate_inspec_metadata
|
190
|
+
metadata = {}
|
191
|
+
|
192
|
+
metadata['maintainer'] = ask('Maintainer: ')
|
193
|
+
metadata['copyright'] = ask('Copyright: ')
|
194
|
+
metadata['copyright_email'] = ask('Copyright Email: ')
|
195
|
+
metadata['license'] = ask('License: ')
|
196
|
+
metadata['version'] = ask('Version: ')
|
197
|
+
|
198
|
+
metadata.delete_if { |_key, value| value.empty? }
|
199
|
+
File.open('metadata.json', 'w') do |f|
|
200
|
+
f.write(metadata.to_json)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
desc 'summary', 'summary parses an inspec results json to create a summary json'
|
205
|
+
long_desc InspecTools::Help.text(:summary)
|
206
|
+
option :inspec_json, required: true, aliases: '-j'
|
207
|
+
option :output, required: false, aliases: '-o'
|
208
|
+
option :cli, required: false, aliases: '-c'
|
209
|
+
option :verbose, type: :boolean, aliases: '-V'
|
210
|
+
|
211
|
+
def summary
|
212
|
+
summary = InspecTools::Summary.new(File.read(options[:inspec_json])).to_summary
|
213
|
+
|
214
|
+
puts "\ncompliance: #{summary[:compliance]}%\n\n"
|
215
|
+
summary[:status].keys.each do |status|
|
216
|
+
puts status
|
217
|
+
summary[:status][status.to_sym].keys.each do |impact|
|
218
|
+
print "\t#{impact} : #{summary[:status][status.to_sym][impact.to_sym]}\n"
|
219
|
+
end
|
220
|
+
end if options[:cli]
|
221
|
+
|
222
|
+
File.write(options[:output], summary.to_json) if options[:output]
|
223
|
+
end
|
224
|
+
|
225
|
+
desc 'compliance', 'compliance parses an inspec results json to check if the compliance level meets a specified threshold'
|
226
|
+
long_desc InspecTools::Help.text(:compliance)
|
227
|
+
option :inspec_json, required: true, aliases: '-j'
|
228
|
+
option :threshold_file, required: false, aliases: '-f'
|
229
|
+
option :threshold_inline, required: false, aliases: '-i'
|
230
|
+
option :verbose, type: :boolean, aliases: '-V'
|
231
|
+
|
232
|
+
def compliance
|
233
|
+
if options[:threshold_file].nil? && options[:threshold_inline].nil?
|
234
|
+
puts 'Please provide threshold as a yaml file or inline yaml'
|
235
|
+
exit(1)
|
236
|
+
end
|
237
|
+
threshold = YAML.load_file(options[:threshold_file]) unless options[:threshold_file].nil?
|
238
|
+
threshold = YAML.safe_load(options[:threshold_inline]) unless options[:threshold_inline].nil?
|
239
|
+
compliance = InspecTools::Summary.new(File.read(options[:inspec_json])).threshold(threshold)
|
240
|
+
compliance ? exit(0) : exit(1)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
#=====================================================================#
|
247
|
+
# Pre-Flight Code
|
248
|
+
#=====================================================================#
|
249
|
+
help_commands = ['-h', '--help', 'help']
|
250
|
+
log_commands = ['-l', '--log-directory']
|
251
|
+
version_commands = ['-v', '--version', 'version']
|
252
|
+
|
253
|
+
#---------------------------------------------------------------------#
|
254
|
+
# Adjustments for non-required version commands
|
255
|
+
#---------------------------------------------------------------------#
|
256
|
+
unless (version_commands & ARGV).empty?
|
257
|
+
puts InspecTools::VERSION
|
258
|
+
exit 0
|
259
|
+
end
|
260
|
+
|
261
|
+
#---------------------------------------------------------------------#
|
262
|
+
# Adjustments for non-required log-directory
|
263
|
+
#---------------------------------------------------------------------#
|
264
|
+
ARGV.push("--log-directory=#{Dir.pwd}/logs") if (log_commands & ARGV).empty? && (help_commands & ARGV).empty?
|
265
|
+
|
266
|
+
# Push help to front of command so thor recognizes subcommands are called with help
|
267
|
+
if help_commands.any? { |cmd| ARGV.include? cmd }
|
268
|
+
help_commands.each do |cmd|
|
269
|
+
if (match = ARGV.delete(cmd))
|
270
|
+
ARGV.unshift match
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
# rubocop:enable Style/GuardClause
|