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.
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