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.
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
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env ruby
1
+ #!/usr/bin/env ruby -E UTF-8
2
2
 
3
3
  # Trap ^C
4
4
  Signal.trap('INT') {
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://iase.disa.mil/stigs/cci/pages/index.aspx
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
 
@@ -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: 'This Security Technical Implementation Guide is published
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: '1'
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: '2017-01-20'
12
+ benchmark.status.date: "2017-01-20"
12
13
  benchmark.notice.id: terms-of-use
13
- benchmark.plaintext: 'Release: 1 Benchmark Date: 20 Jan 2017'
14
+ benchmark.plaintext: "Release: 1 Benchmark Date: 20 Jan 2017"
14
15
  benchmark.plaintext.id: release-info
15
- reference.href: http://iase.disa.mil
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: '3087'
22
+ reference.dc.identifier: "3087"
22
23
  content_ref.name: M
23
24
  content_ref.href: DPMS_XCCDF_Benchmark_PostgreSQL_9-x_STIG.xml
@@ -79,7 +79,6 @@ module HappyMapperTools
79
79
  tag 'CHECKLIST'
80
80
  has_one :asset, Asset, tag: 'ASSET'
81
81
  has_one :stig, Stigs, tag: 'STIGS'
82
- Encoding.default_external = 'UTF-8'
83
82
 
84
83
  def where(attrib, data)
85
84
  stig.istig.vuln.each do |vuln|
data/lib/inspec_tools.rb CHANGED
@@ -13,4 +13,5 @@ module InspecTools
13
13
  autoload :Inspec, 'inspec_tools/inspec'
14
14
  autoload :Summary, 'inspec_tools/summary'
15
15
  autoload :Threshold, 'inspec_tools/threshold'
16
+ autoload :XLSXTool, 'inspec_tools/xlsx.rb'
16
17
  end
@@ -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
- # rubocop:disable Style/GuardClause
7
-
11
+ # This tells the ruby cli app to use the same argument parsing as the plugin
8
12
  module InspecTools
9
- class CLI < Command
10
- desc 'xccdf2inspec', 'xccdf2inspec translates an xccdf file to an inspec profile'
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
- desc 'version', 'prints version'
203
- def version
204
- puts VERSION
205
- end
206
- end
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?
@@ -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': 'inspec',
61
- 'version': Gem.loaded_specs['inspec'].version
58
+ 'name': 'inspec_tools',
59
+ 'version': VERSION
62
60
  }
63
61
  end
64
62
 
@@ -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 = 'http://iase.disa.mil/cci'
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
@@ -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': 'inspec',
96
- 'version': Gem.loaded_specs['inspec'].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