inspec-core 5.22.80 → 6.6.0

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.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/Chef-EULA +9 -0
  3. data/Gemfile +25 -40
  4. data/etc/features.sig +6 -0
  5. data/etc/features.yaml +94 -0
  6. data/inspec-core.gemspec +16 -15
  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 +1 -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/fetcher/url.rb +7 -29
  23. data/lib/inspec/globals.rb +6 -0
  24. data/lib/inspec/input_registry.rb +1 -5
  25. data/lib/inspec/plugin/v1/plugin_types/fetcher.rb +7 -0
  26. data/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb +30 -2
  27. data/lib/inspec/profile.rb +48 -5
  28. data/lib/inspec/reporters/cli.rb +1 -1
  29. data/lib/inspec/reporters.rb +67 -54
  30. data/lib/inspec/resources/audit_policy.rb +2 -8
  31. data/lib/inspec/resources/groups.rb +0 -52
  32. data/lib/inspec/resources/nftables.rb +1 -14
  33. data/lib/inspec/resources/oracledb_session.rb +3 -9
  34. data/lib/inspec/resources/postgres_session.rb +5 -9
  35. data/lib/inspec/resources/sybase_session.rb +2 -11
  36. data/lib/inspec/resources/virtualization.rb +1 -1
  37. data/lib/inspec/rule.rb +9 -14
  38. data/lib/inspec/run_data.rb +7 -5
  39. data/lib/inspec/runner.rb +35 -6
  40. data/lib/inspec/runner_rspec.rb +12 -9
  41. data/lib/inspec/shell.rb +10 -0
  42. data/lib/inspec/ui.rb +4 -0
  43. data/lib/inspec/utils/licensing_config.rb +9 -0
  44. data/lib/inspec/utils/profile_ast_helpers.rb +2 -1
  45. data/lib/inspec/utils/waivers/csv_file_reader.rb +1 -1
  46. data/lib/inspec/utils/waivers/excel_file_reader.rb +1 -1
  47. data/lib/inspec/version.rb +1 -1
  48. data/lib/inspec/waiver_file_reader.rb +18 -35
  49. data/lib/inspec.rb +2 -1
  50. data/lib/matchers/matchers.rb +3 -3
  51. data/lib/plugins/inspec-compliance/README.md +1 -11
  52. data/lib/plugins/inspec-compliance/lib/inspec-compliance/cli.rb +189 -170
  53. data/lib/plugins/inspec-habitat/lib/inspec-habitat/cli.rb +10 -3
  54. data/lib/plugins/inspec-init/lib/inspec-init/cli.rb +1 -0
  55. data/lib/plugins/inspec-init/lib/inspec-init/cli_plugin.rb +23 -21
  56. data/lib/plugins/inspec-init/lib/inspec-init/cli_profile.rb +15 -13
  57. data/lib/plugins/inspec-init/lib/inspec-init/cli_resource.rb +15 -13
  58. data/lib/plugins/inspec-license/README.md +16 -0
  59. data/lib/plugins/inspec-license/inspec-license.gemspec +6 -0
  60. data/lib/plugins/inspec-license/lib/inspec-license/cli.rb +26 -0
  61. data/lib/plugins/inspec-license/lib/inspec-license.rb +14 -0
  62. data/lib/plugins/inspec-parallel/README.md +27 -0
  63. data/lib/plugins/inspec-parallel/inspec-parallel.gemspec +6 -0
  64. data/lib/plugins/inspec-parallel/lib/inspec-parallel/child_status_reporter.rb +61 -0
  65. data/lib/plugins/inspec-parallel/lib/inspec-parallel/cli.rb +39 -0
  66. data/lib/plugins/inspec-parallel/lib/inspec-parallel/command.rb +219 -0
  67. data/lib/plugins/inspec-parallel/lib/inspec-parallel/runner.rb +265 -0
  68. data/lib/plugins/inspec-parallel/lib/inspec-parallel/super_reporter/base.rb +24 -0
  69. data/lib/plugins/inspec-parallel/lib/inspec-parallel/super_reporter/silent.rb +7 -0
  70. data/lib/plugins/inspec-parallel/lib/inspec-parallel/super_reporter/status.rb +124 -0
  71. data/lib/plugins/inspec-parallel/lib/inspec-parallel/super_reporter/text.rb +23 -0
  72. data/lib/plugins/inspec-parallel/lib/inspec-parallel/validator.rb +170 -0
  73. data/lib/plugins/inspec-parallel/lib/inspec-parallel.rb +18 -0
  74. data/lib/plugins/inspec-sign/lib/inspec-sign/base.rb +10 -11
  75. data/lib/plugins/inspec-sign/lib/inspec-sign/cli.rb +11 -4
  76. data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb +6 -13
  77. data/lib/source_readers/inspec.rb +1 -1
  78. metadata +45 -25
@@ -5,8 +5,6 @@ require "inspec/utils/waivers/json_file_reader"
5
5
  module Inspec
6
6
  class WaiverFileReader
7
7
 
8
- SUPPORTED_FILE_EXTENSION = %w{.yaml .yml .csv .json}.freeze
9
-
10
8
  def self.fetch_waivers_by_profile(profile_id, files)
11
9
  read_waivers_from_file(profile_id, files) if @waivers_data.nil? || @waivers_data[profile_id].nil?
12
10
  @waivers_data[profile_id]
@@ -17,10 +15,14 @@ module Inspec
17
15
  output = {}
18
16
 
19
17
  files.each do |file_path|
20
- next unless valid_waiver_file?(file_path)
18
+ data = read_from_file(file_path)
19
+ output.merge!(data) if !data.nil? && data.is_a?(Hash)
21
20
 
22
- data = parse_waiver_file(file_path)
23
- output.merge!(data) if data.is_a?(Hash)
21
+ if data.nil?
22
+ raise Inspec::Exceptions::WaiversFileNotReadable,
23
+ "Cannot find parser for waivers file." \
24
+ "Check to make sure file has the appropriate extension."
25
+ end
24
26
  rescue Inspec::Exceptions::WaiversFileNotReadable, Inspec::Exceptions::WaiversFileInvalidFormatting => e
25
27
  Inspec::Log.error "Error reading waivers file #{file_path}. #{e.message}"
26
28
  Inspec::UI.new.exit(:usage_error)
@@ -29,38 +31,21 @@ module Inspec
29
31
  @waivers_data[profile_id] = output
30
32
  end
31
33
 
32
- def self.valid_waiver_file?(file_path)
33
- # Check if the file is readable
34
- file_extension = File.extname(file_path).downcase
35
- unless SUPPORTED_FILE_EXTENSION.include?(file_extension)
36
- raise Inspec::Exceptions::WaiversFileNotReadable,
37
- "Unsupported file extension for '#{file_path}'. Allowed waiver file extensions: #{SUPPORTED_FILE_EXTENSION.join(", ")}"
38
- end
39
-
40
- # Check if the file is empty
41
- if File.zero?(file_path)
42
- Inspec::Log.warn "Waivers file '#{file_path}' is empty. Skipping waivers."
43
- return false
44
- end
45
-
46
- true
47
- end
48
-
49
- def self.parse_waiver_file(file_path)
50
- file_extension = File.extname(file_path).downcase
51
-
52
- case file_extension
53
- when ".yaml", ".yml"
54
- data = Secrets::YAML.resolve(file_path)&.inputs
34
+ def self.read_from_file(file_path)
35
+ data = nil
36
+ file_extension = File.extname(file_path)
37
+ if [".yaml", ".yml"].include? file_extension
38
+ data = Secrets::YAML.resolve(file_path)
39
+ data = data.inputs unless data.nil?
55
40
  validate_json_yaml(data)
56
- when ".csv"
41
+ elsif file_extension == ".csv"
57
42
  data = Waivers::CSVFileReader.resolve(file_path)
58
- validate_csv_headers(Waivers::CSVFileReader.headers)
59
- when ".json"
43
+ headers = Waivers::CSVFileReader.headers
44
+ validate_csv_headers(headers)
45
+ elsif file_extension == ".json"
60
46
  data = Waivers::JSONFileReader.resolve(file_path)
61
- validate_json_yaml(data)
47
+ validate_json_yaml(data) unless data.nil?
62
48
  end
63
-
64
49
  data
65
50
  end
66
51
 
@@ -96,8 +81,6 @@ module Inspec
96
81
  end
97
82
 
98
83
  def self.validate_json_yaml(data)
99
- return if data.nil?
100
-
101
84
  missing_required_field = false
102
85
  data.each do |key, value|
103
86
  # In case of yaml or json we need to validate headers/parametes for each value
data/lib/inspec.rb CHANGED
@@ -4,6 +4,7 @@ libdir = File.dirname(__FILE__)
4
4
  $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
5
5
 
6
6
  require "inspec/version"
7
+ require "inspec/utils/licensing_config"
7
8
  require "inspec/exceptions"
8
9
  require "inspec/utils/deprecation"
9
10
  require "inspec/profile"
@@ -30,4 +31,4 @@ require "inspec/source_reader"
30
31
  require "inspec/resource"
31
32
 
32
33
  require "inspec/dependency_loader"
33
- require "inspec/dependency_installer"
34
+ require "inspec/dependency_installer"
@@ -299,9 +299,9 @@ RSpec::Matchers.define :cmp do |first_expected| # rubocop:disable Metrics/BlockL
299
299
  end
300
300
  end
301
301
 
302
- def format_actual(actual, negate = false)
302
+ def format_actual(actual)
303
303
  actual = "0%o" % actual if octal?(@expected) && !actual.nil?
304
- "\n%s\n got: %s\n\n(compared using `cmp` matcher)\n" % [format_expectation(negate), actual]
304
+ "\n%s\n got: %s\n\n(compared using `cmp` matcher)\n" % [format_expectation(false), actual]
305
305
  end
306
306
 
307
307
  def format_expectation(negate)
@@ -316,7 +316,7 @@ RSpec::Matchers.define :cmp do |first_expected| # rubocop:disable Metrics/BlockL
316
316
  end
317
317
 
318
318
  failure_message_when_negated do |actual|
319
- format_actual actual, true
319
+ format_actual actual
320
320
  end
321
321
 
322
322
  description do
@@ -14,18 +14,8 @@ To use the CLI, this InSpec add-on adds the following commands:
14
14
  * `$ inspec automate profiles` - list all available Compliance profiles
15
15
  * `$ inspec exec compliance://profile` - runs a Compliance profile
16
16
  * `$ inspec automate upload path/to/local/profile` - uploads a local profile to Chef Automate/Chef Compliance
17
- * `$ inspec automate upload path/to/local/profile --legacy` - uploads a local profile to Chef Automate/Chef Compliance using legacy functionalities of inspec check and inspec export
18
-
19
- *Options*:
20
- ```
21
- [--overwrite], [--no-overwrite] # Overwrite existing profile on Server.
22
- [--owner=OWNER] # Owner that should own the profile
23
- [--legacy], [--no-legacy] # Enable legacy functionality, activating both legacy export and legacy check.
24
-
25
- uploads a local profile to Chef Automate
26
- ```
27
17
  * `$ inspec automate logout` - logout of Chef Automate/Chef Compliance
28
-
18
+
29
19
  Similar to these CLI commands are:
30
20
 
31
21
  * `$ inspec compliance login` - authentication of the API token against Chef Automate/Chef Compliance
@@ -1,6 +1,7 @@
1
1
  require "inspec/dist"
2
2
 
3
3
  require_relative "api"
4
+ require "inspec/feature"
4
5
 
5
6
  module InspecPlugins
6
7
  module Compliance
@@ -32,90 +33,102 @@ module InspecPlugins
32
33
  option :ent, type: :string, required: false,
33
34
  desc: "Enterprise for #{AUTOMATE_PRODUCT_NAME} reporting (#{AUTOMATE_PRODUCT_NAME} Only)"
34
35
  def login(server)
35
- options["server"] = server
36
- login_response = InspecPlugins::Compliance::API.login(options)
37
- puts login_response
36
+ Inspec.with_feature("inspec-cli-compliance-login") {
37
+ options["server"] = server
38
+ login_response = InspecPlugins::Compliance::API.login(options)
39
+ puts login_response
40
+ }
38
41
  end
39
42
 
40
43
  desc "profiles", "list all available profiles in #{AUTOMATE_PRODUCT_NAME}"
41
44
  option :owner, type: :string, required: false,
42
45
  desc: "owner whose profiles to list"
43
46
  def profiles
44
- config = InspecPlugins::Compliance::Configuration.new
45
- return unless loggedin(config)
46
-
47
- # set owner to config
48
- config["owner"] = options["owner"] || config["user"]
49
-
50
- msg, profiles = InspecPlugins::Compliance::API.profiles(config)
51
- profiles.sort_by! { |hsh| hsh["title"] }
52
- if !profiles.empty?
53
- # iterate over profiles
54
- headline("Available profiles:")
55
- profiles.each do |profile|
56
- owner = profile["owner_id"] || profile["owner"]
57
- li("#{profile["title"]} v#{profile["version"]} (#{mark_text(owner + "/" + profile["name"])})")
47
+ Inspec.with_feature("inspec-cli-compliance-profiles") {
48
+ begin
49
+ config = InspecPlugins::Compliance::Configuration.new
50
+ return unless loggedin(config)
51
+
52
+ # set owner to config
53
+ config["owner"] = options["owner"] || config["user"]
54
+
55
+ msg, profiles = InspecPlugins::Compliance::API.profiles(config)
56
+ profiles.sort_by! { |hsh| hsh["title"] }
57
+ if !profiles.empty?
58
+ # iterate over profiles
59
+ headline("Available profiles:")
60
+ profiles.each do |profile|
61
+ owner = profile["owner_id"] || profile["owner"]
62
+ li("#{profile["title"]} v#{profile["version"]} (#{mark_text(owner + "/" + profile["name"])})")
63
+ end
64
+ else
65
+ puts msg if msg != "success"
66
+ puts "Could not find any profiles"
67
+ exit 1
68
+ end
69
+ rescue InspecPlugins::Compliance::ServerConfigurationMissing
70
+ $stderr.puts "\nServer configuration information is missing. Please login using `#{EXEC_NAME} #{subcommand_name} login`"
71
+ exit 1
58
72
  end
59
- else
60
- puts msg if msg != "success"
61
- puts "Could not find any profiles"
62
- exit 1
63
- end
64
- rescue InspecPlugins::Compliance::ServerConfigurationMissing
65
- $stderr.puts "\nServer configuration information is missing. Please login using `#{EXEC_NAME} #{subcommand_name} login`"
66
- exit 1
73
+ }
67
74
  end
68
75
 
69
76
  desc "exec PROFILE", "executes a #{AUTOMATE_PRODUCT_NAME} profile"
70
77
  exec_options
71
78
  def exec(*tests)
72
- compliance_config = InspecPlugins::Compliance::Configuration.new
73
- return unless loggedin(compliance_config)
79
+ Inspec.with_feature("inspec-cli-compliance-exec") {
80
+ begin
81
+ compliance_config = InspecPlugins::Compliance::Configuration.new
82
+ return unless loggedin(compliance_config)
74
83
 
75
- o = config # o is an Inspec::Config object, provided by a helper method from Inspec::BaseCLI
76
- diagnose(o)
77
- configure_logger(o)
84
+ o = config # o is an Inspec::Config object, provided by a helper method from Inspec::BaseCLI
85
+ diagnose(o)
86
+ configure_logger(o)
78
87
 
79
- # iterate over tests and add compliance scheme
80
- tests = tests.map { |t| "compliance://" + InspecPlugins::Compliance::API.sanitize_profile_name(t) }
88
+ # iterate over tests and add compliance scheme
89
+ tests = tests.map { |t| "compliance://" + InspecPlugins::Compliance::API.sanitize_profile_name(t) }
81
90
 
82
- runner = Inspec::Runner.new(o)
83
- tests.each { |target| runner.add_target(target) }
91
+ runner = Inspec::Runner.new(o)
92
+ tests.each { |target| runner.add_target(target) }
84
93
 
85
- exit runner.run
86
- rescue ArgumentError, RuntimeError, Train::UserError => e
87
- $stderr.puts e.message
88
- exit 1
94
+ exit runner.run
95
+ rescue ArgumentError, RuntimeError, Train::UserError => e
96
+ $stderr.puts e.message
97
+ exit 1
98
+ end
99
+ }
89
100
  end
90
101
 
91
102
  desc "download PROFILE", "downloads a profile from #{AUTOMATE_PRODUCT_NAME}"
92
103
  option :name, type: :string,
93
104
  desc: "Name of the archive filename (file type will be added)"
94
105
  def download(profile_name)
95
- o = options.dup
96
- configure_logger(o)
97
-
98
- config = InspecPlugins::Compliance::Configuration.new
99
- return unless loggedin(config)
100
-
101
- profile_name = InspecPlugins::Compliance::API.sanitize_profile_name(profile_name)
102
- if InspecPlugins::Compliance::API.exist?(config, profile_name)
103
- puts "Downloading `#{profile_name}`"
104
-
105
- fetcher = InspecPlugins::Compliance::Fetcher.resolve(
106
- {
107
- compliance: profile_name,
108
- }
109
- )
110
-
111
- # we provide a name, the fetcher adds the extension
112
- _owner, id = profile_name.split("/")
113
- file_name = fetcher.fetch(o.name || id)
114
- puts "Profile stored to #{file_name}"
115
- else
116
- puts "Profile #{profile_name} is not available in #{AUTOMATE_PRODUCT_NAME}."
117
- exit 1
118
- end
106
+ Inspec.with_feature("inspec-cli-compliance-download") {
107
+ o = options.dup
108
+ configure_logger(o)
109
+
110
+ config = InspecPlugins::Compliance::Configuration.new
111
+ return unless loggedin(config)
112
+
113
+ profile_name = InspecPlugins::Compliance::API.sanitize_profile_name(profile_name)
114
+ if InspecPlugins::Compliance::API.exist?(config, profile_name)
115
+ puts "Downloading `#{profile_name}`"
116
+
117
+ fetcher = InspecPlugins::Compliance::Fetcher.resolve(
118
+ {
119
+ compliance: profile_name,
120
+ }
121
+ )
122
+
123
+ # we provide a name, the fetcher adds the extension
124
+ _owner, id = profile_name.split("/")
125
+ file_name = fetcher.fetch(o.name || id)
126
+ puts "Profile stored to #{file_name}"
127
+ else
128
+ puts "Profile #{profile_name} is not available in #{AUTOMATE_PRODUCT_NAME}."
129
+ exit 1
130
+ end
131
+ }
119
132
  end
120
133
 
121
134
  desc "upload PATH", "uploads a local profile to #{AUTOMATE_PRODUCT_NAME}"
@@ -123,132 +136,138 @@ module InspecPlugins
123
136
  desc: "Overwrite existing profile on Server."
124
137
  option :owner, type: :string, required: false,
125
138
  desc: "Owner that should own the profile"
126
- option :legacy, type: :boolean, default: false,
127
- desc: "Enable legacy functionality, activating both legacy export and legacy check."
128
139
  def upload(path) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
129
- config = InspecPlugins::Compliance::Configuration.new
130
- return unless loggedin(config)
140
+ Inspec.with_feature("inspec-cli-compliance-upload") {
141
+ config = InspecPlugins::Compliance::Configuration.new
142
+ return unless loggedin(config)
131
143
 
132
- # set owner to config
133
- config["owner"] = options["owner"] || config["user"]
144
+ # set owner to config
145
+ config["owner"] = options["owner"] || config["user"]
134
146
 
135
- unless File.exist?(path)
136
- puts "Directory #{path} does not exist."
137
- exit 1
138
- end
147
+ unless File.exist?(path)
148
+ puts "Directory #{path} does not exist."
149
+ exit 1
150
+ end
139
151
 
140
- vendor_deps(path, options) if File.directory?(path)
152
+ vendor_deps(path, options) if File.directory?(path)
141
153
 
142
- o = options.dup
143
- configure_logger(o)
154
+ o = options.dup
155
+ configure_logger(o)
144
156
 
145
- # only run against the mock backend, otherwise we run against the local system
146
- o[:backend] = Inspec::Backend.create(Inspec::Config.mock)
147
- o[:check_mode] = true
148
- o[:vendor_cache] = Inspec::Cache.new(o[:vendor_cache])
157
+ # only run against the mock backend, otherwise we run against the local system
158
+ o[:backend] = Inspec::Backend.create(Inspec::Config.mock)
159
+ o[:check_mode] = true
160
+ o[:vendor_cache] = Inspec::Cache.new(o[:vendor_cache])
149
161
 
150
- # check the profile, we only allow to upload valid profiles
151
- profile = Inspec::Profile.for_target(path, o)
162
+ # check the profile, we only allow to upload valid profiles
163
+ profile = Inspec::Profile.for_target(path, o)
152
164
 
153
- # start verification process
154
- error_count = 0
155
- error = lambda { |msg|
156
- error_count += 1
157
- puts msg
158
- }
165
+ # start verification process
166
+ error_count = 0
167
+ error = lambda { |msg|
168
+ error_count += 1
169
+ puts msg
170
+ }
159
171
 
160
- result = options["legacy"] ? profile.legacy_check : profile.check
161
- unless result[:summary][:valid]
162
- error.call("Profile check failed. Please fix the profile before upload.")
163
- else
164
- puts("Profile is valid")
165
- end
166
-
167
- # determine user information
168
- if (config["token"].nil? && config["refresh_token"].nil?) || config["user"].nil?
169
- error.call("Please login via `#{EXEC_NAME} #{subcommand_name} login`")
170
- end
171
-
172
- # read profile name from inspec.yml
173
- profile_name = profile.name
174
-
175
- # read profile version from inspec.yml
176
- profile_version = profile.version
177
-
178
- # check that the profile is not uploaded already,
179
- # confirm upload to the user (overwrite with --force)
180
- if InspecPlugins::Compliance::API.exist?(config, "#{config["owner"]}/#{profile_name}##{profile_version}") && !options["overwrite"]
181
- error.call("Profile exists on the server, use --overwrite")
182
- end
183
-
184
- # abort if we found an error
185
- if error_count > 0
186
- puts "Found #{error_count} error(s)"
187
- exit 1
188
- end
189
-
190
- # if it is a directory, tar it to tmp directory
191
- generated = false
192
- if File.directory?(path)
193
- generated = true
194
- archive_path = Dir::Tmpname.create([profile_name, ".tar.gz"]) {}
195
- puts "Generate temporary profile archive at #{archive_path}"
196
- profile.archive({ output: archive_path, ignore_errors: false, overwrite: true, legacy_export: options["legacy"] })
197
- else
198
- archive_path = path
199
- end
200
-
201
- puts "Start upload to #{config["owner"]}/#{profile_name}"
202
- pname = ERB::Util.url_encode(profile_name)
203
-
204
- puts "Uploading to #{AUTOMATE_PRODUCT_NAME}"
205
-
206
- success, msg = InspecPlugins::Compliance::API.upload(config, config["owner"], pname, archive_path)
207
-
208
- # delete temp file if it was temporary generated
209
- File.delete(archive_path) if generated && File.exist?(archive_path)
210
-
211
- if success
212
- puts "Successfully uploaded profile"
213
- else
214
- puts "Error during profile upload:"
215
- puts msg
216
- exit 1
217
- end
172
+ result = profile.check
173
+ unless result[:summary][:valid]
174
+ error.call("Profile check failed. Please fix the profile before upload.")
175
+ else
176
+ puts("Profile is valid")
177
+ end
178
+
179
+ # determine user information
180
+ if (config["token"].nil? && config["refresh_token"].nil?) || config["user"].nil?
181
+ error.call("Please login via `#{EXEC_NAME} #{subcommand_name} login`")
182
+ end
183
+
184
+ # read profile name from inspec.yml
185
+ profile_name = profile.name
186
+
187
+ # read profile version from inspec.yml
188
+ profile_version = profile.version
189
+
190
+ # check that the profile is not uploaded already,
191
+ # confirm upload to the user (overwrite with --force)
192
+ if InspecPlugins::Compliance::API.exist?(config, "#{config["owner"]}/#{profile_name}##{profile_version}") && !options["overwrite"]
193
+ error.call("Profile exists on the server, use --overwrite")
194
+ end
195
+
196
+ # abort if we found an error
197
+ if error_count > 0
198
+ puts "Found #{error_count} error(s)"
199
+ exit 1
200
+ end
201
+
202
+ # if it is a directory, tar it to tmp directory
203
+ generated = false
204
+ if File.directory?(path)
205
+ generated = true
206
+ archive_path = Dir::Tmpname.create([profile_name, ".tar.gz"]) {}
207
+ puts "Generate temporary profile archive at #{archive_path}"
208
+ profile.archive({ output: archive_path, ignore_errors: false, overwrite: true })
209
+ else
210
+ archive_path = path
211
+ end
212
+
213
+ puts "Start upload to #{config["owner"]}/#{profile_name}"
214
+ pname = ERB::Util.url_encode(profile_name)
215
+
216
+ puts "Uploading to #{AUTOMATE_PRODUCT_NAME}"
217
+
218
+ success, msg = InspecPlugins::Compliance::API.upload(config, config["owner"], pname, archive_path)
219
+
220
+ # delete temp file if it was temporary generated
221
+ File.delete(archive_path) if generated && File.exist?(archive_path)
222
+
223
+ if success
224
+ puts "Successfully uploaded profile"
225
+ else
226
+ puts "Error during profile upload:"
227
+ puts msg
228
+ exit 1
229
+ end
230
+ }
218
231
  end
219
232
 
220
233
  desc "version", "displays the version of the #{AUTOMATE_PRODUCT_NAME} server"
221
234
  def version
222
- config = InspecPlugins::Compliance::Configuration.new
223
- info = InspecPlugins::Compliance::API.version(config)
224
- if !info.nil? && info["build_timestamp"]
225
- # key info["api"] is not longer available in latest version api response
226
- puts "Name: automate"
227
- puts "Version: #{info["build_timestamp"]}"
228
- else
229
- puts "Could not determine server version."
230
- exit 1
231
- end
232
- rescue InspecPlugins::Compliance::ServerConfigurationMissing
233
- puts "\nServer configuration information is missing. Please login using `#{EXEC_NAME} #{subcommand_name} login`"
234
- exit 1
235
+ Inspec.with_feature("inspec-cli-compliance-version") {
236
+ begin
237
+ config = InspecPlugins::Compliance::Configuration.new
238
+ info = InspecPlugins::Compliance::API.version(config)
239
+ if !info.nil? && info["build_timestamp"]
240
+ # key info["api"] is not longer available in latest version api response
241
+ puts "Name: automate"
242
+ puts "Version: #{info["build_timestamp"]}"
243
+ else
244
+ puts "Could not determine server version."
245
+ exit 1
246
+ end
247
+ rescue InspecPlugins::Compliance::ServerConfigurationMissing
248
+ puts "\nServer configuration information is missing. Please login using `#{EXEC_NAME} #{subcommand_name} login`"
249
+ exit 1
250
+ end
251
+ }
235
252
  end
236
253
 
237
254
  desc "logout", "user logout from #{AUTOMATE_PRODUCT_NAME}"
238
255
  def logout
239
- config = InspecPlugins::Compliance::Configuration.new
240
- unless config.supported?(:oidc) || config["token"].nil? || config["server_type"] == "automate"
256
+ Inspec.with_feature("inspec-cli-compliance-logout") {
241
257
  config = InspecPlugins::Compliance::Configuration.new
242
- url = "#{config["server"]}/logout"
243
- InspecPlugins::Compliance::HTTP.post(url, config["token"], config["insecure"], !config.supported?(:oidc))
244
- end
245
- success = config.destroy
246
-
247
- if success
248
- puts "Successfully logged out"
249
- else
250
- puts "Could not log out"
251
- end
258
+ unless config.supported?(:oidc) || config["token"].nil? || config["server_type"] == "automate"
259
+ config = InspecPlugins::Compliance::Configuration.new
260
+ url = "#{config["server"]}/logout"
261
+ InspecPlugins::Compliance::HTTP.post(url, config["token"], config["insecure"], !config.supported?(:oidc))
262
+ end
263
+ success = config.destroy
264
+
265
+ if success
266
+ puts "Successfully logged out"
267
+ else
268
+ puts "Could not log out"
269
+ end
270
+ }
252
271
  end
253
272
 
254
273
  private
@@ -1,5 +1,6 @@
1
1
  require_relative "profile"
2
2
  require "inspec/dist"
3
+ require "inspec/feature"
3
4
 
4
5
  module InspecPlugins
5
6
  module Habitat
@@ -14,17 +15,23 @@ module InspecPlugins
14
15
  option :output_dir, type: :string, required: false,
15
16
  desc: "Output directory for the Habitat artifact. Default: current directory"
16
17
  def create(path = ".")
17
- InspecPlugins::Habitat::Profile.new(path, options).create
18
+ Inspec.with_feature("inspec-cli-habitat-profile-create") {
19
+ InspecPlugins::Habitat::Profile.new(path, options).create
20
+ }
18
21
  end
19
22
 
20
23
  desc "setup PATH", "Configure the profile at PATH for Habitat, including a plan and hooks"
21
24
  def setup(path = ".")
22
- InspecPlugins::Habitat::Profile.new(path, options).setup
25
+ Inspec.with_feature("inspec-cli-habitat-profile-setup") {
26
+ InspecPlugins::Habitat::Profile.new(path, options).setup
27
+ }
23
28
  end
24
29
 
25
30
  desc "upload PATH", "Create then upload a Habitat artifact for the profile found at PATH to the Habitat Builder Depot"
26
31
  def upload(path = ".")
27
- InspecPlugins::Habitat::Profile.new(path, options).upload
32
+ Inspec.with_feature("inspec-cli-habitat-profile-upload") {
33
+ InspecPlugins::Habitat::Profile.new(path, options).upload
34
+ }
28
35
  end
29
36
  end
30
37
 
@@ -1,5 +1,6 @@
1
1
  require "pathname" unless defined?(Pathname)
2
2
  require_relative "renderer"
3
+ require "inspec/feature"
3
4
 
4
5
  module InspecPlugins
5
6
  module Init