inspec-core 5.22.29 → 6.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/Chef-EULA +9 -0
  3. data/Gemfile +10 -1
  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 +300 -230
  11. data/lib/inspec/config.rb +24 -2
  12. data/lib/inspec/dependencies/cache.rb +33 -0
  13. data/lib/inspec/enhanced_outcomes.rb +1 -0
  14. data/lib/inspec/errors.rb +5 -0
  15. data/lib/inspec/exceptions.rb +2 -0
  16. data/lib/inspec/feature/config.rb +75 -0
  17. data/lib/inspec/feature/runner.rb +26 -0
  18. data/lib/inspec/feature.rb +34 -0
  19. data/lib/inspec/fetcher/git.rb +5 -0
  20. data/lib/inspec/globals.rb +6 -0
  21. data/lib/inspec/plugin/v1/plugin_types/fetcher.rb +7 -0
  22. data/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb +30 -2
  23. data/lib/inspec/profile.rb +373 -12
  24. data/lib/inspec/reporters/cli.rb +1 -1
  25. data/lib/inspec/reporters.rb +67 -54
  26. data/lib/inspec/resources/security_policy.rb +7 -2
  27. data/lib/inspec/run_data.rb +7 -5
  28. data/lib/inspec/runner.rb +34 -5
  29. data/lib/inspec/runner_rspec.rb +12 -9
  30. data/lib/inspec/secrets/yaml.rb +9 -3
  31. data/lib/inspec/shell.rb +10 -0
  32. data/lib/inspec/ui.rb +4 -0
  33. data/lib/inspec/utils/licensing_config.rb +9 -0
  34. data/lib/inspec/utils/profile_ast_helpers.rb +372 -0
  35. data/lib/inspec/version.rb +1 -1
  36. data/lib/inspec/waiver_file_reader.rb +68 -27
  37. data/lib/inspec.rb +2 -1
  38. data/lib/plugins/inspec-compliance/lib/inspec-compliance/cli.rb +189 -168
  39. data/lib/plugins/inspec-habitat/lib/inspec-habitat/cli.rb +10 -3
  40. data/lib/plugins/inspec-init/lib/inspec-init/cli.rb +1 -0
  41. data/lib/plugins/inspec-init/lib/inspec-init/cli_plugin.rb +23 -21
  42. data/lib/plugins/inspec-init/lib/inspec-init/cli_profile.rb +15 -13
  43. data/lib/plugins/inspec-init/lib/inspec-init/cli_resource.rb +15 -13
  44. data/lib/plugins/inspec-license/README.md +16 -0
  45. data/lib/plugins/inspec-license/inspec-license.gemspec +6 -0
  46. data/lib/plugins/inspec-license/lib/inspec-license/cli.rb +26 -0
  47. data/lib/plugins/inspec-license/lib/inspec-license.rb +14 -0
  48. data/lib/plugins/inspec-parallel/README.md +27 -0
  49. data/lib/plugins/inspec-parallel/inspec-parallel.gemspec +6 -0
  50. data/lib/plugins/inspec-parallel/lib/inspec-parallel/child_status_reporter.rb +61 -0
  51. data/lib/plugins/inspec-parallel/lib/inspec-parallel/cli.rb +39 -0
  52. data/lib/plugins/inspec-parallel/lib/inspec-parallel/command.rb +219 -0
  53. data/lib/plugins/inspec-parallel/lib/inspec-parallel/runner.rb +265 -0
  54. data/lib/plugins/inspec-parallel/lib/inspec-parallel/super_reporter/base.rb +24 -0
  55. data/lib/plugins/inspec-parallel/lib/inspec-parallel/super_reporter/silent.rb +7 -0
  56. data/lib/plugins/inspec-parallel/lib/inspec-parallel/super_reporter/status.rb +124 -0
  57. data/lib/plugins/inspec-parallel/lib/inspec-parallel/super_reporter/text.rb +23 -0
  58. data/lib/plugins/inspec-parallel/lib/inspec-parallel/validator.rb +170 -0
  59. data/lib/plugins/inspec-parallel/lib/inspec-parallel.rb +18 -0
  60. data/lib/plugins/inspec-reporter-html2/templates/control.html.erb +7 -6
  61. data/lib/plugins/inspec-reporter-html2/templates/default.js +6 -6
  62. data/lib/plugins/inspec-sign/lib/inspec-sign/base.rb +6 -2
  63. data/lib/plugins/inspec-sign/lib/inspec-sign/cli.rb +11 -4
  64. data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb +6 -13
  65. metadata +54 -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."
@@ -68,11 +74,19 @@ class Inspec::InspecCLI < Inspec::BaseCLI
68
74
  desc: "A list of controls to include. Ignore all other tests."
69
75
  option :tags, type: :array,
70
76
  desc: "A list of tags to filter controls and include only those. Ignore all other tests."
77
+ option :legacy_export, type: :boolean, default: false,
78
+ desc: "Run with legacy export."
71
79
  profile_options
72
80
  def json(target)
73
- # This deprecation warning is ignored currently.
74
- Inspec.deprecate(:renamed_to_inspec_export)
75
- 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
+ }
76
90
  end
77
91
 
78
92
  desc "export PATH", "read the profile in PATH and generate a summary in the given format."
@@ -86,63 +100,70 @@ class Inspec::InspecCLI < Inspec::BaseCLI
86
100
  desc: "For --what=profile, a list of controls to include. Ignore all other tests."
87
101
  option :tags, type: :array,
88
102
  desc: "For --what=profile, a list of tags to filter controls and include only those. Ignore all other tests."
103
+ option :legacy_export, type: :boolean, default: false,
104
+ desc: "Run with legacy export."
89
105
  profile_options
90
106
  def export(target, as_json = false)
91
- o = config
92
- diagnose(o)
93
- o["log_location"] = $stderr
94
- configure_logger(o)
95
-
96
- # using dup to resolve "can't modify frozen String" error.
97
- what = o[:what].dup || "profile"
98
- what.downcase!
99
- raise Inspec::Error.new("Unrecognized option '#{what}' for --what - expected one of profile, readme, or metadata.") unless %w{profile readme metadata}.include?(what)
100
-
101
- default_format_for_what = {
102
- "profile" => "yaml",
103
- "metadata" => "raw",
104
- "readme" => "raw",
105
- }
106
- valid_formats_for_what = {
107
- "profile" => %w{yaml json},
108
- "metadata" => %w{yaml raw}, # not going to argue
109
- "readme" => ["raw"],
110
- }
111
- format = o[:format] || default_format_for_what[what]
112
- # default is json if we were called as old json command
113
- format = "json" if as_json
114
- raise Inspec::Error.new("Invalid option '#{format}' for --format and --what combination") unless format && valid_formats_for_what[what].include?(format)
115
-
116
- o[:backend] = Inspec::Backend.create(Inspec::Config.mock)
117
- o[:check_mode] = true
118
- o[:vendor_cache] = Inspec::Cache.new(o[:vendor_cache])
119
- profile = Inspec::Profile.for_target(target, o)
120
- dst = o[:output].to_s
121
-
122
- case what
123
- when "profile"
124
- if format == "json"
125
- require "json" unless defined?(JSON)
126
- # Write JSON
127
- Inspec::Utils::JsonProfileSummary.produce_json(
128
- info: profile.info,
129
- write_path: dst
130
- )
131
- elsif format == "yaml"
132
- Inspec::Utils::YamlProfileSummary.produce_yaml(
133
- info: profile.info,
134
- write_path: dst
135
- )
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)
136
165
  end
137
- when "readme"
138
- out = dst.empty? ? $stdout : File.open(dst, "w")
139
- out.write(profile.readme)
140
- when "metadata"
141
- out = dst.empty? ? $stdout : File.open(dst, "w")
142
- out.write(profile.metadata_src)
143
- end
144
- rescue StandardError => e
145
- pretty_handle_exception(e)
166
+ }
146
167
  end
147
168
 
148
169
  desc "check PATH", "Verify the metadata in the `inspec.yml` file,\
@@ -152,82 +173,89 @@ class Inspec::InspecCLI < Inspec::BaseCLI
152
173
  desc: "The output format to use. Valid values: `json` and `doc`. Default value: `doc`."
153
174
  option :with_cookstyle, type: :boolean,
154
175
  desc: "Enable or disable cookstyle checks.", default: false
176
+ option :legacy_check, type: :boolean, default: false,
177
+ desc: "Run with legacy check."
155
178
  profile_options
156
179
  def check(path) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
157
- o = config
158
- diagnose(o)
159
- o["log_location"] ||= STDERR if o["format"] == "json"
160
- o["log_level"] ||= "warn"
161
- configure_logger(o)
162
-
163
- o[:backend] = Inspec::Backend.create(Inspec::Config.mock)
164
- o[:check_mode] = true
165
- o[:vendor_cache] = Inspec::Cache.new(o[:vendor_cache])
166
-
167
- # run check
168
- profile = Inspec::Profile.for_target(path, o)
169
- result = profile.check
170
-
171
- if o["format"] == "json"
172
- puts JSON.generate(result)
173
- else
174
- %w{location profile controls timestamp valid}.each do |item|
175
- prepared_string = format("%-12s %s",
176
- "#{item.to_s.capitalize} :",
177
- result[:summary][item.to_sym])
178
- ui.plain_line(prepared_string)
179
- end
180
- puts
181
-
182
- enable_offenses = !Inspec.locally_windows? # See 5723
183
- if result[:errors].empty? && result[:warnings].empty? && result[:offenses].empty?
184
- if enable_offenses
185
- 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)
186
198
  else
187
- ui.plain_line("No errors or warnings")
188
- end
189
- else
190
- item_msg = lambda { |item|
191
- pos = [item[:file], item[:line], item[:column]].compact.join(":")
192
- pos.empty? ? item[:msg] : pos + ": " + item[:msg]
193
- }
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
+ }
194
219
 
195
- result[:errors].each { |item| ui.red " #{Inspec::UI::GLYPHS[:script_x]} #{item_msg.call(item)}\n" }
196
- 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" }
197
222
 
198
- puts
223
+ puts
199
224
 
200
- if enable_offenses && !result[:offenses].empty?
201
- puts "Offenses:\n"
202
- result[:offenses].each { |item| ui.cyan(" #{Inspec::UI::GLYPHS[:script_x]} #{item_msg.call(item)}\n\n") }
203
- 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
204
229
 
205
- offenses = ui.cyan("#{result[:offenses].length} offenses", print: false)
206
- errors = ui.red("#{result[:errors].length} errors", print: false)
207
- warnings = ui.yellow("#{result[:warnings].length} warnings", print: false)
208
- if enable_offenses
209
- ui.plain_line("Summary: #{errors}, #{warnings}, #{offenses}")
210
- else
211
- 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
212
239
  end
240
+ ui.exit Inspec::UI::EXIT_USAGE_ERROR unless result[:summary][:valid]
241
+ rescue StandardError => e
242
+ pretty_handle_exception(e)
213
243
  end
214
- end
215
-
216
- ui.exit Inspec::UI::EXIT_USAGE_ERROR unless result[:summary][:valid]
217
- rescue StandardError => e
218
- pretty_handle_exception(e)
244
+ }
219
245
  end
220
246
 
221
247
  desc "vendor PATH", "Download all dependencies and generate a lockfile in a `vendor` directory"
222
248
  option :overwrite, type: :boolean, default: false,
223
249
  desc: "Overwrite existing vendored dependencies and lockfile."
224
250
  def vendor(path = nil)
225
- o = config
226
- configure_logger(o)
227
- o[:logger] = Logger.new($stdout)
228
- 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])
229
256
 
230
- vendor_deps(path, o)
257
+ vendor_deps(path, o)
258
+ }
231
259
  end
232
260
 
233
261
  desc "archive PATH", "Archive a profile to a tar file (default) or zip file."
@@ -248,37 +276,43 @@ class Inspec::InspecCLI < Inspec::BaseCLI
248
276
  desc: "Run profile check before archiving."
249
277
  option :export, type: :boolean, default: false,
250
278
  desc: "Export the profile to inspec.json and include in archive"
279
+ option :legacy_export, type: :boolean, default: false,
280
+ desc: "Export the profile in legacy mode to inspec.json and include in archive"
251
281
  def archive(path, log_level = nil)
252
- o = config
253
- diagnose(o)
254
-
255
- o[:logger] = Logger.new($stdout)
256
- o[:logger].level = get_log_level(log_level || o[:log_level])
257
- o[:backend] = Inspec::Backend.create(Inspec::Config.mock)
258
-
259
- # Force vendoring with overwrite when archiving
260
- vendor_options = o.dup
261
- vendor_options[:overwrite] = true
262
- vendor_deps(path, vendor_options)
263
-
264
- profile = Inspec::Profile.for_target(path, o)
265
- gem_deps = profile.metadata.gem_dependencies + \
266
- profile.locked_dependencies.list.map { |_k, v| v.profile.metadata.gem_dependencies }.flatten
267
- unless gem_deps.empty?
268
- 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."
269
- 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
270
302
 
271
- result = profile.check if o[:check]
303
+ result = profile.check if o[:check]
272
304
 
273
- if result && !o[:ignore_errors] == false
274
- o[:logger].info "Profile check failed. Please fix the profile before generating an archive."
275
- return ui.exit Inspec::UI::EXIT_USAGE_ERROR
276
- 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
277
309
 
278
- # generate archive
279
- ui.exit Inspec::UI::EXIT_USAGE_ERROR unless profile.archive(o)
280
- rescue StandardError => e
281
- 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
+ }
282
316
  end
283
317
 
284
318
  desc "exec LOCATIONS", "Run all test files at the specified locations."
@@ -356,46 +390,60 @@ class Inspec::InspecCLI < Inspec::BaseCLI
356
390
  ```
357
391
  EOT
358
392
  exec_options
393
+ audit_log_options
359
394
  def exec(*targets)
360
- o = config
361
- diagnose(o)
362
- deprecate_target_id(config)
363
- configure_logger(o)
364
-
365
- runner = Inspec::Runner.new(o)
366
- targets.each { |target| runner.add_target(target) }
367
-
368
- ui.exit runner.run
369
- rescue ArgumentError, RuntimeError, Train::UserError => e
370
- $stderr.puts e.message
371
- ui.exit Inspec::UI::EXIT_USAGE_ERROR
372
- rescue StandardError => e
373
- 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
+ }
374
418
  end
375
419
 
376
420
  desc "detect", "detects the target OS."
377
421
  target_options
378
422
  option :format, type: :string
379
423
  def detect
380
- o = config
381
- deprecate_target_id(config)
382
- 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"
383
429
 
384
- configure_logger(o)
430
+ configure_logger(o)
385
431
 
386
- (_, res) = run_command(o)
432
+ (_, res) = run_command(o)
387
433
 
388
- if o["format"] == "json"
389
- puts res.to_json
390
- else
391
- ui.headline("Platform Details")
392
- ui.plain Inspec::BaseCLI.format_platform_info(params: res, indent: 0, color: 36, enable_color: ui.color?)
393
- end
394
- rescue ArgumentError, RuntimeError, Train::UserError => e
395
- $stderr.puts e.message
396
- ui.exit Inspec::UI::EXIT_USAGE_ERROR
397
- rescue StandardError => e
398
- 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
+ }
399
447
  end
400
448
 
401
449
  desc "shell", "open an interactive debugging shell."
@@ -419,79 +467,99 @@ class Inspec::InspecCLI < Inspec::BaseCLI
419
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."
420
468
  option :enhanced_outcomes, type: :boolean,
421
469
  desc: "Show enhanced outcomes in output"
470
+ audit_log_options
422
471
  def shell_func
423
- o = config
424
- deprecate_target_id(config)
425
- diagnose(o)
426
- o[:debug_shell] = true
427
-
428
- 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
+ }
429
481
 
430
- log_device = suppress_log_output?(o) ? nil : $stdout
431
- o[:logger] = Logger.new(log_device)
432
- o[:logger].level = get_log_level(o[:log_level])
482
+ Inspec::Resource.toggle_inspect unless o[:inspect]
433
483
 
434
- if o[:command].nil?
435
- runner = Inspec::Runner.new(o)
436
- return Inspec::Shell.new(runner).start
437
- 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])
438
487
 
439
- run_type, res = run_command(o)
440
- 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
441
492
 
442
- # No InSpec tests - just print evaluation output.
443
- reporters = o["reporter"] || {}
444
- if reporters.keys.include?("json")
445
- res = if res.respond_to?(:to_json)
446
- res.to_json
447
- else
448
- JSON.dump(res)
449
- end
450
- 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
451
505
 
452
- puts res
453
- ui.exit Inspec::UI::EXIT_NORMAL
454
- rescue RuntimeError, Train::UserError => e
455
- $stderr.puts e.message
456
- rescue StandardError => e
457
- 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
+ }
458
514
  end
459
515
 
460
516
  desc "env", "Outputs shell-appropriate completion configuration."
461
517
  def env(shell = nil)
462
- p = Inspec::EnvPrinter.new(self.class, shell)
463
- p.print_and_exit!
464
- rescue StandardError => e
465
- 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
+ }
466
526
  end
467
527
 
468
528
  option :enhanced_outcomes, type: :boolean,
469
529
  desc: "Show enhanced outcomes output"
470
530
  desc "schema NAME", "print the JSON schema", hide: true
471
531
  def schema(name)
472
- require "inspec/schema/output_schema"
473
- o = config
474
- puts Inspec::Schema::OutputSchema.json(name, o)
475
- rescue StandardError => e
476
- puts e
477
- 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
+ }
478
542
  end
479
543
 
480
544
  desc "run_context", "used to test run-context detection", hide: true
481
545
  def run_context
482
- require "inspec/utils/telemetry/run_context_probe"
483
- 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
+ }
484
550
  end
485
551
 
486
552
  desc "version", "prints the version of this tool."
487
553
  option :format, type: :string
488
554
  def version
489
- if config["format"] == "json"
490
- v = { version: Inspec::VERSION }
491
- puts v.to_json
492
- else
493
- puts Inspec::VERSION
494
- 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
+ }
495
563
  end
496
564
  map %w{-v --version} => :version
497
565
 
@@ -499,14 +567,16 @@ class Inspec::InspecCLI < Inspec::BaseCLI
499
567
  option :vendor_cache, type: :string,
500
568
  desc: "Use the given path for caching dependencies, (default: `~/.inspec/cache`)."
501
569
  def clear_cache
502
- o = config
503
- configure_logger(o)
504
- cache_path = o[:vendor_cache] || "~/.inspec/cache"
505
- FileUtils.rm_r Dir.glob(File.expand_path(cache_path))
506
-
507
- o[:logger] = Logger.new($stdout)
508
- o[:logger].level = get_log_level(o[:log_level])
509
- 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
+ }
510
580
  end
511
581
 
512
582
  private