inspec-core 5.22.40 → 6.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/Chef-EULA +9 -0
  3. data/Gemfile +10 -5
  4. data/etc/features.sig +6 -0
  5. data/etc/features.yaml +94 -0
  6. data/inspec-core.gemspec +14 -5
  7. data/lib/inspec/backend.rb +2 -0
  8. data/lib/inspec/base_cli.rb +80 -4
  9. data/lib/inspec/cached_fetcher.rb +24 -3
  10. data/lib/inspec/cli.rb +292 -235
  11. data/lib/inspec/config.rb +24 -11
  12. data/lib/inspec/dependencies/cache.rb +33 -0
  13. data/lib/inspec/dependencies/dependency_set.rb +2 -2
  14. data/lib/inspec/dsl.rb +1 -1
  15. data/lib/inspec/enhanced_outcomes.rb +1 -0
  16. data/lib/inspec/errors.rb +5 -0
  17. data/lib/inspec/exceptions.rb +2 -0
  18. data/lib/inspec/feature/config.rb +75 -0
  19. data/lib/inspec/feature/runner.rb +26 -0
  20. data/lib/inspec/feature.rb +34 -0
  21. data/lib/inspec/fetcher/git.rb +5 -0
  22. data/lib/inspec/globals.rb +6 -0
  23. data/lib/inspec/plugin/v1/plugin_types/fetcher.rb +7 -0
  24. data/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb +30 -2
  25. data/lib/inspec/profile.rb +46 -3
  26. data/lib/inspec/reporters/cli.rb +1 -1
  27. data/lib/inspec/reporters.rb +67 -54
  28. data/lib/inspec/rule.rb +9 -14
  29. data/lib/inspec/run_data.rb +7 -5
  30. data/lib/inspec/runner.rb +35 -6
  31. data/lib/inspec/runner_rspec.rb +12 -9
  32. data/lib/inspec/secrets/yaml.rb +9 -3
  33. data/lib/inspec/shell.rb +10 -0
  34. data/lib/inspec/ui.rb +4 -0
  35. data/lib/inspec/utils/licensing_config.rb +9 -0
  36. data/lib/inspec/utils/waivers/csv_file_reader.rb +1 -1
  37. data/lib/inspec/utils/waivers/excel_file_reader.rb +1 -1
  38. data/lib/inspec/version.rb +1 -1
  39. data/lib/inspec/waiver_file_reader.rb +68 -27
  40. data/lib/inspec.rb +2 -1
  41. data/lib/plugins/inspec-compliance/lib/inspec-compliance/cli.rb +189 -168
  42. data/lib/plugins/inspec-habitat/lib/inspec-habitat/cli.rb +10 -3
  43. data/lib/plugins/inspec-init/lib/inspec-init/cli.rb +1 -0
  44. data/lib/plugins/inspec-init/lib/inspec-init/cli_plugin.rb +23 -21
  45. data/lib/plugins/inspec-init/lib/inspec-init/cli_profile.rb +15 -13
  46. data/lib/plugins/inspec-init/lib/inspec-init/cli_resource.rb +15 -13
  47. data/lib/plugins/inspec-license/README.md +16 -0
  48. data/lib/plugins/inspec-license/inspec-license.gemspec +6 -0
  49. data/lib/plugins/inspec-license/lib/inspec-license/cli.rb +26 -0
  50. data/lib/plugins/inspec-license/lib/inspec-license.rb +14 -0
  51. data/lib/plugins/inspec-parallel/README.md +27 -0
  52. data/lib/plugins/inspec-parallel/inspec-parallel.gemspec +6 -0
  53. data/lib/plugins/inspec-parallel/lib/inspec-parallel/child_status_reporter.rb +61 -0
  54. data/lib/plugins/inspec-parallel/lib/inspec-parallel/cli.rb +39 -0
  55. data/lib/plugins/inspec-parallel/lib/inspec-parallel/command.rb +219 -0
  56. data/lib/plugins/inspec-parallel/lib/inspec-parallel/runner.rb +265 -0
  57. data/lib/plugins/inspec-parallel/lib/inspec-parallel/super_reporter/base.rb +24 -0
  58. data/lib/plugins/inspec-parallel/lib/inspec-parallel/super_reporter/silent.rb +7 -0
  59. data/lib/plugins/inspec-parallel/lib/inspec-parallel/super_reporter/status.rb +124 -0
  60. data/lib/plugins/inspec-parallel/lib/inspec-parallel/super_reporter/text.rb +23 -0
  61. data/lib/plugins/inspec-parallel/lib/inspec-parallel/validator.rb +170 -0
  62. data/lib/plugins/inspec-parallel/lib/inspec-parallel.rb +18 -0
  63. data/lib/plugins/inspec-sign/lib/inspec-sign/base.rb +6 -2
  64. data/lib/plugins/inspec-sign/lib/inspec-sign/cli.rb +11 -4
  65. data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb +6 -13
  66. metadata +53 -13
data/lib/inspec/cli.rb CHANGED
@@ -6,6 +6,7 @@ require "inspec/backend"
6
6
  require "inspec/dependencies/cache"
7
7
  require "inspec/utils/json_profile_summary"
8
8
  require "inspec/utils/yaml_profile_summary"
9
+ require "inspec/feature"
9
10
 
10
11
  module Inspec # TODO: move this somewhere "better"?
11
12
  autoload :BaseCLI, "inspec/base_cli"
@@ -61,6 +62,11 @@ class Inspec::InspecCLI < Inspec::BaseCLI
61
62
  require "license_acceptance/cli_flags/thor"
62
63
  include LicenseAcceptance::CLIFlags::Thor
63
64
 
65
+ if Inspec::Dist::EXEC_NAME == "inspec"
66
+ require "chef-licensing/cli_flags/thor"
67
+ include ChefLicensing::CLIFlags::Thor
68
+ end
69
+
64
70
  desc "json PATH", "read all tests in the PATH and generate a JSON summary."
65
71
  option :output, aliases: :o, type: :string,
66
72
  desc: "Save the created profile to a path."
@@ -72,13 +78,15 @@ class Inspec::InspecCLI < Inspec::BaseCLI
72
78
  desc: "Run with legacy export."
73
79
  profile_options
74
80
  def json(target)
75
- # Config initialisation is needed before deprecation warning can be issued
76
- # Deprecator calls config get method to fetch the config value
77
- # Without config initialisation, the config value is not set and hence calling config get through deprecator will set the value of config as blank, making options of json command inaccessible.
78
- config
79
- # This deprecation warning is ignored currently.
80
- Inspec.deprecate(:renamed_to_inspec_export)
81
- export(target, true)
81
+ Inspec.with_feature("inspec-cli-json") {
82
+ # Config initialisation is needed before deprecation warning can be issued
83
+ # Deprecator calls config get method to fetch the config value
84
+ # Without config initialisation, the config value is not set and hence calling config get through deprecator will set the value of config as blank, making options of json command inaccessible.
85
+ config
86
+ # This deprecation warning is ignored currently.
87
+ Inspec.deprecate(:renamed_to_inspec_export)
88
+ export(target, true)
89
+ }
82
90
  end
83
91
 
84
92
  desc "export PATH", "read the profile in PATH and generate a summary in the given format."
@@ -96,62 +104,66 @@ class Inspec::InspecCLI < Inspec::BaseCLI
96
104
  desc: "Run with legacy export."
97
105
  profile_options
98
106
  def export(target, as_json = false)
99
- o = config
100
- diagnose(o)
101
- o["log_location"] = $stderr
102
- configure_logger(o)
103
-
104
- # using dup to resolve "can't modify frozen String" error.
105
- what = o[:what].dup || "profile"
106
- what.downcase!
107
- raise Inspec::Error.new("Unrecognized option '#{what}' for --what - expected one of profile, readme, or metadata.") unless %w{profile readme metadata}.include?(what)
108
-
109
- default_format_for_what = {
110
- "profile" => "yaml",
111
- "metadata" => "raw",
112
- "readme" => "raw",
113
- }
114
- valid_formats_for_what = {
115
- "profile" => %w{yaml json},
116
- "metadata" => %w{yaml raw}, # not going to argue
117
- "readme" => ["raw"],
118
- }
119
- format = o[:format] || default_format_for_what[what]
120
- # default is json if we were called as old json command
121
- format = "json" if as_json
122
- raise Inspec::Error.new("Invalid option '#{format}' for --format and --what combination") unless format && valid_formats_for_what[what].include?(format)
123
-
124
- o[:backend] = Inspec::Backend.create(Inspec::Config.mock)
125
- o[:check_mode] = true
126
- o[:vendor_cache] = Inspec::Cache.new(o[:vendor_cache])
127
- profile = Inspec::Profile.for_target(target, o)
128
- dst = o[:output].to_s
129
-
130
- case what
131
- when "profile"
132
- profile_info = o[:legacy_export] ? profile.info : profile.info_from_parse
133
- if format == "json"
134
- require "json" unless defined?(JSON)
135
- # Write JSON
136
- Inspec::Utils::JsonProfileSummary.produce_json(
137
- info: profile_info,
138
- write_path: dst
139
- )
140
- elsif format == "yaml"
141
- Inspec::Utils::YamlProfileSummary.produce_yaml(
142
- info: profile_info,
143
- write_path: dst
144
- )
107
+ Inspec.with_feature("inspec-cli-export") {
108
+ begin
109
+ o = config
110
+ diagnose(o)
111
+ o["log_location"] = $stderr
112
+ configure_logger(o)
113
+
114
+ # using dup to resolve "can't modify frozen String" error.
115
+ what = o[:what].dup || "profile"
116
+ what.downcase!
117
+ raise Inspec::Error.new("Unrecognized option '#{what}' for --what - expected one of profile, readme, or metadata.") unless %w{profile readme metadata}.include?(what)
118
+
119
+ default_format_for_what = {
120
+ "profile" => "yaml",
121
+ "metadata" => "raw",
122
+ "readme" => "raw",
123
+ }
124
+ valid_formats_for_what = {
125
+ "profile" => %w{yaml json},
126
+ "metadata" => %w{yaml raw}, # not going to argue
127
+ "readme" => ["raw"],
128
+ }
129
+ format = o[:format] || default_format_for_what[what]
130
+ # default is json if we were called as old json command
131
+ format = "json" if as_json
132
+ raise Inspec::Error.new("Invalid option '#{format}' for --format and --what combination") unless format && valid_formats_for_what[what].include?(format)
133
+
134
+ o[:backend] = Inspec::Backend.create(Inspec::Config.mock)
135
+ o[:check_mode] = true
136
+ o[:vendor_cache] = Inspec::Cache.new(o[:vendor_cache])
137
+ profile = Inspec::Profile.for_target(target, o)
138
+ dst = o[:output].to_s
139
+
140
+ case what
141
+ when "profile"
142
+ profile_info = o[:legacy_export] ? profile.info : profile.info_from_parse
143
+ if format == "json"
144
+ require "json" unless defined?(JSON)
145
+ # Write JSON
146
+ Inspec::Utils::JsonProfileSummary.produce_json(
147
+ info: profile_info,
148
+ write_path: dst
149
+ )
150
+ elsif format == "yaml"
151
+ Inspec::Utils::YamlProfileSummary.produce_yaml(
152
+ info: profile_info,
153
+ write_path: dst
154
+ )
155
+ end
156
+ when "readme"
157
+ out = dst.empty? ? $stdout : File.open(dst, "w")
158
+ out.write(profile.readme)
159
+ when "metadata"
160
+ out = dst.empty? ? $stdout : File.open(dst, "w")
161
+ out.write(profile.metadata_src)
162
+ end
163
+ rescue StandardError => e
164
+ pretty_handle_exception(e)
145
165
  end
146
- when "readme"
147
- out = dst.empty? ? $stdout : File.open(dst, "w")
148
- out.write(profile.readme)
149
- when "metadata"
150
- out = dst.empty? ? $stdout : File.open(dst, "w")
151
- out.write(profile.metadata_src)
152
- end
153
- rescue StandardError => e
154
- pretty_handle_exception(e)
166
+ }
155
167
  end
156
168
 
157
169
  desc "check PATH", "Verify the metadata in the `inspec.yml` file,\
@@ -165,80 +177,85 @@ class Inspec::InspecCLI < Inspec::BaseCLI
165
177
  desc: "Run with legacy check."
166
178
  profile_options
167
179
  def check(path) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
168
- o = config
169
- diagnose(o)
170
- o["log_location"] ||= STDERR if o["format"] == "json"
171
- o["log_level"] ||= "warn"
172
- configure_logger(o)
173
-
174
- o[:backend] = Inspec::Backend.create(Inspec::Config.mock)
175
- o[:check_mode] = true
176
- o[:vendor_cache] = Inspec::Cache.new(o[:vendor_cache])
177
-
178
- # run check
179
- profile = Inspec::Profile.for_target(path, o)
180
- result = o[:legacy_check] ? profile.legacy_check : profile.check
181
-
182
- if o["format"] == "json"
183
- puts JSON.generate(result)
184
- else
185
- %w{location profile controls timestamp valid}.each do |item|
186
- prepared_string = format("%-12s %s",
187
- "#{item.to_s.capitalize} :",
188
- result[:summary][item.to_sym])
189
- ui.plain_line(prepared_string)
190
- end
191
- puts
192
-
193
- enable_offenses = !Inspec.locally_windows? # See 5723
194
- if result[:errors].empty? && result[:warnings].empty? && result[:offenses].empty?
195
- if enable_offenses
196
- ui.plain_line("No errors, warnings, or offenses")
180
+ Inspec.with_feature("inspec-cli-check") {
181
+ begin
182
+ o = config
183
+ diagnose(o)
184
+ o["log_location"] ||= STDERR if o["format"] == "json"
185
+ o["log_level"] ||= "warn"
186
+ configure_logger(o)
187
+
188
+ o[:backend] = Inspec::Backend.create(Inspec::Config.mock)
189
+ o[:check_mode] = true
190
+ o[:vendor_cache] = Inspec::Cache.new(o[:vendor_cache])
191
+
192
+ # run check
193
+ profile = Inspec::Profile.for_target(path, o)
194
+ result = o[:legacy_check] ? profile.legacy_check : profile.check
195
+
196
+ if o["format"] == "json"
197
+ puts JSON.generate(result)
197
198
  else
198
- ui.plain_line("No errors or warnings")
199
- end
200
- else
201
- item_msg = lambda { |item|
202
- pos = [item[:file], item[:line], item[:column]].compact.join(":")
203
- pos.empty? ? item[:msg] : pos + ": " + item[:msg]
204
- }
199
+ %w{location profile controls timestamp valid}.each do |item|
200
+ prepared_string = format("%-12s %s",
201
+ "#{item.to_s.capitalize} :",
202
+ result[:summary][item.to_sym])
203
+ ui.plain_line(prepared_string)
204
+ end
205
+ puts
206
+
207
+ enable_offenses = !Inspec.locally_windows? # See 5723
208
+ if result[:errors].empty? && result[:warnings].empty? && result[:offenses].empty?
209
+ if enable_offenses
210
+ ui.plain_line("No errors, warnings, or offenses")
211
+ else
212
+ ui.plain_line("No errors or warnings")
213
+ end
214
+ else
215
+ item_msg = lambda { |item|
216
+ pos = [item[:file], item[:line], item[:column]].compact.join(":")
217
+ pos.empty? ? item[:msg] : pos + ": " + item[:msg]
218
+ }
205
219
 
206
- result[:errors].each { |item| ui.red " #{Inspec::UI::GLYPHS[:script_x]} #{item_msg.call(item)}\n" }
207
- result[:warnings].each { |item| ui.yellow " ! #{item_msg.call(item)}\n" }
220
+ result[:errors].each { |item| ui.red " #{Inspec::UI::GLYPHS[:script_x]} #{item_msg.call(item)}\n" }
221
+ result[:warnings].each { |item| ui.yellow " ! #{item_msg.call(item)}\n" }
208
222
 
209
- puts
223
+ puts
210
224
 
211
- if enable_offenses && !result[:offenses].empty?
212
- puts "Offenses:\n"
213
- result[:offenses].each { |item| ui.cyan(" #{Inspec::UI::GLYPHS[:script_x]} #{item_msg.call(item)}\n\n") }
214
- end
225
+ if enable_offenses && !result[:offenses].empty?
226
+ puts "Offenses:\n"
227
+ result[:offenses].each { |item| ui.cyan(" #{Inspec::UI::GLYPHS[:script_x]} #{item_msg.call(item)}\n\n") }
228
+ end
215
229
 
216
- offenses = ui.cyan("#{result[:offenses].length} offenses", print: false)
217
- errors = ui.red("#{result[:errors].length} errors", print: false)
218
- warnings = ui.yellow("#{result[:warnings].length} warnings", print: false)
219
- if enable_offenses
220
- ui.plain_line("Summary: #{errors}, #{warnings}, #{offenses}")
221
- else
222
- ui.plain_line("Summary: #{errors}, #{warnings}")
230
+ offenses = ui.cyan("#{result[:offenses].length} offenses", print: false)
231
+ errors = ui.red("#{result[:errors].length} errors", print: false)
232
+ warnings = ui.yellow("#{result[:warnings].length} warnings", print: false)
233
+ if enable_offenses
234
+ ui.plain_line("Summary: #{errors}, #{warnings}, #{offenses}")
235
+ else
236
+ ui.plain_line("Summary: #{errors}, #{warnings}")
237
+ end
238
+ end
223
239
  end
240
+ ui.exit Inspec::UI::EXIT_USAGE_ERROR unless result[:summary][:valid]
241
+ rescue StandardError => e
242
+ pretty_handle_exception(e)
224
243
  end
225
- end
226
-
227
- ui.exit Inspec::UI::EXIT_USAGE_ERROR unless result[:summary][:valid]
228
- rescue StandardError => e
229
- pretty_handle_exception(e)
244
+ }
230
245
  end
231
246
 
232
247
  desc "vendor PATH", "Download all dependencies and generate a lockfile in a `vendor` directory"
233
248
  option :overwrite, type: :boolean, default: false,
234
249
  desc: "Overwrite existing vendored dependencies and lockfile."
235
250
  def vendor(path = nil)
236
- o = config
237
- configure_logger(o)
238
- o[:logger] = Logger.new($stdout)
239
- o[:logger].level = get_log_level(o[:log_level])
251
+ Inspec.with_feature("inspec-cli-vendor") {
252
+ o = config
253
+ configure_logger(o)
254
+ o[:logger] = Logger.new($stdout)
255
+ o[:logger].level = get_log_level(o[:log_level])
240
256
 
241
- vendor_deps(path, o)
257
+ vendor_deps(path, o)
258
+ }
242
259
  end
243
260
 
244
261
  desc "archive PATH", "Archive a profile to a tar file (default) or zip file."
@@ -262,36 +279,40 @@ class Inspec::InspecCLI < Inspec::BaseCLI
262
279
  option :legacy_export, type: :boolean, default: false,
263
280
  desc: "Export the profile in legacy mode to inspec.json and include in archive"
264
281
  def archive(path, log_level = nil)
265
- o = config
266
- diagnose(o)
267
-
268
- o[:logger] = Logger.new($stdout)
269
- o[:logger].level = get_log_level(log_level || o[:log_level])
270
- o[:backend] = Inspec::Backend.create(Inspec::Config.mock)
271
-
272
- # Force vendoring with overwrite when archiving
273
- vendor_options = o.dup
274
- vendor_options[:overwrite] = true
275
- vendor_deps(path, vendor_options)
276
-
277
- profile = Inspec::Profile.for_target(path, o)
278
- gem_deps = profile.metadata.gem_dependencies + \
279
- profile.locked_dependencies.list.map { |_k, v| v.profile.metadata.gem_dependencies }.flatten
280
- unless gem_deps.empty?
281
- o[:logger].warn "Archiving a profile that contains gem dependencies, but InSpec cannot package gems with the profile! Please archive your ~/.inspec/gems directory separately."
282
- end
282
+ Inspec.with_feature("inspec-cli-archive") {
283
+ begin
284
+ o = config
285
+ diagnose(o)
286
+
287
+ o[:logger] = Logger.new($stdout)
288
+ o[:logger].level = get_log_level(log_level || o[:log_level])
289
+ o[:backend] = Inspec::Backend.create(Inspec::Config.mock)
290
+
291
+ # Force vendoring with overwrite when archiving
292
+ vendor_options = o.dup
293
+ vendor_options[:overwrite] = true
294
+ vendor_deps(path, vendor_options)
295
+
296
+ profile = Inspec::Profile.for_target(path, o)
297
+ gem_deps = profile.metadata.gem_dependencies + \
298
+ profile.locked_dependencies.list.map { |_k, v| v.profile.metadata.gem_dependencies }.flatten
299
+ unless gem_deps.empty?
300
+ o[:logger].warn "Archiving a profile that contains gem dependencies, but InSpec cannot package gems with the profile! Please archive your ~/.inspec/gems directory separately."
301
+ end
283
302
 
284
- result = profile.check if o[:check]
303
+ result = profile.check if o[:check]
285
304
 
286
- if result && !o[:ignore_errors] == false
287
- o[:logger].info "Profile check failed. Please fix the profile before generating an archive."
288
- return ui.exit Inspec::UI::EXIT_USAGE_ERROR
289
- end
305
+ if result && !o[:ignore_errors] == false
306
+ o[:logger].info "Profile check failed. Please fix the profile before generating an archive."
307
+ return ui.exit Inspec::UI::EXIT_USAGE_ERROR
308
+ end
290
309
 
291
- # generate archive
292
- ui.exit Inspec::UI::EXIT_USAGE_ERROR unless profile.archive(o)
293
- rescue StandardError => e
294
- pretty_handle_exception(e)
310
+ # generate archive
311
+ ui.exit Inspec::UI::EXIT_USAGE_ERROR unless profile.archive(o)
312
+ rescue StandardError => e
313
+ pretty_handle_exception(e)
314
+ end
315
+ }
295
316
  end
296
317
 
297
318
  desc "exec LOCATIONS", "Run all test files at the specified locations."
@@ -369,46 +390,60 @@ class Inspec::InspecCLI < Inspec::BaseCLI
369
390
  ```
370
391
  EOT
371
392
  exec_options
393
+ audit_log_options
372
394
  def exec(*targets)
373
- o = config
374
- diagnose(o)
375
- deprecate_target_id(config)
376
- configure_logger(o)
377
-
378
- runner = Inspec::Runner.new(o)
379
- targets.each { |target| runner.add_target(target) }
380
-
381
- ui.exit runner.run
382
- rescue ArgumentError, RuntimeError, Train::UserError => e
383
- $stderr.puts e.message
384
- ui.exit Inspec::UI::EXIT_USAGE_ERROR
385
- rescue StandardError => e
386
- pretty_handle_exception(e)
395
+ Inspec.with_feature("inspec-cli-exec") {
396
+ begin
397
+ o = config
398
+ diagnose(o)
399
+ deprecate_target_id(config)
400
+ configure_logger(o)
401
+
402
+ # Only runs this block when preview flag CHEF_PREVIEW_AUDIT_LOGGING is set
403
+ Inspec.with_feature("inspec-audit-logging") {
404
+ set_and_validate_audit_log_options(o)
405
+ }
406
+
407
+ runner = Inspec::Runner.new(o)
408
+ targets.each { |target| runner.add_target(target) }
409
+
410
+ ui.exit runner.run
411
+ rescue ArgumentError, RuntimeError, Train::UserError => e
412
+ $stderr.puts e.message
413
+ ui.exit Inspec::UI::EXIT_USAGE_ERROR
414
+ rescue StandardError => e
415
+ pretty_handle_exception(e)
416
+ end
417
+ }
387
418
  end
388
419
 
389
420
  desc "detect", "detects the target OS."
390
421
  target_options
391
422
  option :format, type: :string
392
423
  def detect
393
- o = config
394
- deprecate_target_id(config)
395
- o[:command] = "platform.params"
424
+ Inspec.with_feature("inspec-cli-detect") {
425
+ begin
426
+ o = config
427
+ deprecate_target_id(config)
428
+ o[:command] = "platform.params"
396
429
 
397
- configure_logger(o)
430
+ configure_logger(o)
398
431
 
399
- (_, res) = run_command(o)
432
+ (_, res) = run_command(o)
400
433
 
401
- if o["format"] == "json"
402
- puts res.to_json
403
- else
404
- ui.headline("Platform Details")
405
- ui.plain Inspec::BaseCLI.format_platform_info(params: res, indent: 0, color: 36, enable_color: ui.color?)
406
- end
407
- rescue ArgumentError, RuntimeError, Train::UserError => e
408
- $stderr.puts e.message
409
- ui.exit Inspec::UI::EXIT_USAGE_ERROR
410
- rescue StandardError => e
411
- pretty_handle_exception(e)
434
+ if o["format"] == "json"
435
+ puts res.to_json
436
+ else
437
+ ui.headline("Platform Details")
438
+ ui.plain Inspec::BaseCLI.format_platform_info(params: res, indent: 0, color: 36, enable_color: ui.color?)
439
+ end
440
+ rescue ArgumentError, RuntimeError, Train::UserError => e
441
+ $stderr.puts e.message
442
+ ui.exit Inspec::UI::EXIT_USAGE_ERROR
443
+ rescue StandardError => e
444
+ pretty_handle_exception(e)
445
+ end
446
+ }
412
447
  end
413
448
 
414
449
  desc "shell", "open an interactive debugging shell."
@@ -432,79 +467,99 @@ class Inspec::InspecCLI < Inspec::BaseCLI
432
467
  desc: "Specify one or more inputs directly on the command line to the shell, as --input NAME=VALUE. Accepts single-quoted YAML and JSON structures."
433
468
  option :enhanced_outcomes, type: :boolean,
434
469
  desc: "Show enhanced outcomes in output"
470
+ audit_log_options
435
471
  def shell_func
436
- o = config
437
- deprecate_target_id(config)
438
- diagnose(o)
439
- o[:debug_shell] = true
440
-
441
- Inspec::Resource.toggle_inspect unless o[:inspect]
472
+ Inspec.with_feature("inspec-cli-shell") {
473
+ begin
474
+ o = config
475
+ deprecate_target_id(config)
476
+ diagnose(o)
477
+ o[:debug_shell] = true
478
+ Inspec.with_feature("inspec-audit-logging") {
479
+ set_and_validate_audit_log_options(o)
480
+ }
442
481
 
443
- log_device = suppress_log_output?(o) ? nil : $stdout
444
- o[:logger] = Logger.new(log_device)
445
- o[:logger].level = get_log_level(o[:log_level])
482
+ Inspec::Resource.toggle_inspect unless o[:inspect]
446
483
 
447
- if o[:command].nil?
448
- runner = Inspec::Runner.new(o)
449
- return Inspec::Shell.new(runner).start
450
- end
484
+ log_device = suppress_log_output?(o) ? nil : $stdout
485
+ o[:logger] = Logger.new(log_device)
486
+ o[:logger].level = get_log_level(o[:log_level])
451
487
 
452
- run_type, res = run_command(o)
453
- ui.exit res unless run_type == :ruby_eval
488
+ if o[:command].nil?
489
+ runner = Inspec::Runner.new(o)
490
+ return Inspec::Shell.new(runner).start
491
+ end
454
492
 
455
- # No InSpec tests - just print evaluation output.
456
- reporters = o["reporter"] || {}
457
- if reporters.keys.include?("json")
458
- res = if res.respond_to?(:to_json)
459
- res.to_json
460
- else
461
- JSON.dump(res)
462
- end
463
- end
493
+ run_type, res = run_command(o)
494
+ ui.exit res unless run_type == :ruby_eval
495
+
496
+ # No InSpec tests - just print evaluation output.
497
+ reporters = o["reporter"] || {}
498
+ if reporters.keys.include?("json")
499
+ res = if res.respond_to?(:to_json)
500
+ res.to_json
501
+ else
502
+ JSON.dump(res)
503
+ end
504
+ end
464
505
 
465
- puts res
466
- ui.exit Inspec::UI::EXIT_NORMAL
467
- rescue RuntimeError, Train::UserError => e
468
- $stderr.puts e.message
469
- rescue StandardError => e
470
- pretty_handle_exception(e)
506
+ puts res
507
+ ui.exit Inspec::UI::EXIT_NORMAL
508
+ rescue RuntimeError, Train::UserError => e
509
+ $stderr.puts e.message
510
+ rescue StandardError => e
511
+ pretty_handle_exception(e)
512
+ end
513
+ }
471
514
  end
472
515
 
473
516
  desc "env", "Outputs shell-appropriate completion configuration."
474
517
  def env(shell = nil)
475
- p = Inspec::EnvPrinter.new(self.class, shell)
476
- p.print_and_exit!
477
- rescue StandardError => e
478
- pretty_handle_exception(e)
518
+ Inspec.with_feature("inspec-cli-env") {
519
+ begin
520
+ p = Inspec::EnvPrinter.new(self.class, shell)
521
+ p.print_and_exit!
522
+ rescue StandardError => e
523
+ pretty_handle_exception(e)
524
+ end
525
+ }
479
526
  end
480
527
 
481
528
  option :enhanced_outcomes, type: :boolean,
482
529
  desc: "Show enhanced outcomes output"
483
530
  desc "schema NAME", "print the JSON schema", hide: true
484
531
  def schema(name)
485
- require "inspec/schema/output_schema"
486
- o = config
487
- puts Inspec::Schema::OutputSchema.json(name, o)
488
- rescue StandardError => e
489
- puts e
490
- puts "Valid schemas are #{Inspec::Schema::OutputSchema.names.join(", ")}"
532
+ Inspec.with_feature("inspec-cli-schema") {
533
+ begin
534
+ require "inspec/schema/output_schema"
535
+ o = config
536
+ puts Inspec::Schema::OutputSchema.json(name, o)
537
+ rescue StandardError => e
538
+ puts e
539
+ puts "Valid schemas are #{Inspec::Schema::OutputSchema.names.join(", ")}"
540
+ end
541
+ }
491
542
  end
492
543
 
493
544
  desc "run_context", "used to test run-context detection", hide: true
494
545
  def run_context
495
- require "inspec/utils/telemetry/run_context_probe"
496
- puts Inspec::Telemetry::RunContextProbe.guess_run_context
546
+ Inspec.with_feature("inspec-cli-run-context") {
547
+ require "inspec/utils/telemetry/run_context_probe"
548
+ puts Inspec::Telemetry::RunContextProbe.guess_run_context
549
+ }
497
550
  end
498
551
 
499
552
  desc "version", "prints the version of this tool."
500
553
  option :format, type: :string
501
554
  def version
502
- if config["format"] == "json"
503
- v = { version: Inspec::VERSION }
504
- puts v.to_json
505
- else
506
- puts Inspec::VERSION
507
- end
555
+ Inspec.with_feature("inspec-cli-version") {
556
+ if config["format"] == "json"
557
+ v = { version: Inspec::VERSION }
558
+ puts v.to_json
559
+ else
560
+ puts Inspec::VERSION
561
+ end
562
+ }
508
563
  end
509
564
  map %w{-v --version} => :version
510
565
 
@@ -512,14 +567,16 @@ class Inspec::InspecCLI < Inspec::BaseCLI
512
567
  option :vendor_cache, type: :string,
513
568
  desc: "Use the given path for caching dependencies, (default: `~/.inspec/cache`)."
514
569
  def clear_cache
515
- o = config
516
- configure_logger(o)
517
- cache_path = o[:vendor_cache] || "~/.inspec/cache"
518
- FileUtils.rm_r Dir.glob(File.expand_path(cache_path))
519
-
520
- o[:logger] = Logger.new($stdout)
521
- o[:logger].level = get_log_level(o[:log_level])
522
- o[:logger].info "== InSpec cache cleared successfully =="
570
+ Inspec.with_feature("inspec-cli-clear-cache") {
571
+ o = config
572
+ configure_logger(o)
573
+ cache_path = o[:vendor_cache] || "~/.inspec/cache"
574
+ FileUtils.rm_r Dir.glob(File.expand_path(cache_path))
575
+
576
+ o[:logger] = Logger.new($stdout)
577
+ o[:logger].level = get_log_level(o[:log_level])
578
+ o[:logger].info "== InSpec cache cleared successfully =="
579
+ }
523
580
  end
524
581
 
525
582
  private