inspec-core 5.22.65 → 6.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/Chef-EULA +9 -0
  3. data/Gemfile +24 -32
  4. data/etc/features.sig +6 -0
  5. data/etc/features.yaml +94 -0
  6. data/inspec-core.gemspec +15 -14
  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/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 +46 -3
  28. data/lib/inspec/reporters/cli.rb +1 -1
  29. data/lib/inspec/reporters.rb +67 -54
  30. data/lib/inspec/resources/nftables.rb +1 -14
  31. data/lib/inspec/resources/oracledb_session.rb +3 -9
  32. data/lib/inspec/resources/postgres_session.rb +1 -1
  33. data/lib/inspec/resources/sybase_session.rb +2 -11
  34. data/lib/inspec/resources/virtualization.rb +1 -1
  35. data/lib/inspec/rule.rb +9 -14
  36. data/lib/inspec/run_data.rb +7 -5
  37. data/lib/inspec/runner.rb +35 -6
  38. data/lib/inspec/runner_rspec.rb +12 -9
  39. data/lib/inspec/secrets/yaml.rb +9 -3
  40. data/lib/inspec/shell.rb +10 -0
  41. data/lib/inspec/ui.rb +4 -0
  42. data/lib/inspec/utils/licensing_config.rb +9 -0
  43. data/lib/inspec/utils/profile_ast_helpers.rb +2 -1
  44. data/lib/inspec/utils/waivers/csv_file_reader.rb +1 -1
  45. data/lib/inspec/utils/waivers/excel_file_reader.rb +1 -1
  46. data/lib/inspec/version.rb +1 -1
  47. data/lib/inspec/waiver_file_reader.rb +68 -27
  48. data/lib/inspec.rb +2 -1
  49. data/lib/matchers/matchers.rb +3 -3
  50. data/lib/plugins/inspec-compliance/README.md +1 -11
  51. data/lib/plugins/inspec-compliance/lib/inspec-compliance/cli.rb +189 -170
  52. data/lib/plugins/inspec-habitat/lib/inspec-habitat/cli.rb +10 -3
  53. data/lib/plugins/inspec-init/lib/inspec-init/cli.rb +1 -0
  54. data/lib/plugins/inspec-init/lib/inspec-init/cli_plugin.rb +23 -21
  55. data/lib/plugins/inspec-init/lib/inspec-init/cli_profile.rb +15 -13
  56. data/lib/plugins/inspec-init/lib/inspec-init/cli_resource.rb +15 -13
  57. data/lib/plugins/inspec-license/README.md +16 -0
  58. data/lib/plugins/inspec-license/inspec-license.gemspec +6 -0
  59. data/lib/plugins/inspec-license/lib/inspec-license/cli.rb +26 -0
  60. data/lib/plugins/inspec-license/lib/inspec-license.rb +14 -0
  61. data/lib/plugins/inspec-parallel/README.md +27 -0
  62. data/lib/plugins/inspec-parallel/inspec-parallel.gemspec +6 -0
  63. data/lib/plugins/inspec-parallel/lib/inspec-parallel/child_status_reporter.rb +61 -0
  64. data/lib/plugins/inspec-parallel/lib/inspec-parallel/cli.rb +39 -0
  65. data/lib/plugins/inspec-parallel/lib/inspec-parallel/command.rb +219 -0
  66. data/lib/plugins/inspec-parallel/lib/inspec-parallel/runner.rb +265 -0
  67. data/lib/plugins/inspec-parallel/lib/inspec-parallel/super_reporter/base.rb +24 -0
  68. data/lib/plugins/inspec-parallel/lib/inspec-parallel/super_reporter/silent.rb +7 -0
  69. data/lib/plugins/inspec-parallel/lib/inspec-parallel/super_reporter/status.rb +124 -0
  70. data/lib/plugins/inspec-parallel/lib/inspec-parallel/super_reporter/text.rb +23 -0
  71. data/lib/plugins/inspec-parallel/lib/inspec-parallel/validator.rb +170 -0
  72. data/lib/plugins/inspec-parallel/lib/inspec-parallel.rb +18 -0
  73. data/lib/plugins/inspec-sign/lib/inspec-sign/base.rb +10 -11
  74. data/lib/plugins/inspec-sign/lib/inspec-sign/cli.rb +11 -4
  75. data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb +6 -13
  76. data/lib/source_readers/inspec.rb +1 -1
  77. metadata +45 -19
@@ -15,49 +15,90 @@ module Inspec
15
15
  output = {}
16
16
 
17
17
  files.each do |file_path|
18
- file_extension = File.extname(file_path)
19
- data = nil
20
- if [".yaml", ".yml"].include? file_extension
21
- data = Secrets::YAML.resolve(file_path)
22
- unless data.nil?
23
- data = data.inputs
24
- validate_json_yaml(data)
25
- end
26
- elsif file_extension == ".csv"
27
- data = Waivers::CSVFileReader.resolve(file_path)
28
- headers = Waivers::CSVFileReader.headers
29
- validate_headers(headers)
30
- elsif file_extension == ".json"
31
- data = Waivers::JSONFileReader.resolve(file_path)
32
- validate_json_yaml(data) unless data.nil?
33
- end
18
+ data = read_from_file(file_path)
34
19
  output.merge!(data) if !data.nil? && data.is_a?(Hash)
35
20
 
36
21
  if data.nil?
37
22
  raise Inspec::Exceptions::WaiversFileNotReadable,
38
- "Cannot find parser for waivers file '#{file_path}'. " \
23
+ "Cannot find parser for waivers file." \
39
24
  "Check to make sure file has the appropriate extension."
40
25
  end
26
+ rescue Inspec::Exceptions::WaiversFileNotReadable, Inspec::Exceptions::WaiversFileInvalidFormatting => e
27
+ Inspec::Log.error "Error reading waivers file #{file_path}. #{e.message}"
28
+ Inspec::UI.new.exit(:usage_error)
41
29
  end
42
30
 
43
31
  @waivers_data[profile_id] = output
44
32
  end
45
33
 
46
- def self.validate_headers(headers, json_yaml = false)
47
- required_fields = json_yaml ? %w{justification} : %w{control_id justification}
48
- all_fields = %w{control_id justification expiration_date run}
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?
40
+ validate_json_yaml(data)
41
+ elsif file_extension == ".csv"
42
+ data = Waivers::CSVFileReader.resolve(file_path)
43
+ headers = Waivers::CSVFileReader.headers
44
+ validate_csv_headers(headers)
45
+ elsif file_extension == ".json"
46
+ data = Waivers::JSONFileReader.resolve(file_path)
47
+ validate_json_yaml(data) unless data.nil?
48
+ end
49
+ data
50
+ end
51
+
52
+ def self.all_fields
53
+ %w{control_id justification expiration_date run}
54
+ end
55
+
56
+ def self.validate_csv_headers(headers)
57
+ invalid_headers_info = fetch_invalid_headers_info(headers)
58
+ # Warn if blank column found in csv file
59
+ Inspec::Log.warn "Invalid column headers: Column can't be nil" if invalid_headers_info[:blank_column]
60
+ # Warn if extra header found in csv file
61
+ Inspec::Log.warn "Extra header/s #{invalid_headers_info[:extra_headers]}" unless invalid_headers_info[:extra_headers].empty?
62
+ unless invalid_headers_info[:missing_required_fields].empty?
63
+ raise Inspec::Exceptions::WaiversFileInvalidFormatting,
64
+ "Missing required header/s #{invalid_headers_info[:missing_required_fields]}. Fix headers in file to proceed."
65
+ end
66
+ end
49
67
 
50
- Inspec::Log.warn "Missing column headers: #{(required_fields - headers)}" unless (required_fields - headers).empty?
51
- Inspec::Log.warn "Invalid column header: Column can't be nil" if headers.include? nil
52
- Inspec::Log.warn "Extra column headers: #{(headers - all_fields)}" unless (headers - all_fields).empty?
68
+ def self.fetch_invalid_headers_info(headers, json_yaml = false)
69
+ required_fields = json_yaml ? %w{justification} : %w{control_id justification}
70
+ data = {}
71
+ data[:missing_required_fields] = []
72
+ # Finds missing required fields
73
+ unless (required_fields - headers).empty?
74
+ data[:missing_required_fields] = required_fields - headers
75
+ end
76
+ # If column with no header found set the blank_column flag. Only applicable for csv
77
+ data[:blank_column] = headers.include?(nil) ? true : false
78
+ # Find extra headers/parameters
79
+ data[:extra_headers] = (headers - all_fields)
80
+ data
53
81
  end
54
82
 
55
83
  def self.validate_json_yaml(data)
56
- headers = []
57
- data.each_value do |value|
58
- headers.push value.keys
84
+ missing_required_field = false
85
+ data.each do |key, value|
86
+ # In case of yaml or json we need to validate headers/parametes for each value
87
+ invalid_headers_info = fetch_invalid_headers_info(value.keys, true)
88
+ # WARN in case of extra parameters found in each waived control
89
+ Inspec::Log.warn "Control ID #{key}: extra parameter/s #{invalid_headers_info[:extra_headers]}" unless invalid_headers_info[:extra_headers].empty?
90
+ unless invalid_headers_info[:missing_required_fields].empty?
91
+ missing_required_field = true
92
+ # Log error for each waived control
93
+ Inspec::Log.error "Control ID #{key}: missing required parameter/s #{invalid_headers_info[:missing_required_fields]}"
94
+ end
95
+ end
96
+
97
+ # Raise error if any of the waived control has missing required filed
98
+ if missing_required_field
99
+ raise Inspec::Exceptions::WaiversFileInvalidFormatting,
100
+ "Missing required parameter [justification]. Fix parameters in file to proceed."
59
101
  end
60
- validate_headers(headers.flatten.uniq, true)
61
102
  end
62
103
  end
63
104
  end
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