inspec_tools 1.4.2 → 1.7.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|